jsfe 1.39.1 → 1.40.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"form-client.d.ts","sourceRoot":"","sources":["../../src/forms/form-client.js"],"names":[],"mappings":"AA2BA,iGAAiG;AACjG;;;;;SAiEC;AAED,+GAA+G;AAC/G;;;;SAgCC;AAED;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BC;AA5JD,qCAAsC,eAAe,CAAC"}
1
+ {"version":3,"file":"form-client.d.ts","sourceRoot":"","sources":["../../src/forms/form-client.js"],"names":[],"mappings":"AA2BA,iGAAiG;AACjG;;;;;SA0EC;AAED,+GAA+G;AAC/G;;;;SAgCC;AAED;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BC;AArKD,qCAAsC,eAAe,CAAC"}
@@ -40,12 +40,13 @@ export function projectForm(session, flows) {
40
40
  const { state: root, context } = pending, cargo = session.cargo, language = session.language || 'en';
41
41
  // Both the code field and its free-text fallback can contain the one-time code.
42
42
  const collectingOtp = session.flowStacks.flat().some(frame => frame.flowId === 'get-and-validate-otp-code');
43
+ let askedShown = false;
43
44
  const fields = (specs, state, prefix = '', depth = 0) => {
44
45
  if (depth > 16)
45
46
  return [];
46
47
  const out = [];
47
48
  for (const input of specs || []) {
48
- if (!safe(input.name) || input.form_client === false || input.internal || input.bind || input.confirmation || !formInputApplies(input, state.values, cargo, state))
49
+ if (!safe(input.name) || input.form_client === false || input.bind || input.confirmation || !formInputApplies(input, state.values, cargo, state))
49
50
  continue;
50
51
  const path = prefix + input.name;
51
52
  const required = input.required_when === undefined || formInputApplies({ when: input.required_when }, state.values, cargo, state);
@@ -57,8 +58,10 @@ export function projectForm(session, flows) {
57
58
  // An alternative is a group constraint, not several individually required fields.
58
59
  const members = fields(input.any_of, state, prefix, depth + 1).map(field => ({ ...field, required: false }));
59
60
  const groupLabel = language === 'es' ? input.label_es || input.label : input.label;
61
+ if (members.length && isCurrent)
62
+ askedShown = true;
60
63
  if (members.length)
61
- out.push({ name: input.name, path, kind: 'alternatives', label: groupLabel || '', required, exclusive: input.exclusive === true, fields: members });
64
+ out.push({ name: input.name, path, kind: 'alternatives', label: groupLabel || '', required, exclusive: input.exclusive === true, ...(isCurrent ? { current: true } : {}), fields: members });
62
65
  }
63
66
  else if (input.form && input.schema) {
64
67
  if (input.prepare && !state.prepared?.[input.name])
@@ -67,8 +70,10 @@ export function projectForm(session, flows) {
67
70
  const snapshot = state.childResults?.[input.name];
68
71
  const virtual = child || { values: state.values?.[input.name] || {}, data: snapshot?.data || {}, prepared: snapshot?.prepared || {}, errors: {} };
69
72
  const nested = fields(child?.specs || input.schema.inputs, virtual, path + '.', depth + 1);
73
+ if (nested.length && isCurrent)
74
+ askedShown = true;
70
75
  if (nested.length)
71
- out.push({ name: input.name, path, kind: 'group', label: label(input, language), required, fields: nested });
76
+ out.push({ name: input.name, path, kind: 'group', label: label(input, language), required, ...(isCurrent ? { current: true } : {}), fields: nested });
72
77
  }
73
78
  else if (input.profile) {
74
79
  const profile = input.profile;
@@ -103,12 +108,22 @@ export function projectForm(session, flows) {
103
108
  field.value = state.values[input.name];
104
109
  if (state.errors?.[input.name])
105
110
  field.error = String(state.errors[input.name]);
111
+ // The field the conversation is asking for now, so a client can point at it on a redrawn form.
112
+ if (isCurrent) {
113
+ field.current = true;
114
+ askedShown = true;
115
+ }
106
116
  out.push(field);
107
117
  }
108
118
  }
109
119
  return out;
110
120
  };
111
121
  const projected = fields(root.specs, root);
122
+ // A card that cannot take the answer being asked for (a chat-only or not yet prepared field) is not drawn: the
123
+ // question is answered in chat, never beside a form that has no place for it.
124
+ const asked = context.names?.length || context.current;
125
+ if (asked && !askedShown)
126
+ return null;
112
127
  return projected.length ? { version: 1, form: root.id, language, fields: projected } : null;
113
128
  }
114
129
  /** Host preflight: call before updateActivity. Unknown paths and malformed envelopes never enter execution. */
@@ -1 +1 @@
1
- export const JSFE_RUNTIME_FINGERPRINT = "sha256:a953832e13337486b1aa6d9ea0eef7279c549109af33081d13f92e5265e48e20";
1
+ export const JSFE_RUNTIME_FINGERPRINT = "sha256:16e28f7248d2a884103079326baaf8d4f9aaf65486e99a8b3611bd248d646439";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsfe",
3
- "version": "1.39.1",
3
+ "version": "1.40.1",
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",