jsfe 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/forms/contracts.d.ts +2 -0
- package/dist/forms/contracts.d.ts.map +1 -1
- package/dist/forms/flows.d.ts +2 -0
- package/dist/forms/flows.d.ts.map +1 -1
- package/dist/forms/flows.js +6 -2
- package/dist/helpers/form-conversation.d.ts.map +1 -1
- package/dist/helpers/form-conversation.js +2 -0
- package/dist/runtime-identity.js +1 -1
- package/docs/development-release.md +13 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/forms/contracts.js"],"names":[],"mappings":"AAgBA;;;IAGC;AACD;;;;;EAGC;AACD,qGAAqG;AACrG;;QAwCC;AAqBD;;;QAEC;AAgBD;;;;;;;;;QASC;AACD;;;;QAGC;AACD;;;;;;;;QA8BC;AAED,mHAAmH;AACnH,kDAgBC;AAED,+GAA+G;AAC/G
|
|
1
|
+
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/forms/contracts.js"],"names":[],"mappings":"AAgBA;;;IAGC;AACD;;;;;EAGC;AACD,qGAAqG;AACrG;;QAwCC;AAqBD;;;QAEC;AAgBD;;;;;;;;;QASC;AACD;;;;QAGC;AACD;;;;;;;;QA8BC;AAED,mHAAmH;AACnH,kDAgBC;AAED,+GAA+G;AAC/G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2EC"}
|
package/dist/forms/flows.d.ts
CHANGED
|
@@ -264,6 +264,8 @@ export namespace FIELD_PRESETS {
|
|
|
264
264
|
export { label_9 as label };
|
|
265
265
|
let label_es_9: string;
|
|
266
266
|
export { label_es_9 as label_es };
|
|
267
|
+
export let accumulate: boolean;
|
|
268
|
+
export let accumulate_first: string;
|
|
267
269
|
export namespace profile_9 {
|
|
268
270
|
let kind_9: string;
|
|
269
271
|
export { kind_9 as kind };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/forms/flows.js"],"names":[],"mappings":"AA4FA;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0DC;AACD;;;GAGG;AACH;;;;;;EAGC;
|
|
1
|
+
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/forms/flows.js"],"names":[],"mappings":"AA4FA;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0DC;AACD;;;GAGG;AACH;;;;;;EAGC;AAuYD,wHAAwH;AACxH,wEAaC;AACD,4FAA4F;AAC5F;;;EAYC;AACD,sGAAsG;AACtG;;;EAOC;AAviBM,6CAAuG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8tB9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOG"}
|
package/dist/forms/flows.js
CHANGED
|
@@ -65,7 +65,7 @@ export const FIELD_PRESETS = {
|
|
|
65
65
|
city: { label: 'city', label_es: 'ciudad', profile: { kind: 'text' }, question: 'What city?', question_es: '¿En qué ciudad?' },
|
|
66
66
|
state: { label: 'state', label_es: 'estado', profile: { kind: 'pattern', regex: '^\\s*(?:Alabama|Alaska|Arizona|Arkansas|California|Colorado|Connecticut|Delaware|Florida|Georgia|Hawaii|Idaho|Illinois|Indiana|Iowa|Kansas|Kentucky|Louisiana|Maine|Maryland|Massachusetts|Michigan|Minnesota|Mississippi|Missouri|Montana|Nebraska|Nevada|New Hampshire|New Jersey|New Mexico|New York|North Carolina|North Dakota|Ohio|Oklahoma|Oregon|Pennsylvania|Rhode Island|South Carolina|South Dakota|Tennessee|Texas|Utah|Vermont|Virginia|Washington|West Virginia|Wisconsin|Wyoming|District of Columbia|Puerto Rico|AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR)\\s*$' },
|
|
67
67
|
question: 'What state?', question_es: '¿En qué estado?' },
|
|
68
|
-
card_number: { label: 'card number', label_es: 'número de tarjeta', profile: { kind: 'digits', min: 13, max: 19 }, digits: { min: 13, max: 19, complete: [{ first: '4', lengths: [16, 19] }, { first: '25', lengths: [16] }, { first: '3', lengths: [15] }, { first: '6', lengths: [16, 19] }] },
|
|
68
|
+
card_number: { label: 'card number', label_es: 'número de tarjeta', accumulate: true, accumulate_first: '^[2-6]', profile: { kind: 'digits', min: 13, max: 19 }, digits: { min: 13, max: 19, complete: [{ first: '4', lengths: [16, 19] }, { first: '25', lengths: [16] }, { first: '3', lengths: [15] }, { first: '6', lengths: [16, 19] }] },
|
|
69
69
|
pii: true, validator: 'validate-card-number',
|
|
70
70
|
question: 'Please say or enter your card number.', question_es: 'Por favor diga o ingrese el número de su tarjeta.',
|
|
71
71
|
retry: 'Please say or enter the full card number.', retry_es: 'Por favor diga o ingrese el número completo de su tarjeta.' },
|
|
@@ -211,7 +211,7 @@ const collectInput = {
|
|
|
211
211
|
id: 'collect-input', name: 'CollectInput', version: '1.0.0', scope: 'isolated',
|
|
212
212
|
description: "Worker: ask the form's current input once and file the answer wherever it belongs in the form chain (matchInputs, decided by scope; a declared confirm_against pair is asked). One reading → collect-input-accept; ambiguous → disambiguate-input; a question about the form's data → provide-data; a short miss → counted, the loop re-asks; 3+ words that fit nothing → DISPATCH. Params: form, current, questions, provides, exit_context(_es).",
|
|
213
213
|
parameters: ['form', 'current', 'questions', 'provides', 'exit_context', 'exit_context_es'].map((n) => ({ name: n, type: ['questions'].includes(n) ? 'array' : n === 'provides' ? 'object' : 'string', description: n })),
|
|
214
|
-
variables: vars({ ci_exhaustion: null, ci_d: {}, ci_pre: '', ci_val: '', ci_routed: '', ci_names: [], ci_chain: [], ci_spec: null, ci_choices: [], ci_q: '', ci_q_es: '', ci_missed: 0, ci_max: 3, ci_misses: 0, ci_miss_limit: 2, ci_m: null, ci_qkey: '', ci_skip_gate: '', ci_words: 0, ci_digits: null, ci_amount: false, ci_as_spoken: false, ci_reader: {}, ci_pii: false, ci_ctx: '', ci_ctx_es: '' }),
|
|
214
|
+
variables: vars({ ci_partial: '', ci_exhaustion: null, ci_d: {}, ci_pre: '', ci_val: '', ci_routed: '', ci_names: [], ci_chain: [], ci_spec: null, ci_choices: [], ci_q: '', ci_q_es: '', ci_missed: 0, ci_max: 3, ci_misses: 0, ci_miss_limit: 2, ci_m: null, ci_qkey: '', ci_skip_gate: '', ci_words: 0, ci_digits: null, ci_amount: false, ci_as_spoken: false, ci_reader: {}, ci_pii: false, ci_ctx: '', ci_ctx_es: '' }),
|
|
215
215
|
steps: [
|
|
216
216
|
SET('ci_find_spec', 'ci_spec', "(function () { var s = ((cargo.forms[form] || {}).specs || []).filter(function (s) { return s.name === current; })[0]; return s ? (Array.isArray(s.any_of) ? Object.assign({}, s, { profile: { kind: 'group' } }) : s) : null; })()"),
|
|
217
217
|
SET('ci_current_names', 'ci_names', "Array.isArray(ci_spec && ci_spec.any_of) ? ci_spec.any_of.map(function (m) { return m.name; }) : [current]"),
|
|
@@ -251,6 +251,10 @@ const collectInput = {
|
|
|
251
251
|
SET('ci_route_to', 'ci_routed', "(ci_spec.route_keys || []).filter(function (r) { return String(cargo.choice || '').indexOf(r.prefix) === 0; }).map(function (r) { return r.flow; })[0] || ''"),
|
|
252
252
|
WHEN('ci_route_key', 'ci_routed', FLOW('ci_route_key_flow', '{{ci_routed}}', 'call', { form: '{{form}}', input: '{{current}}' })),
|
|
253
253
|
WHEN('ci_routed_end', 'ci_routed', END('ci_routed_done')),
|
|
254
|
+
// `accumulate` (opt-in, digit inputs): a number said in groups across turns ("4347", "6971 2210"…) is kept and joined;
|
|
255
|
+
// short of the input's minimum the next question is "Go on." Anything that is not digits drops the partial.
|
|
256
|
+
SET('ci_partial_join', 'ci_partial', "(function () { var state = cargo.forms[form]; if (!ci_spec || ci_spec.accumulate !== true || !ci_spec.digits) return ''; var said = String(cargo.choiceText || '').trim(); var data = state.data = state.data || {}; var key = '__partial_' + current; if (!/^[0-9][0-9\\s.,-]*$/.test(said)) { delete data[key]; return ''; } var joined = String(data[key] || '') + said.replace(/[^0-9]/g, ''); var starts = ci_spec.accumulate_first ? new RegExp(ci_spec.accumulate_first) : null; if (!data[key] && starts && !starts.test(joined)) return ''; if (joined.length < Number(ci_spec.digits.min || 0)) { data[key] = joined; var fb = state.feedback || {}; fb[current] = 'partial'; state.feedback = fb; return 'hold'; } delete data[key]; cargo.choiceText = joined; cargo.choice = joined; return 'joined'; })()"),
|
|
257
|
+
WHEN('ci_partial_hold', "ci_partial === 'hold'", END('ci_partial_wait')),
|
|
254
258
|
SET('ci_build_chain', 'ci_chain', CHAIN_JS),
|
|
255
259
|
SET('ci_match', 'ci_m', "readFormAnswer(cargo.choiceText, ci_chain, Array.isArray(ci_spec.any_of) ? { form: form, names: ci_names } : { form: form, name: current }, cargo.choice, provides && Array.isArray(questions) ? questions : [])"),
|
|
256
260
|
// The labels and validators ride on each reading, so the presets stay in this one flow.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-conversation.d.ts","sourceRoot":"","sources":["../../src/helpers/form-conversation.js"],"names":[],"mappings":"AAGA,0EAGC;AACD,oEAGC;AACD,sGAAsG;AACtG,+
|
|
1
|
+
{"version":3,"file":"form-conversation.d.ts","sourceRoot":"","sources":["../../src/helpers/form-conversation.js"],"names":[],"mappings":"AAGA,0EAGC;AACD,oEAGC;AACD,sGAAsG;AACtG,+GAOC;AACD,8EAAwI;AAExI,yGAAyG;AACzG;;;;;;;;;;;;;;;;;;;;EAUC;AAED,sGAAsG;AACtG;;;;;;;;;;;;;;;;;;;;EAeC"}
|
|
@@ -25,6 +25,8 @@ export function formInputPrompt(cargo, id, input, spec, data = {}, language = 'e
|
|
|
25
25
|
const es = language === 'es', feedback = cargo.forms?.[id]?.feedback?.[input];
|
|
26
26
|
const question = spec.question_from && (es ? (data[spec.question_from + '_es'] || data[spec.question_from]) : data[spec.question_from]) || (es ? (spec.question_es || spec.question) : spec.question) || '';
|
|
27
27
|
const retry = es ? spec.retry_es : spec.retry;
|
|
28
|
+
if (feedback === 'partial')
|
|
29
|
+
return es ? 'Continúe.' : 'Go on.';
|
|
28
30
|
if (feedback && retry)
|
|
29
31
|
return retry;
|
|
30
32
|
return (feedback === 'miss' ? (es ? 'Disculpe, no le entendí. ' : "Sorry, I didn't catch that. ") : '') + question;
|
package/dist/runtime-identity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const JSFE_RUNTIME_FINGERPRINT = "sha256:
|
|
1
|
+
export const JSFE_RUNTIME_FINGERPRINT = "sha256:ea563a7ddbdb680ea2a5acaa1dc4472dbb417ce95e20d7463db1198946c98475";
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 1.9.4: card groups start like a card
|
|
2
|
+
|
|
3
|
+
`accumulate_first` (a regular expression) limits accumulation to a first group that matches it; the `card_number` preset
|
|
4
|
+
uses `^[2-6]` (every card network's first digit), so "1234" or "0729" said at the card question keeps its normal reading
|
|
5
|
+
as an expiration date or a security code.
|
|
6
|
+
|
|
7
|
+
## 1.9.3: a card number said in groups
|
|
8
|
+
|
|
9
|
+
A digit input may opt in to `accumulate: true`: an answer that is only digits and short of the input's minimum is kept, and
|
|
10
|
+
the next question is "Go on." / "Continúe."; the next digits are joined to it, and anything that is not digits drops the
|
|
11
|
+
partial. The shared `card_number` preset opts in, so a card read in groups across turns is collected whole instead of each
|
|
12
|
+
group being read as another field (an account number, an amount).
|
|
13
|
+
|
|
1
14
|
## 1.9.2: address city list and part corrections
|
|
2
15
|
|
|
3
16
|
The shared address form (`collect-geocoded-address`) asks the city when the ZIP code carries several place names (`geocode-zip` returns `localities`, Google's `postcode_localities`; at most 7): "Which city is it: Commerce, East Los Angeles, Los Angeles or Vernon?", by name or key. With one name it is not asked, and a caller who says the city is wrong is asked it plainly and any city is taken. At the read-back, naming one part without a value ("the city is wrong", "la ciudad no") asks only that part again, and a plain NO asks which part is wrong (zip code, city, street, apartment, or ALL to start over) instead of starting over. The part naming is an opt-in form confirmation setting, `confirm.named_fields: true`, read from each input's `names`; other forms keep their behavior. A host whose `geocode-zip` returns no `localities` keeps the one city it returns.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsfe",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
4
4
|
"description": "TypeScript workflow engine for conversational/automation apps: multi-flow orchestration, interrupt/resume, stack-based control, robust errors, and flexible tool/REST integration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ron Pinkas",
|