Skip to content

Commit

Permalink
+++
Browse files Browse the repository at this point in the history
  • Loading branch information
Offirmo committed Oct 1, 2024
1 parent b14c76c commit 99f6b46
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 65 deletions.
23 changes: 6 additions & 17 deletions stack--current/3-advanced/normalize-string/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,13 @@ TODO publish on npm?


```ts
import { NORMALIZERS } from '@offirmo-private/normalize-string'
import {
combinenormalizers,
ensure_string,
normalize_unicode,
trim,
} from '@offirmo-private/normalize-string'

NORMALIZERS.coerce_to_redeemable_code(s)
NORMALIZERS.ensure_string(s)
NORMALIZERS.capitalize(s)
NORMALIZERS.to_lower_case(s)
NORMALIZERS.to_upper_case(s)
NORMALIZERS.trim(s)
NORMALIZERS.coerce_to_ascii(s)
NORMALIZERS.normalize_unicode(s)
NORMALIZERS.coerce_blanks_to_single_spaces(s)
NORMALIZERS.coerce_delimiters_to_space(s)
NORMALIZERS.convert_spaces_to_camel_case(s)
NORMALIZERS.coerce_to_safe_nickname(s)
NORMALIZERS.coerce_to_redeemable_code(s)
NORMALIZERS.normalizeemailsafe(s)
NORMALIZERS.normalizeemailreasonable(s)
NORMALIZERS.normalizeemailfull(s)
```
https://thread.engineering/2018-08-29-searching-and-sorting-text-with-diacritical-marks-in-javascript/

Expand Down
1 change: 1 addition & 0 deletions stack--current/5-incubator/active/view--chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"tslib": "^2"
},
"dependencies": {
"@offirmo-private/normalize-string": "*",
"@offirmo-private/ts-types": "*",
"is-promise": "^4",
"p-progress": "^1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,79 @@
/** trivial console-based chat primitives for testing
* no need to be fancy.
*/
import { type ChatPrimitives } from '../implementation/types.js'

const DEBUG = false

const CHAT_CONSOLE: ChatPrimitives<string> = {
setup: async () => {
console.log('[ChatPrimitives.setup()]')
DEBUG && console.log('[ChatPrimitives.setup()]')
},

display_message: async ({msg, choices}) => {
console.log('[ChatPrimitives.display_message()]')
DEBUG && console.log('[ChatPrimitives.display_message()]')

console.log(msg)
if (choices) console.log('Choices:', choices)
if (choices) {
console.log('Choices:', choices)
}
},

pretend_to_think: async ({duration_ms}) => { throw new Error('NO UI pretend_to_think') },
pretend_to_think: async ({duration_ms}) => {
DEBUG && console.log('[ChatPrimitives.pretend_to_think(${duration_ms})]')

pretend_to_work: async({
console.log('…')
await new Promise(resolve => setTimeout(resolve, duration_ms))
console.log('↳ ✔')
},

pretend_to_work: async ({
msg_before,
duration_ms,
msg_after,
}) => {
DEBUG && console.log(`[ChatPrimitives.pretend_to_work(${duration_ms})]`)

console.log(msg_before)
await new Promise(resolve => setTimeout(resolve, duration_ms))
console.log(msg_after)
console.log('↳ ' + msg_after)
},

//read_answer: async () => { throw new Error('NO UI read_answer') },

display_task: ({
display_task: async ({
msg_before,
promise,
msg_after,
}) => {
throw new Error(`NO UI display_task!`)
DEBUG && console.log('[ChatPrimitives.display_task(...)]')

console.log(msg_before)
let result: any = undefined
let error: Error | undefined = undefined
const success = await promise.then(
(_res) => {
result = _res
return true
},
(_err) => {
error = _err as any // TODO one day coerce to error using error utils
return false
})
console.log('↳ ' + msg_after(success, result || error))
},

spin_until_resolution: async ({promise}) => promise,
spin_until_resolution: async ({promise}) => {
DEBUG && console.log('[ChatPrimitives.spin_until_resolution(...)]')

console.log('[ChatPrimitives.spin_until_resolution()] begin…')
await promise
console.log('↳ end.')
return promise
},

teardown: async () => {
console.log('[ChatPrimitives.teardown()]')
DEBUG && console.log('[ChatPrimitives.teardown()]')
},
}

Expand Down
31 changes: 18 additions & 13 deletions stack--current/5-incubator/active/view--chat/src/__fixtures/tour.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { type Step, StepType } from '../types/types.js'
import { type StepsGenerator } from '../loop/types.js'
import Deferred from '@offirmo/deferred'

import { type Step, StepType } from '../steps/types.js'
import { getꓽInputStepⵧnonEmptyString } from '../steps/bases.js'

import { type StepsGenerator } from '../loop/types.js'

export default function* get_next_step(skip_to_index: number = 0) {
console.log('get_next_step()', { skip_to_index })

const state = {
mode: 'main',
name: undefined,
city: undefined,
name: undefined as string | undefined,
city: undefined as string | undefined,
}

const warmup_promise = new Deferred<void>()
Expand All @@ -20,7 +23,7 @@ export default function* get_next_step(skip_to_index: number = 0) {
type: StepType.perceived_labor,

msg_before: 'Waking up...',
duration_ms: 2000,
duration_ms: 1000,
msg_after: 'Awoken!',
},

Expand All @@ -39,15 +42,17 @@ export default function* get_next_step(skip_to_index: number = 0) {
msg: 'Welcome. I’ll have a few questions…',
},

getꓽInputStepⵧnonEmptyString<string>({
prompt: 'What’s your name?',
msg_as_user: (value: string) => `My name is "${value}".`,
msg_acknowledge: (value: string) => `Thanks, ${value}!`,

callback: (value: string) => {
console.log(`[callback called: ${value}]`)
state.name = value
},
}),
/*
{
type: 'ask_for_string',
msg_main: 'What’s your name?',
//validator: null, // TODO
msgg_as_user: value => `My name is "${value}".`,
msgg_acknowledge: name => `Thanks for the answer, ${name}!`,
callback: value => { state.name = value },
},
{
type: 'ask_for_string',
msg_main: 'What city do you live in?',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Enum } from 'typescript-string-enums'
import { PProgress as PromiseWithProgress } from 'p-progress'
import { type Immutable } from '@offirmo-private/ts-types'

import { type TaskProgressStep } from '../types/types.js'
import { type TaskProgressStep } from '../steps/types.js'

/////////////////////////////////////////////////

Expand Down Expand Up @@ -30,9 +30,9 @@ interface ChatPrimitives<ContentType> {
//read_answer(step) TODO clarify

display_task(p: {
msg_before: TaskProgressStep<ContentType>['msg_before'],
msg_before: ContentType | string,
promise: TaskProgressStep<ContentType>['promise'],
msg_after: TaskProgressStep<ContentType>['msg_after'],
msg_after: NonNullable<TaskProgressStep<ContentType>['msg_after']>,
}): Promise<void>

// while we wait for the next step.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import primitives from '../__fixtures/primitives--console.js'
import { create } from './index.js'

const chat = create({
DEBUG: true,
DEBUG: false,
gen_next_step: generator_func() as any,
primitives,
})
Expand Down
36 changes: 19 additions & 17 deletions stack--current/5-incubator/active/view--chat/src/loop/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import is_promise from 'is-promise'
import { type Immutable } from '@offirmo-private/ts-types'
import {} from '@offirmo-private/normalize-string'

import { type ChatPrimitives } from '../implementation/types.js'
import { type Step, StepType } from '../types/index.js'
import { type Step, StepType } from '../steps/index.js'
import { create_dummy_progress_promise } from '../utils/index.js'
import { StepsGenerator } from './types.js'

Expand Down Expand Up @@ -89,7 +90,7 @@ function create<ContentType>({
}

async function execute_step(step: Step<ContentType>) {
if (DEBUG) console.log('↘ ${LIB}.execute_step(\n', DEBUG_to_prettified_str(step), '\n)')
if (DEBUG) console.log('↘ ${LIB}.execute_step(', DEBUG_to_prettified_str(step), ')')

//const step = normalize_step(raw_step)

Expand All @@ -110,30 +111,30 @@ function create<ContentType>({
case StepType.progress: {
let result: any = undefined
let error: Error | undefined = undefined
let success = false
step.promise.then(
(_res) => {
success = true
result = _res
},
(_err) => {
success = false
error = _err as any // TODO one day coerce to error using error utils
})

await primitives.display_task({
msg_before: step.msg_before || 'Processing…',
promise: step.promise,
msg_after: step.msg_after || ((success: boolean, result: any) => {
if (success)
return 'Done!'
return '✔ Success'
else
return `Failed! ("${result?.message}")`
return `Failed ("${result?.message}")`
}),
})
.then(
(_res) => {
result = _res
return true
},
(_err) => {
error = _err as any // TODO one day coerce to error using error utils
return false
})
.then(success => {
if (step.callback)
step.callback(success, result || error)
})

if (step.callback)
step.callback(success, result || error)
break
}

Expand Down Expand Up @@ -165,6 +166,7 @@ function create<ContentType>({
return answer
}*/
default:
console.error(`Unsupported step type: "${step.type}"!`, step)
throw new Error(`Unsupported step type: "${step.type}"!`)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { type Step } from '../types/index.js'
import { type Step } from '../steps/index.js'

/////////////////////////////////////////////////

Expand Down
65 changes: 65 additions & 0 deletions stack--current/5-incubator/active/view--chat/src/steps/bases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { StepType, type InputStep } from './types.js'
import {
combineꓽnormalizers,
ensure_string,
normalize_unicode,
trim,
} from '@offirmo-private/normalize-string'

function getꓽInputStepⵧnonEmptyString<ContentType>(
parts: Omit<InputStep<ContentType, string>, 'type' | 'normalizer' | 'validators'>
): InputStep<ContentType, string> {
const step: InputStep<ContentType, string> = {
type: StepType.input,

normalizer: (raw: any): string => {
let val = ensure_string(raw)
val = normalize_unicode(val)
val = trim(val)
return val
},
validators: [
(value: string) => [value.length > 0, 'Should have at least 1 letter.'],
],
...parts
}
return step
}

/*
function getꓽInputStepⵧconfirmation<ContentType>(): InputStep<ContentType, boolean> {
return {
type: typeof StepType.input,
normalizer: (raw: any): number => {
let val = Boolean(raw)
return val
},
validators: [
],
}
}
function getꓽInputStepⵧinteger<ContentType>(): Omit<InputStep<ContentType, number>, 'prompt' | 'msg_as_user' | 'msg_acknowledge'> {
return {
type: typeof StepType.input,
normalizer: (raw: any): number => {
raw = ensure_string(raw)
let val = Number(raw)
val = normalize_unicode(val)
val = trim(val)
return val
},
validators: [
],
}
}*/

/////////////////////////////////////////////////

export {
getꓽInputStepⵧnonEmptyString,
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './types.js'
export * from './bases.js'
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type StepType = Enum<typeof StepType> // eslint-disable-line no-redeclare

/////////////////////////////////////////////////

// TODO more async?? everything?
// TODO more async?? callbacks? everything?

interface BaseStep {
// TODO needed?
Expand Down Expand Up @@ -57,16 +57,19 @@ interface AskForConfirmationStep<ContentType> extends BaseStep {
callback?: (confirmation: boolean) => void
}

// inspired by <input> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
// TODO refine
// TODO select between choices
// TODO types
interface InputStep<ContentType, T = string> extends BaseStep {
type: typeof StepType.input

prompt: ContentType | string
normalizer?: (raw: T) => T
msg_as_user: (value: T) => ContentType | string
placeholder?: ContentType | string // may be useful in input, but primitive is free to ignore it https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder
default?: T
normalizer?: (raw: any) => T // raw is most likely string
validators: Array<(value: T) => [ boolean, ContentType | string ]>
msg_as_user: (value: T) => ContentType | string
msg_acknowledge: (value: T) => ContentType | string

callback?: (value: T) => void
Expand Down

0 comments on commit 99f6b46

Please sign in to comment.