forge-openclaw-plugin 0.2.24 → 0.2.26

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.
Files changed (208) hide show
  1. package/README.md +13 -0
  2. package/dist/assets/{board-_C6oMy5w.js → board-ta0rUHOf.js} +3 -3
  3. package/dist/assets/{board-_C6oMy5w.js.map → board-ta0rUHOf.js.map} +1 -1
  4. package/dist/assets/index-Ro0ZF_az.css +1 -0
  5. package/dist/assets/index-ytlpSj23.js +79 -0
  6. package/dist/assets/index-ytlpSj23.js.map +1 -0
  7. package/dist/assets/{motion-D4sZgCHd.js → motion-fBKPB6yw.js} +3 -3
  8. package/dist/assets/motion-fBKPB6yw.js.map +1 -0
  9. package/dist/assets/{table-BWzTaky1.js → table-C-IGTQni.js} +2 -2
  10. package/dist/assets/{table-BWzTaky1.js.map → table-C-IGTQni.js.map} +1 -1
  11. package/dist/assets/{ui-BzK4azQb.js → ui-DInOpaYF.js} +2 -2
  12. package/dist/assets/{ui-BzK4azQb.js.map → ui-DInOpaYF.js.map} +1 -1
  13. package/dist/assets/vendor-lE3tZJcC.js +876 -0
  14. package/dist/assets/vendor-lE3tZJcC.js.map +1 -0
  15. package/dist/index.html +7 -8
  16. package/dist/openclaw/local-runtime.d.ts +3 -1
  17. package/dist/openclaw/local-runtime.js +51 -15
  18. package/dist/openclaw/parity.d.ts +1 -1
  19. package/dist/openclaw/parity.js +29 -0
  20. package/dist/openclaw/plugin-entry-shared.d.ts +1 -0
  21. package/dist/openclaw/plugin-entry-shared.js +31 -6
  22. package/dist/openclaw/plugin-sdk-types.d.ts +29 -0
  23. package/dist/openclaw/routes.js +236 -0
  24. package/dist/openclaw/session-bootstrap.d.ts +78 -0
  25. package/dist/openclaw/session-bootstrap.js +240 -0
  26. package/dist/openclaw/tools.js +279 -6
  27. package/dist/server/server/migrations/001_core.sql +411 -0
  28. package/dist/server/server/migrations/002_psyche.sql +392 -0
  29. package/dist/server/server/migrations/003_habits.sql +30 -0
  30. package/dist/server/server/migrations/004_habit_links.sql +8 -0
  31. package/dist/server/server/migrations/005_habit_psyche_links.sql +24 -0
  32. package/dist/server/server/migrations/006_work_adjustments.sql +14 -0
  33. package/dist/server/server/migrations/007_weekly_review_closures.sql +17 -0
  34. package/dist/server/server/migrations/008_calendar_execution.sql +147 -0
  35. package/dist/server/server/migrations/009_true_calendar_events.sql +195 -0
  36. package/dist/server/server/migrations/010_calendar_selection_state.sql +6 -0
  37. package/dist/server/server/migrations/011_calendar_timezone_backfill.sql +11 -0
  38. package/dist/server/server/migrations/012_work_block_ranges.sql +7 -0
  39. package/dist/server/server/migrations/013_microsoft_local_auth_settings.sql +8 -0
  40. package/dist/server/server/migrations/014_note_tags_and_ephemeral.sql +8 -0
  41. package/dist/server/server/migrations/015_multi_user_and_strategies.sql +244 -0
  42. package/dist/server/server/migrations/016_health_companion.sql +158 -0
  43. package/dist/server/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
  44. package/dist/server/server/migrations/017_preferences.sql +131 -0
  45. package/dist/server/server/migrations/018_preference_catalogs.sql +31 -0
  46. package/dist/server/server/migrations/019_wiki_memory.sql +255 -0
  47. package/dist/server/server/migrations/020_wiki_page_hierarchy.sql +11 -0
  48. package/dist/server/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
  49. package/dist/server/server/migrations/022_wiki_ingest_background.sql +85 -0
  50. package/dist/server/server/migrations/023_diagnostic_logs.sql +28 -0
  51. package/dist/server/server/migrations/024_questionnaires.sql +96 -0
  52. package/dist/server/server/migrations/025_ai_model_connections.sql +26 -0
  53. package/dist/server/server/migrations/026_custom_theme_settings.sql +2 -0
  54. package/dist/server/server/migrations/027_ai_processors.sql +31 -0
  55. package/dist/server/server/migrations/028_movement_domain.sql +136 -0
  56. package/dist/server/server/migrations/029_watch_micro_capture.sql +23 -0
  57. package/dist/server/server/migrations/030_surface_layouts.sql +5 -0
  58. package/dist/server/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
  59. package/dist/server/server/migrations/032_ai_connectors.sql +44 -0
  60. package/dist/server/server/migrations/033_movement_trip_point_sync.sql +36 -0
  61. package/dist/server/server/migrations/034_movement_segment_sync.sql +49 -0
  62. package/dist/server/server/migrations/035_google_local_auth_settings.sql +2 -0
  63. package/dist/server/server/migrations/036_google_local_auth_client_secret.sql +2 -0
  64. package/dist/server/{app.js → server/src/app.js} +992 -25
  65. package/dist/server/server/src/connectors/box-registry.js +188 -0
  66. package/dist/server/{db.js → server/src/db.js} +6 -0
  67. package/dist/server/server/src/debug.js +19 -0
  68. package/dist/server/server/src/discovery-advertiser.js +114 -0
  69. package/dist/server/{health.js → server/src/health.js} +39 -11
  70. package/dist/server/{index.js → server/src/index.js} +4 -0
  71. package/dist/server/{managers → server/src/managers}/platform/llm-manager.js +40 -4
  72. package/dist/server/{managers → server/src/managers}/platform/openai-responses-provider.js +129 -19
  73. package/dist/server/server/src/movement.js +2935 -0
  74. package/dist/server/{openapi.js → server/src/openapi.js} +628 -5
  75. package/dist/server/{psyche-types.js → server/src/psyche-types.js} +15 -1
  76. package/dist/server/server/src/questionnaire-flow.js +552 -0
  77. package/dist/server/server/src/questionnaire-seeds.js +853 -0
  78. package/dist/server/server/src/questionnaire-types.js +340 -0
  79. package/dist/server/server/src/repositories/ai-connectors.js +1207 -0
  80. package/dist/server/server/src/repositories/ai-processors.js +547 -0
  81. package/dist/server/{repositories → server/src/repositories}/calendar.js +1 -1
  82. package/dist/server/{repositories → server/src/repositories}/entity-ownership.js +9 -1
  83. package/dist/server/{repositories → server/src/repositories}/habits.js +69 -5
  84. package/dist/server/server/src/repositories/model-settings.js +216 -0
  85. package/dist/server/{repositories → server/src/repositories}/notes.js +57 -15
  86. package/dist/server/{repositories → server/src/repositories}/preferences.js +124 -0
  87. package/dist/server/server/src/repositories/questionnaires.js +1338 -0
  88. package/dist/server/{repositories → server/src/repositories}/settings.js +156 -12
  89. package/dist/server/server/src/repositories/surface-layouts.js +76 -0
  90. package/dist/server/{repositories → server/src/repositories}/wiki-memory.js +5 -1
  91. package/dist/server/{services → server/src/services}/calendar-runtime.js +775 -58
  92. package/dist/server/{services → server/src/services}/entity-crud.js +81 -2
  93. package/dist/server/server/src/services/google-calendar-oauth-config.js +176 -0
  94. package/dist/server/server/src/services/openai-codex-oauth.js +153 -0
  95. package/dist/server/server/src/services/psyche-observation-calendar.js +46 -0
  96. package/dist/server/{types.js → server/src/types.js} +621 -14
  97. package/dist/server/server/src/watch-mobile.js +562 -0
  98. package/dist/server/{web.js → server/src/web.js} +30 -4
  99. package/dist/server/src/components/customization/utility-widgets.js +330 -0
  100. package/dist/server/src/components/workbench-boxes/health/health-boxes.js +92 -0
  101. package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +128 -0
  102. package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +37 -0
  103. package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +114 -0
  104. package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +57 -0
  105. package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +4 -0
  106. package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +13 -0
  107. package/dist/server/src/components/workbench-boxes/today/today-boxes.js +63 -0
  108. package/dist/server/src/lib/api-error.js +37 -0
  109. package/dist/server/src/lib/api.js +1859 -0
  110. package/dist/server/src/lib/calendar-name-deduper.js +144 -0
  111. package/dist/server/src/lib/diagnostics.js +67 -0
  112. package/dist/server/src/lib/psyche-types.js +1 -0
  113. package/dist/server/src/lib/questionnaire-types.js +1 -0
  114. package/dist/server/src/lib/runtime-paths.js +24 -0
  115. package/dist/server/src/lib/schemas.js +234 -0
  116. package/dist/server/src/lib/snapshot-normalizer.js +374 -0
  117. package/dist/server/src/lib/theme-system.js +319 -0
  118. package/dist/server/src/lib/types.js +1 -0
  119. package/dist/server/src/lib/utils.js +22 -0
  120. package/dist/server/src/lib/workbench/boxes.js +16 -0
  121. package/dist/server/src/lib/workbench/nodes.js +15 -0
  122. package/dist/server/src/lib/workbench/registry.js +73 -0
  123. package/dist/server/src/lib/workbench/runtime.js +181 -0
  124. package/openclaw.plugin.json +1 -1
  125. package/package.json +6 -1
  126. package/server/index.js +68 -0
  127. package/server/migrations/024_questionnaires.sql +96 -0
  128. package/server/migrations/025_ai_model_connections.sql +26 -0
  129. package/server/migrations/026_custom_theme_settings.sql +2 -0
  130. package/server/migrations/027_ai_processors.sql +31 -0
  131. package/server/migrations/028_movement_domain.sql +136 -0
  132. package/server/migrations/029_watch_micro_capture.sql +23 -0
  133. package/server/migrations/030_surface_layouts.sql +5 -0
  134. package/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
  135. package/server/migrations/032_ai_connectors.sql +44 -0
  136. package/server/migrations/033_movement_trip_point_sync.sql +36 -0
  137. package/server/migrations/034_movement_segment_sync.sql +49 -0
  138. package/server/migrations/035_google_local_auth_settings.sql +2 -0
  139. package/server/migrations/036_google_local_auth_client_secret.sql +2 -0
  140. package/skills/forge-openclaw/SKILL.md +15 -1
  141. package/skills/forge-openclaw/entity_conversation_playbooks.md +523 -87
  142. package/skills/forge-openclaw/psyche_entity_playbooks.md +331 -221
  143. package/dist/assets/index-DTCwBWAs.js +0 -65
  144. package/dist/assets/index-DTCwBWAs.js.map +0 -1
  145. package/dist/assets/index-DttXlAgi.css +0 -1
  146. package/dist/assets/motion-D4sZgCHd.js.map +0 -1
  147. package/dist/assets/vendor-De38P6YR.js +0 -729
  148. package/dist/assets/vendor-De38P6YR.js.map +0 -1
  149. package/dist/assets/viz-C6hfyqzu.js +0 -34
  150. package/dist/assets/viz-C6hfyqzu.js.map +0 -1
  151. package/skills/forge-openclaw/cron_jobs.md +0 -395
  152. /package/dist/server/{demo-data.js → server/src/demo-data.js} +0 -0
  153. /package/dist/server/{e2e-server.js → server/src/e2e-server.js} +0 -0
  154. /package/dist/server/{errors.js → server/src/errors.js} +0 -0
  155. /package/dist/server/{managers → server/src/managers}/base.js +0 -0
  156. /package/dist/server/{managers → server/src/managers}/contracts.js +0 -0
  157. /package/dist/server/{managers → server/src/managers}/platform/api-gateway-manager.js +0 -0
  158. /package/dist/server/{managers → server/src/managers}/platform/audit-manager.js +0 -0
  159. /package/dist/server/{managers → server/src/managers}/platform/authentication-manager.js +0 -0
  160. /package/dist/server/{managers → server/src/managers}/platform/authorization-manager.js +0 -0
  161. /package/dist/server/{managers → server/src/managers}/platform/background-job-manager.js +0 -0
  162. /package/dist/server/{managers → server/src/managers}/platform/configuration-manager.js +0 -0
  163. /package/dist/server/{managers → server/src/managers}/platform/database-manager.js +0 -0
  164. /package/dist/server/{managers → server/src/managers}/platform/event-bus-manager.js +0 -0
  165. /package/dist/server/{managers → server/src/managers}/platform/external-service-manager.js +0 -0
  166. /package/dist/server/{managers → server/src/managers}/platform/health-manager.js +0 -0
  167. /package/dist/server/{managers → server/src/managers}/platform/migration-manager.js +0 -0
  168. /package/dist/server/{managers → server/src/managers}/platform/search-index-manager.js +0 -0
  169. /package/dist/server/{managers → server/src/managers}/platform/secrets-manager.js +0 -0
  170. /package/dist/server/{managers → server/src/managers}/platform/session-manager.js +0 -0
  171. /package/dist/server/{managers → server/src/managers}/platform/storage-manager.js +0 -0
  172. /package/dist/server/{managers → server/src/managers}/platform/token-manager.js +0 -0
  173. /package/dist/server/{managers → server/src/managers}/platform/transaction-manager.js +0 -0
  174. /package/dist/server/{managers → server/src/managers}/platform/trusted-network.js +0 -0
  175. /package/dist/server/{managers → server/src/managers}/runtime.js +0 -0
  176. /package/dist/server/{managers → server/src/managers}/type-guards.js +0 -0
  177. /package/dist/server/{preferences-seeds.js → server/src/preferences-seeds.js} +0 -0
  178. /package/dist/server/{preferences-types.js → server/src/preferences-types.js} +0 -0
  179. /package/dist/server/{repositories → server/src/repositories}/activity-events.js +0 -0
  180. /package/dist/server/{repositories → server/src/repositories}/collaboration.js +0 -0
  181. /package/dist/server/{repositories → server/src/repositories}/deleted-entities.js +0 -0
  182. /package/dist/server/{repositories → server/src/repositories}/diagnostic-logs.js +0 -0
  183. /package/dist/server/{repositories → server/src/repositories}/domains.js +0 -0
  184. /package/dist/server/{repositories → server/src/repositories}/event-log.js +0 -0
  185. /package/dist/server/{repositories → server/src/repositories}/goals.js +0 -0
  186. /package/dist/server/{repositories → server/src/repositories}/projects.js +0 -0
  187. /package/dist/server/{repositories → server/src/repositories}/psyche.js +0 -0
  188. /package/dist/server/{repositories → server/src/repositories}/rewards.js +0 -0
  189. /package/dist/server/{repositories → server/src/repositories}/strategies.js +0 -0
  190. /package/dist/server/{repositories → server/src/repositories}/tags.js +0 -0
  191. /package/dist/server/{repositories → server/src/repositories}/task-runs.js +0 -0
  192. /package/dist/server/{repositories → server/src/repositories}/tasks.js +0 -0
  193. /package/dist/server/{repositories → server/src/repositories}/users.js +0 -0
  194. /package/dist/server/{repositories → server/src/repositories}/weekly-reviews.js +0 -0
  195. /package/dist/server/{repositories → server/src/repositories}/work-adjustments.js +0 -0
  196. /package/dist/server/{seed-demo.js → server/src/seed-demo.js} +0 -0
  197. /package/dist/server/{services → server/src/services}/context.js +0 -0
  198. /package/dist/server/{services → server/src/services}/dashboard.js +0 -0
  199. /package/dist/server/{services → server/src/services}/gamification.js +0 -0
  200. /package/dist/server/{services → server/src/services}/insights.js +0 -0
  201. /package/dist/server/{services → server/src/services}/projects.js +0 -0
  202. /package/dist/server/{services → server/src/services}/psyche.js +0 -0
  203. /package/dist/server/{services → server/src/services}/relations.js +0 -0
  204. /package/dist/server/{services → server/src/services}/reviews.js +0 -0
  205. /package/dist/server/{services → server/src/services}/run-recovery.js +0 -0
  206. /package/dist/server/{services → server/src/services}/tagging.js +0 -0
  207. /package/dist/server/{services → server/src/services}/task-run-watchdog.js +0 -0
  208. /package/dist/server/{services → server/src/services}/work-time.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { userSummarySchema } from "./types.js";
2
+ import { noteSchema, userSummarySchema } from "./types.js";
3
3
  const trimmedString = z.string().trim();
4
4
  const nonEmptyTrimmedString = trimmedString.min(1);
5
5
  const uniqueStringArraySchema = z.array(nonEmptyTrimmedString);
@@ -258,6 +258,20 @@ export const psycheOverviewPayloadSchema = z.object({
258
258
  openNotes: z.number().int().nonnegative(),
259
259
  committedActions: z.array(trimmedString)
260
260
  });
261
+ export const psycheObservationEntrySchema = z.object({
262
+ id: z.string(),
263
+ observedAt: z.string(),
264
+ note: noteSchema,
265
+ linkedPatterns: z.array(behaviorPatternSchema),
266
+ linkedReports: z.array(triggerReportSchema)
267
+ });
268
+ export const psycheObservationCalendarPayloadSchema = z.object({
269
+ generatedAt: z.string(),
270
+ from: z.string(),
271
+ to: z.string(),
272
+ observations: z.array(psycheObservationEntrySchema),
273
+ availableTags: z.array(trimmedString)
274
+ });
261
275
  export const createPsycheValueSchema = z.object({
262
276
  title: nonEmptyTrimmedString,
263
277
  description: trimmedString.default(""),
@@ -0,0 +1,552 @@
1
+ import { HttpError } from "./errors.js";
2
+ const PARSE_CACHE = new Map();
3
+ function createQuestionnaireFlowError(message) {
4
+ return new HttpError(400, "questionnaire_flow_invalid", message);
5
+ }
6
+ function tokenize(script) {
7
+ const tokens = [];
8
+ let index = 0;
9
+ while (index < script.length) {
10
+ const current = script[index];
11
+ if (/\s/.test(current)) {
12
+ index += 1;
13
+ continue;
14
+ }
15
+ const pair = script.slice(index, index + 2);
16
+ if (pair === "==" ||
17
+ pair === "!=" ||
18
+ pair === ">=" ||
19
+ pair === "<=" ||
20
+ pair === "&&" ||
21
+ pair === "||") {
22
+ tokens.push({ type: pair });
23
+ index += 2;
24
+ continue;
25
+ }
26
+ if ("(),><+-*/!".includes(current)) {
27
+ tokens.push({
28
+ type: current
29
+ });
30
+ index += 1;
31
+ continue;
32
+ }
33
+ if (current === "'" || current === '"') {
34
+ const quote = current;
35
+ index += 1;
36
+ let value = "";
37
+ while (index < script.length && script[index] !== quote) {
38
+ const char = script[index];
39
+ if (char === "\\" && index + 1 < script.length) {
40
+ value += script[index + 1];
41
+ index += 2;
42
+ continue;
43
+ }
44
+ value += char;
45
+ index += 1;
46
+ }
47
+ if (script[index] !== quote) {
48
+ throw createQuestionnaireFlowError(`Unterminated string in flow rule: ${script}`);
49
+ }
50
+ index += 1;
51
+ tokens.push({ type: "string", value });
52
+ continue;
53
+ }
54
+ if (/\d/.test(current)) {
55
+ let value = current;
56
+ index += 1;
57
+ while (index < script.length && /[\d.]/.test(script[index])) {
58
+ value += script[index];
59
+ index += 1;
60
+ }
61
+ tokens.push({ type: "number", value: Number(value) });
62
+ continue;
63
+ }
64
+ if (/[A-Za-z_]/.test(current)) {
65
+ let value = current;
66
+ index += 1;
67
+ while (index < script.length && /[A-Za-z0-9_]/.test(script[index])) {
68
+ value += script[index];
69
+ index += 1;
70
+ }
71
+ const normalized = value.toLowerCase();
72
+ if (normalized === "and") {
73
+ tokens.push({ type: "&&" });
74
+ }
75
+ else if (normalized === "or") {
76
+ tokens.push({ type: "||" });
77
+ }
78
+ else if (normalized === "not") {
79
+ tokens.push({ type: "!" });
80
+ }
81
+ else if (normalized === "true") {
82
+ tokens.push({ type: "boolean", value: true });
83
+ }
84
+ else if (normalized === "false") {
85
+ tokens.push({ type: "boolean", value: false });
86
+ }
87
+ else if (normalized === "null") {
88
+ tokens.push({ type: "null" });
89
+ }
90
+ else {
91
+ tokens.push({ type: "identifier", value });
92
+ }
93
+ continue;
94
+ }
95
+ throw createQuestionnaireFlowError(`Unexpected token '${current}' in flow rule: ${script}`);
96
+ }
97
+ return tokens;
98
+ }
99
+ function parseExpression(tokens, script) {
100
+ let index = 0;
101
+ function peek() {
102
+ return tokens[index];
103
+ }
104
+ function consume(type) {
105
+ const token = tokens[index];
106
+ if (!token) {
107
+ return null;
108
+ }
109
+ if (type && token.type !== type) {
110
+ return null;
111
+ }
112
+ index += 1;
113
+ return token;
114
+ }
115
+ function expect(type) {
116
+ const token = consume(type);
117
+ if (!token) {
118
+ throw createQuestionnaireFlowError(`Expected '${type}' in flow rule: ${script}`);
119
+ }
120
+ return token;
121
+ }
122
+ function parsePrimary() {
123
+ const token = peek();
124
+ if (!token) {
125
+ throw createQuestionnaireFlowError(`Unexpected end of flow rule: ${script}`);
126
+ }
127
+ if (consume("(")) {
128
+ const expression = parseOr();
129
+ expect(")");
130
+ return expression;
131
+ }
132
+ if (token.type === "number") {
133
+ consume("number");
134
+ return { kind: "literal", value: token.value };
135
+ }
136
+ if (token.type === "string") {
137
+ consume("string");
138
+ return { kind: "literal", value: token.value };
139
+ }
140
+ if (token.type === "boolean") {
141
+ consume("boolean");
142
+ return { kind: "literal", value: token.value };
143
+ }
144
+ if (token.type === "null") {
145
+ consume("null");
146
+ return { kind: "literal", value: null };
147
+ }
148
+ if (token.type === "identifier") {
149
+ const identifier = consume("identifier").value;
150
+ if (consume("(")) {
151
+ const args = [];
152
+ if (!consume(")")) {
153
+ do {
154
+ args.push(parseOr());
155
+ } while (consume(","));
156
+ expect(")");
157
+ }
158
+ if (identifier !== "answered" &&
159
+ identifier !== "option" &&
160
+ identifier !== "label" &&
161
+ identifier !== "value") {
162
+ throw createQuestionnaireFlowError(`Unknown flow function '${identifier}' in rule: ${script}`);
163
+ }
164
+ return { kind: "call", name: identifier, args };
165
+ }
166
+ return { kind: "reference", itemId: identifier };
167
+ }
168
+ throw createQuestionnaireFlowError(`Unexpected token '${token.type}' in flow rule: ${script}`);
169
+ }
170
+ function parseUnary() {
171
+ if (consume("!")) {
172
+ return { kind: "unary", operator: "!", operand: parseUnary() };
173
+ }
174
+ if (consume("-")) {
175
+ return { kind: "unary", operator: "-", operand: parseUnary() };
176
+ }
177
+ return parsePrimary();
178
+ }
179
+ function parseMultiplicative() {
180
+ let expression = parseUnary();
181
+ while (true) {
182
+ if (consume("*")) {
183
+ expression = {
184
+ kind: "binary",
185
+ operator: "*",
186
+ left: expression,
187
+ right: parseUnary()
188
+ };
189
+ continue;
190
+ }
191
+ if (consume("/")) {
192
+ expression = {
193
+ kind: "binary",
194
+ operator: "/",
195
+ left: expression,
196
+ right: parseUnary()
197
+ };
198
+ continue;
199
+ }
200
+ return expression;
201
+ }
202
+ }
203
+ function parseAdditive() {
204
+ let expression = parseMultiplicative();
205
+ while (true) {
206
+ if (consume("+")) {
207
+ expression = {
208
+ kind: "binary",
209
+ operator: "+",
210
+ left: expression,
211
+ right: parseMultiplicative()
212
+ };
213
+ continue;
214
+ }
215
+ if (consume("-")) {
216
+ expression = {
217
+ kind: "binary",
218
+ operator: "-",
219
+ left: expression,
220
+ right: parseMultiplicative()
221
+ };
222
+ continue;
223
+ }
224
+ return expression;
225
+ }
226
+ }
227
+ function parseComparison() {
228
+ let expression = parseAdditive();
229
+ while (true) {
230
+ const token = peek();
231
+ const operator = token &&
232
+ (token.type === "==" ||
233
+ token.type === "!=" ||
234
+ token.type === ">=" ||
235
+ token.type === "<=" ||
236
+ token.type === ">" ||
237
+ token.type === "<")
238
+ ? token.type
239
+ : null;
240
+ if (!operator) {
241
+ return expression;
242
+ }
243
+ index += 1;
244
+ expression = {
245
+ kind: "binary",
246
+ operator,
247
+ left: expression,
248
+ right: parseAdditive()
249
+ };
250
+ }
251
+ }
252
+ function parseAnd() {
253
+ let expression = parseComparison();
254
+ while (consume("&&")) {
255
+ expression = {
256
+ kind: "binary",
257
+ operator: "&&",
258
+ left: expression,
259
+ right: parseComparison()
260
+ };
261
+ }
262
+ return expression;
263
+ }
264
+ function parseOr() {
265
+ let expression = parseAnd();
266
+ while (consume("||")) {
267
+ expression = {
268
+ kind: "binary",
269
+ operator: "||",
270
+ left: expression,
271
+ right: parseAnd()
272
+ };
273
+ }
274
+ return expression;
275
+ }
276
+ const expression = parseOr();
277
+ if (index < tokens.length) {
278
+ throw createQuestionnaireFlowError(`Unexpected trailing tokens in flow rule: ${script}`);
279
+ }
280
+ return expression;
281
+ }
282
+ function parseScript(script) {
283
+ const cached = PARSE_CACHE.get(script);
284
+ if (cached) {
285
+ return cached;
286
+ }
287
+ const parsed = parseExpression(tokenize(script), script);
288
+ PARSE_CACHE.set(script, parsed);
289
+ return parsed;
290
+ }
291
+ function resolveReference(expression) {
292
+ if (expression.kind === "reference") {
293
+ return expression.itemId;
294
+ }
295
+ if (expression.kind === "literal" &&
296
+ typeof expression.value === "string" &&
297
+ expression.value.trim().length > 0) {
298
+ return expression.value.trim();
299
+ }
300
+ return null;
301
+ }
302
+ function collectReferences(expression) {
303
+ switch (expression.kind) {
304
+ case "reference":
305
+ return [expression.itemId];
306
+ case "literal":
307
+ return [];
308
+ case "unary":
309
+ return collectReferences(expression.operand);
310
+ case "binary":
311
+ return [
312
+ ...collectReferences(expression.left),
313
+ ...collectReferences(expression.right)
314
+ ];
315
+ case "call": {
316
+ const direct = expression.args[0] ? resolveReference(expression.args[0]) : null;
317
+ return [
318
+ ...(direct ? [direct] : []),
319
+ ...expression.args.flatMap((arg) => collectReferences(arg))
320
+ ];
321
+ }
322
+ default:
323
+ return [];
324
+ }
325
+ }
326
+ function toBoolean(value) {
327
+ if (typeof value === "boolean") {
328
+ return value;
329
+ }
330
+ if (typeof value === "number") {
331
+ return value !== 0;
332
+ }
333
+ if (typeof value === "string") {
334
+ return value.length > 0;
335
+ }
336
+ return false;
337
+ }
338
+ function toNumber(value) {
339
+ if (typeof value === "number" && Number.isFinite(value)) {
340
+ return value;
341
+ }
342
+ if (typeof value === "boolean") {
343
+ return value ? 1 : 0;
344
+ }
345
+ if (typeof value === "string") {
346
+ const parsed = Number(value);
347
+ return Number.isFinite(parsed) ? parsed : null;
348
+ }
349
+ return null;
350
+ }
351
+ function compareValues(left, operator, right) {
352
+ const leftNumber = toNumber(left);
353
+ const rightNumber = toNumber(right);
354
+ if (leftNumber !== null && rightNumber !== null) {
355
+ switch (operator) {
356
+ case "==":
357
+ return leftNumber === rightNumber;
358
+ case "!=":
359
+ return leftNumber !== rightNumber;
360
+ case ">":
361
+ return leftNumber > rightNumber;
362
+ case ">=":
363
+ return leftNumber >= rightNumber;
364
+ case "<":
365
+ return leftNumber < rightNumber;
366
+ case "<=":
367
+ return leftNumber <= rightNumber;
368
+ }
369
+ }
370
+ if (operator === "==" || operator === "!=") {
371
+ return operator === "==" ? left === right : left !== right;
372
+ }
373
+ return false;
374
+ }
375
+ function evaluateExpression(expression, context) {
376
+ switch (expression.kind) {
377
+ case "literal":
378
+ return expression.value;
379
+ case "reference":
380
+ return context.getAnswerValue(expression.itemId);
381
+ case "unary": {
382
+ const value = evaluateExpression(expression.operand, context);
383
+ if (expression.operator === "!") {
384
+ return !toBoolean(value);
385
+ }
386
+ const number = toNumber(value);
387
+ return number === null ? null : -number;
388
+ }
389
+ case "binary": {
390
+ const left = evaluateExpression(expression.left, context);
391
+ const right = evaluateExpression(expression.right, context);
392
+ switch (expression.operator) {
393
+ case "&&":
394
+ return toBoolean(left) && toBoolean(right);
395
+ case "||":
396
+ return toBoolean(left) || toBoolean(right);
397
+ case "+":
398
+ return left === null || right === null
399
+ ? null
400
+ : (toNumber(left) ?? 0) + (toNumber(right) ?? 0);
401
+ case "-":
402
+ return left === null || right === null
403
+ ? null
404
+ : (toNumber(left) ?? 0) - (toNumber(right) ?? 0);
405
+ case "*":
406
+ return left === null || right === null
407
+ ? null
408
+ : (toNumber(left) ?? 0) * (toNumber(right) ?? 0);
409
+ case "/": {
410
+ const divisor = toNumber(right);
411
+ if (left === null || divisor === null || divisor === 0) {
412
+ return null;
413
+ }
414
+ return (toNumber(left) ?? 0) / divisor;
415
+ }
416
+ default:
417
+ return compareValues(left, expression.operator, right);
418
+ }
419
+ }
420
+ case "call": {
421
+ const reference = expression.args[0]
422
+ ? resolveReference(expression.args[0])
423
+ : null;
424
+ if (!reference) {
425
+ return null;
426
+ }
427
+ switch (expression.name) {
428
+ case "answered":
429
+ return context.isAnswered(reference);
430
+ case "option":
431
+ return context.getOptionKey(reference);
432
+ case "label":
433
+ return context.getLabel(reference);
434
+ case "value":
435
+ return context.getAnswerValue(reference);
436
+ }
437
+ }
438
+ }
439
+ }
440
+ function buildAnswerMap(answers) {
441
+ return new Map(answers
442
+ .map((answer) => {
443
+ const itemId = answer.itemId ?? answer.item_id;
444
+ if (!itemId) {
445
+ return null;
446
+ }
447
+ return [
448
+ itemId,
449
+ {
450
+ optionKey: answer.optionKey ?? answer.option_key ?? null,
451
+ numericValue: answer.numericValue ?? answer.numeric_value ?? null,
452
+ valueText: answer.valueText ?? answer.value_text ?? null
453
+ }
454
+ ];
455
+ })
456
+ .filter((entry) => entry !== null));
457
+ }
458
+ export function evaluateFlowRuleScript(script, answers, visibleItemIds) {
459
+ const expression = parseScript(script);
460
+ const answerMap = buildAnswerMap(answers);
461
+ const result = evaluateExpression(expression, {
462
+ getAnswerValue: (itemId) => {
463
+ if (!visibleItemIds.has(itemId)) {
464
+ return null;
465
+ }
466
+ const answer = answerMap.get(itemId);
467
+ return answer?.numericValue ?? answer?.optionKey ?? answer?.valueText ?? null;
468
+ },
469
+ getOptionKey: (itemId) => visibleItemIds.has(itemId) ? answerMap.get(itemId)?.optionKey ?? null : null,
470
+ getLabel: (itemId) => visibleItemIds.has(itemId) ? answerMap.get(itemId)?.valueText ?? null : null,
471
+ isAnswered: (itemId) => {
472
+ if (!visibleItemIds.has(itemId)) {
473
+ return false;
474
+ }
475
+ const answer = answerMap.get(itemId);
476
+ return Boolean(answer &&
477
+ (answer.optionKey !== null ||
478
+ answer.numericValue !== null ||
479
+ (answer.valueText ?? "").trim().length > 0));
480
+ }
481
+ });
482
+ return toBoolean(result);
483
+ }
484
+ export function getQuestionnaireVisibilityState(definition, answers) {
485
+ const visibleItemIds = new Set();
486
+ const visibleSectionIds = new Set();
487
+ const itemById = new Map(definition.items.map((item) => [item.id, item]));
488
+ for (const section of definition.sections) {
489
+ const sectionVisible = !section.visibility ||
490
+ evaluateFlowRuleScript(section.visibility.script, answers, visibleItemIds);
491
+ if (!sectionVisible) {
492
+ continue;
493
+ }
494
+ const visibleItemsInSection = section.itemIds.filter((itemId) => {
495
+ const item = itemById.get(itemId);
496
+ if (!item) {
497
+ return false;
498
+ }
499
+ const itemVisible = !item.visibility ||
500
+ evaluateFlowRuleScript(item.visibility.script, answers, visibleItemIds);
501
+ if (itemVisible) {
502
+ visibleItemIds.add(item.id);
503
+ }
504
+ return itemVisible;
505
+ });
506
+ if (visibleItemsInSection.length > 0) {
507
+ visibleSectionIds.add(section.id);
508
+ }
509
+ }
510
+ return {
511
+ visibleItemIds,
512
+ visibleSectionIds
513
+ };
514
+ }
515
+ export function validateQuestionnaireFlow(definition) {
516
+ const knownItemIds = new Set(definition.items.map((item) => item.id));
517
+ const itemOrder = new Map(definition.itemIds.map((itemId, index) => [itemId, index]));
518
+ for (const section of definition.sections) {
519
+ for (const itemId of section.itemIds) {
520
+ if (!knownItemIds.has(itemId)) {
521
+ throw createQuestionnaireFlowError(`Questionnaire section '${section.id}' references unknown item '${itemId}'.`);
522
+ }
523
+ }
524
+ if (section.visibility) {
525
+ const refs = Array.from(new Set(collectReferences(parseScript(section.visibility.script))));
526
+ const firstSectionItemIndex = Math.min(...section.itemIds.map((itemId) => itemOrder.get(itemId) ?? Number.MAX_SAFE_INTEGER));
527
+ for (const ref of refs) {
528
+ if (!knownItemIds.has(ref)) {
529
+ throw createQuestionnaireFlowError(`Section '${section.id}' flow rule references unknown item '${ref}'.`);
530
+ }
531
+ if ((itemOrder.get(ref) ?? -1) >= firstSectionItemIndex) {
532
+ throw createQuestionnaireFlowError(`Section '${section.id}' flow rule must only depend on earlier items.`);
533
+ }
534
+ }
535
+ }
536
+ }
537
+ for (const item of definition.items) {
538
+ if (!item.visibility) {
539
+ continue;
540
+ }
541
+ const refs = Array.from(new Set(collectReferences(parseScript(item.visibility.script))));
542
+ const itemIndex = itemOrder.get(item.id) ?? Number.MAX_SAFE_INTEGER;
543
+ for (const ref of refs) {
544
+ if (!knownItemIds.has(ref)) {
545
+ throw createQuestionnaireFlowError(`Item '${item.id}' flow rule references unknown item '${ref}'.`);
546
+ }
547
+ if ((itemOrder.get(ref) ?? -1) >= itemIndex) {
548
+ throw createQuestionnaireFlowError(`Item '${item.id}' flow rule must only depend on earlier items.`);
549
+ }
550
+ }
551
+ }
552
+ }