@topogram/cli 0.3.51 → 0.3.52

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 (77) hide show
  1. package/ARCHITECTURE.md +4 -4
  2. package/CHANGELOG.md +11 -11
  3. package/package.json +1 -1
  4. package/src/adoption/plan.js +2 -2
  5. package/src/agent-ops/query-builders.js +42 -33
  6. package/src/cli.js +174 -129
  7. package/src/generator/adapters.d.ts +1 -0
  8. package/src/generator/adapters.js +64 -39
  9. package/src/generator/check.js +19 -12
  10. package/src/generator/context/diff.js +9 -9
  11. package/src/generator/context/domain-coverage.js +11 -10
  12. package/src/generator/context/domain-page.js +6 -6
  13. package/src/generator/context/shared.js +37 -21
  14. package/src/generator/context/slice.js +70 -65
  15. package/src/generator/index.js +12 -12
  16. package/src/generator/output.js +21 -20
  17. package/src/generator/registry.js +61 -49
  18. package/src/generator/runtime/app-bundle.js +15 -15
  19. package/src/generator/runtime/compile-check.js +7 -7
  20. package/src/generator/runtime/deployment.js +9 -9
  21. package/src/generator/runtime/environment.js +39 -39
  22. package/src/generator/runtime/runtime-check.js +5 -5
  23. package/src/generator/runtime/shared.js +40 -38
  24. package/src/generator/runtime/smoke.js +5 -5
  25. package/src/generator/surfaces/databases/contract.js +1 -1
  26. package/src/generator/surfaces/databases/lifecycle-shared.js +6 -5
  27. package/src/generator/surfaces/databases/postgres/drizzle.js +3 -2
  28. package/src/generator/surfaces/databases/postgres/prisma.js +3 -2
  29. package/src/generator/surfaces/databases/shared.js +3 -2
  30. package/src/generator/surfaces/databases/snapshot.js +1 -1
  31. package/src/generator/surfaces/databases/sqlite/prisma.js +3 -2
  32. package/src/generator/surfaces/native/swiftui-app.js +3 -3
  33. package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +1 -1
  34. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +3 -3
  35. package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +3 -3
  36. package/src/generator/surfaces/services/persistence-wiring.js +3 -2
  37. package/src/generator/surfaces/services/server-contract.js +4 -4
  38. package/src/generator/surfaces/shared.js +2 -2
  39. package/src/generator/surfaces/web/design-intent.js +1 -1
  40. package/src/generator/surfaces/web/index.js +7 -7
  41. package/src/generator/surfaces/web/{react-components.js → react-widgets.js} +53 -53
  42. package/src/generator/surfaces/web/react.js +36 -36
  43. package/src/generator/surfaces/web/{sveltekit-components.js → sveltekit-widgets.js} +53 -53
  44. package/src/generator/surfaces/web/sveltekit.js +34 -34
  45. package/src/generator/surfaces/web/{ui-web-contract.js → ui-surface-contract.js} +8 -8
  46. package/src/generator/surfaces/web/vanilla.js +6 -6
  47. package/src/generator/{component-conformance.js → widget-conformance.js} +129 -128
  48. package/src/generator/widgets.js +40 -0
  49. package/src/generator-policy.js +10 -12
  50. package/src/import/core/runner.js +34 -34
  51. package/src/import/core/shared.js +1 -1
  52. package/src/import/extractors/ui/android-compose.js +1 -1
  53. package/src/import/extractors/ui/blazor.js +1 -1
  54. package/src/import/extractors/ui/razor-pages.js +1 -1
  55. package/src/import/extractors/ui/react-router.js +4 -4
  56. package/src/import/extractors/ui/sveltekit.js +4 -4
  57. package/src/import/extractors/ui/swiftui.js +1 -1
  58. package/src/import/extractors/ui/uikit.js +1 -1
  59. package/src/new-project.js +19 -18
  60. package/src/project-config.js +92 -42
  61. package/src/proofs/contract-audit.js +1 -1
  62. package/src/proofs/ios-parity.js +1 -1
  63. package/src/proofs/issues-parity.js +1 -1
  64. package/src/realization/backend/build-backend-runtime-realization.js +2 -2
  65. package/src/realization/ui/build-ui-shared-realization.js +33 -33
  66. package/src/realization/ui/build-web-realization.js +23 -20
  67. package/src/reconcile/journeys.js +1 -1
  68. package/src/resolver/index.js +148 -65
  69. package/src/validator/index.js +473 -423
  70. package/src/validator/kinds.js +36 -36
  71. package/src/validator/per-kind/{component.js → widget.js} +47 -47
  72. package/src/{component-behavior.js → widget-behavior.js} +3 -3
  73. package/src/workflows.js +39 -38
  74. package/template-helpers/react.js +4 -4
  75. package/template-helpers/sveltekit.js +4 -4
  76. package/src/generator/components.js +0 -39
  77. /package/src/resolver/enrich/{component.js → widget.js} +0 -0
@@ -7,7 +7,7 @@ export const STATEMENT_KINDS = new Set([
7
7
  "shape",
8
8
  "rule",
9
9
  "capability",
10
- "component",
10
+ "widget",
11
11
  "decision",
12
12
  "projection",
13
13
  "orchestration",
@@ -148,7 +148,7 @@ export const FIELD_SPECS = {
148
148
  required: ["name", "description", "status"],
149
149
  allowed: ["name", "description", "actors", "roles", "reads", "creates", "updates", "deletes", "input", "output", "domain", "status"]
150
150
  },
151
- component: {
151
+ widget: {
152
152
  required: ["name", "description", "props", "status"],
153
153
  allowed: ["name", "description", "category", "props", "events", "slots", "behavior", "behaviors", "patterns", "regions", "lookups", "dependencies", "version", "approvals", "status"]
154
154
  },
@@ -157,45 +157,45 @@ export const FIELD_SPECS = {
157
157
  allowed: ["name", "description", "context", "consequences", "pitch", "supersedes", "domain", "status"]
158
158
  },
159
159
  projection: {
160
- required: ["name", "description", "platform", "realizes", "outputs", "status"],
160
+ required: ["name", "description", "type", "realizes", "outputs", "status"],
161
161
  allowed: [
162
162
  "name",
163
163
  "description",
164
- "platform",
164
+ "type",
165
165
  "realizes",
166
166
  "outputs",
167
- "http",
168
- "http_errors",
169
- "http_fields",
170
- "http_responses",
171
- "http_preconditions",
172
- "http_idempotency",
173
- "http_cache",
174
- "http_delete",
175
- "http_async",
176
- "http_status",
177
- "http_download",
178
- "http_authz",
179
- "http_callbacks",
180
- "ui_screens",
181
- "ui_collections",
182
- "ui_actions",
183
- "ui_visibility",
184
- "ui_lookups",
185
- "ui_routes",
186
- "ui_web",
187
- "ui_ios",
188
- "ui_app_shell",
189
- "ui_navigation",
190
- "ui_screen_regions",
191
- "ui_components",
192
- "ui_design",
193
- "db_tables",
194
- "db_columns",
195
- "db_keys",
196
- "db_indexes",
197
- "db_relations",
198
- "db_lifecycle",
167
+ "endpoints",
168
+ "error_responses",
169
+ "wire_fields",
170
+ "responses",
171
+ "preconditions",
172
+ "idempotency",
173
+ "cache",
174
+ "delete_semantics",
175
+ "async_jobs",
176
+ "async_status",
177
+ "downloads",
178
+ "authorization",
179
+ "callbacks",
180
+ "screens",
181
+ "collection_views",
182
+ "screen_actions",
183
+ "visibility_rules",
184
+ "field_lookups",
185
+ "screen_routes",
186
+ "web_hints",
187
+ "ios_hints",
188
+ "app_shell",
189
+ "navigation",
190
+ "screen_regions",
191
+ "widget_bindings",
192
+ "design_tokens",
193
+ "tables",
194
+ "columns",
195
+ "keys",
196
+ "indexes",
197
+ "relations",
198
+ "lifecycle",
199
199
  "generator_defaults",
200
200
  "status"
201
201
  ]
@@ -9,7 +9,7 @@ import {
9
9
  symbolValues
10
10
  } from "../utils.js";
11
11
 
12
- const COMPONENT_CATEGORIES = new Set([
12
+ const WIDGET_CATEGORIES = new Set([
13
13
  "collection",
14
14
  "form",
15
15
  "display",
@@ -21,7 +21,7 @@ const COMPONENT_CATEGORIES = new Set([
21
21
  "service"
22
22
  ]);
23
23
 
24
- const COMPONENT_BEHAVIOR_KINDS = new Set([
24
+ const WIDGET_BEHAVIOR_KINDS = new Set([
25
25
  "selection",
26
26
  "sorting",
27
27
  "filtering",
@@ -36,7 +36,7 @@ const COMPONENT_BEHAVIOR_KINDS = new Set([
36
36
  "keyboard_navigation"
37
37
  ]);
38
38
 
39
- const COMPONENT_BEHAVIOR_DIRECTIVES = {
39
+ const WIDGET_BEHAVIOR_DIRECTIVES = {
40
40
  selection: new Set(["mode", "state", "emits"]),
41
41
  sorting: new Set(["fields", "default"]),
42
42
  filtering: new Set(["fields"]),
@@ -66,101 +66,101 @@ function tokenValues(token) {
66
66
  return value == null ? [] : [value];
67
67
  }
68
68
 
69
- function componentPropNames(statement) {
69
+ function widgetPropNames(statement) {
70
70
  return new Set(blockEntries(getFieldValue(statement, "props"))
71
71
  .map((entry) => entry.items[0])
72
72
  .filter((item) => item?.type === "symbol")
73
73
  .map((item) => item.value));
74
74
  }
75
75
 
76
- function componentEventNames(statement) {
76
+ function widgetEventNames(statement) {
77
77
  return new Set(blockEntries(getFieldValue(statement, "events"))
78
78
  .map((entry) => entry.items[0])
79
79
  .filter((item) => item?.type === "symbol")
80
80
  .map((item) => item.value));
81
81
  }
82
82
 
83
- function validateComponentCategory(errors, statement, fieldMap) {
83
+ function validateWidgetCategory(errors, statement, fieldMap) {
84
84
  const field = fieldMap.get("category")?.[0];
85
85
  if (!field) {
86
86
  return;
87
87
  }
88
88
 
89
89
  if (field.value.type !== "symbol") {
90
- pushError(errors, `Field 'category' on component ${statement.id} must be a symbol`, field.loc);
90
+ pushError(errors, `Field 'category' on widget ${statement.id} must be a symbol`, field.loc);
91
91
  return;
92
92
  }
93
93
 
94
- if (!COMPONENT_CATEGORIES.has(field.value.value)) {
95
- pushError(errors, `Invalid component category '${field.value.value}' on component ${statement.id}`, field.loc);
94
+ if (!WIDGET_CATEGORIES.has(field.value.value)) {
95
+ pushError(errors, `Invalid widget category '${field.value.value}' on widget ${statement.id}`, field.loc);
96
96
  }
97
97
  }
98
98
 
99
- function validateComponentProps(errors, statement) {
99
+ function validateWidgetProps(errors, statement) {
100
100
  for (const entry of blockEntries(getFieldValue(statement, "props"))) {
101
101
  const [name, type, requiredness, ...rest] = entry.items;
102
102
  if (!name || !type || !requiredness) {
103
- pushError(errors, `Component ${statement.id} props entries must be '<name> <type> <required|optional> [default <value>]'`, entry.loc);
103
+ pushError(errors, `Widget ${statement.id} props entries must be '<name> <type> <required|optional> [default <value>]'`, entry.loc);
104
104
  continue;
105
105
  }
106
106
 
107
107
  if (name.type !== "symbol" || type.type !== "symbol" || requiredness.type !== "symbol") {
108
- pushError(errors, `Component ${statement.id} props entries must start with symbols`, entry.loc);
108
+ pushError(errors, `Widget ${statement.id} props entries must start with symbols`, entry.loc);
109
109
  continue;
110
110
  }
111
111
 
112
112
  if (requiredness.value !== "required" && requiredness.value !== "optional") {
113
- pushError(errors, `Component ${statement.id} prop '${name.value}' must use required or optional`, requiredness.loc);
113
+ pushError(errors, `Widget ${statement.id} prop '${name.value}' must use required or optional`, requiredness.loc);
114
114
  }
115
115
 
116
116
  for (let i = 0; i < rest.length; i += 1) {
117
117
  const token = rest[i];
118
118
  if (token.type === "symbol" && token.value === "default") {
119
119
  if (!rest[i + 1]) {
120
- pushError(errors, `Component ${statement.id} prop '${name.value}' default is missing a value`, token.loc);
120
+ pushError(errors, `Widget ${statement.id} prop '${name.value}' default is missing a value`, token.loc);
121
121
  }
122
122
  i += 1;
123
123
  continue;
124
124
  }
125
- pushError(errors, `Component ${statement.id} prop '${name.value}' has unsupported directive '${token.value}'`, token.loc);
125
+ pushError(errors, `Widget ${statement.id} prop '${name.value}' has unsupported directive '${token.value}'`, token.loc);
126
126
  }
127
127
  }
128
128
  }
129
129
 
130
- function validateComponentEvents(errors, statement, registry) {
130
+ function validateWidgetEvents(errors, statement, registry) {
131
131
  for (const entry of blockEntries(getFieldValue(statement, "events"))) {
132
132
  const [eventName, shapeRef] = entry.items;
133
133
  if (!eventName || !shapeRef) {
134
- pushError(errors, `Component ${statement.id} events entries must be '<event_name> <shape_id>'`, entry.loc);
134
+ pushError(errors, `Widget ${statement.id} events entries must be '<event_name> <shape_id>'`, entry.loc);
135
135
  continue;
136
136
  }
137
137
 
138
138
  if (eventName.type !== "symbol" || shapeRef.type !== "symbol") {
139
- pushError(errors, `Component ${statement.id} events entries must use symbols`, entry.loc);
139
+ pushError(errors, `Widget ${statement.id} events entries must use symbols`, entry.loc);
140
140
  continue;
141
141
  }
142
142
 
143
143
  const target = registry.get(shapeRef.value);
144
144
  if (!target) {
145
- pushError(errors, `Component ${statement.id} event '${eventName.value}' references missing shape '${shapeRef.value}'`, shapeRef.loc);
145
+ pushError(errors, `Widget ${statement.id} event '${eventName.value}' references missing shape '${shapeRef.value}'`, shapeRef.loc);
146
146
  continue;
147
147
  }
148
148
  if (target.kind !== "shape") {
149
- pushError(errors, `Component ${statement.id} event '${eventName.value}' must reference a shape, found ${target.kind} '${target.id}'`, shapeRef.loc);
149
+ pushError(errors, `Widget ${statement.id} event '${eventName.value}' must reference a shape, found ${target.kind} '${target.id}'`, shapeRef.loc);
150
150
  }
151
151
  }
152
152
  }
153
153
 
154
- function validateComponentSlots(errors, statement) {
154
+ function validateWidgetSlots(errors, statement) {
155
155
  for (const entry of blockEntries(getFieldValue(statement, "slots"))) {
156
156
  const [slotName, description] = entry.items;
157
157
  if (!slotName || !description) {
158
- pushError(errors, `Component ${statement.id} slots entries must be '<slot_name> <description>'`, entry.loc);
158
+ pushError(errors, `Widget ${statement.id} slots entries must be '<slot_name> <description>'`, entry.loc);
159
159
  continue;
160
160
  }
161
161
 
162
162
  if (slotName.type !== "symbol" || (description.type !== "string" && description.type !== "symbol")) {
163
- pushError(errors, `Component ${statement.id} slots entries must use a symbol name and string or symbol description`, entry.loc);
163
+ pushError(errors, `Widget ${statement.id} slots entries must use a symbol name and string or symbol description`, entry.loc);
164
164
  }
165
165
  }
166
166
  }
@@ -173,7 +173,7 @@ function validateSymbolList(errors, statement, fieldMap, key, allowed, label) {
173
173
 
174
174
  for (const value of symbolValues(field.value)) {
175
175
  if (!allowed.has(value)) {
176
- pushError(errors, `Component ${statement.id} ${label} '${value}' is not supported`, field.loc);
176
+ pushError(errors, `Widget ${statement.id} ${label} '${value}' is not supported`, field.loc);
177
177
  }
178
178
  }
179
179
  }
@@ -189,60 +189,60 @@ function validateBehaviorActionReferences(errors, statement, registry, kind, dir
189
189
  }
190
190
  pushError(
191
191
  errors,
192
- `Component ${statement.id} behavior '${kind}' references unknown event or capability '${actionId}' for '${directive}'`,
192
+ `Widget ${statement.id} behavior '${kind}' references unknown event or capability '${actionId}' for '${directive}'`,
193
193
  valueToken.loc
194
194
  );
195
195
  }
196
196
  }
197
197
 
198
- function validateComponentBehaviors(errors, statement, fieldMap, registry) {
199
- validateSymbolList(errors, statement, fieldMap, "behavior", COMPONENT_BEHAVIOR_KINDS, "behavior");
198
+ function validateWidgetBehaviors(errors, statement, fieldMap, registry) {
199
+ validateSymbolList(errors, statement, fieldMap, "behavior", WIDGET_BEHAVIOR_KINDS, "behavior");
200
200
 
201
201
  const field = fieldMap.get("behaviors")?.[0];
202
202
  if (!field || field.value.type !== "block") {
203
203
  return;
204
204
  }
205
205
 
206
- const propNames = componentPropNames(statement);
207
- const eventNames = componentEventNames(statement);
206
+ const propNames = widgetPropNames(statement);
207
+ const eventNames = widgetEventNames(statement);
208
208
 
209
209
  for (const entry of field.value.entries) {
210
210
  const [kindToken, ...rest] = entry.items;
211
211
  const kind = tokenValue(kindToken);
212
212
  if (!kind || kindToken.type !== "symbol") {
213
- pushError(errors, `Component ${statement.id} behaviors entries must start with a behavior kind`, entry.loc);
213
+ pushError(errors, `Widget ${statement.id} behaviors entries must start with a behavior kind`, entry.loc);
214
214
  continue;
215
215
  }
216
- if (!COMPONENT_BEHAVIOR_KINDS.has(kind)) {
217
- pushError(errors, `Component ${statement.id} behavior '${kind}' is not supported`, entry.loc);
216
+ if (!WIDGET_BEHAVIOR_KINDS.has(kind)) {
217
+ pushError(errors, `Widget ${statement.id} behavior '${kind}' is not supported`, entry.loc);
218
218
  continue;
219
219
  }
220
220
 
221
- const allowedDirectives = COMPONENT_BEHAVIOR_DIRECTIVES[kind] || new Set();
221
+ const allowedDirectives = WIDGET_BEHAVIOR_DIRECTIVES[kind] || new Set();
222
222
  for (let i = 0; i < rest.length; i += 2) {
223
223
  const directiveToken = rest[i];
224
224
  const valueToken = rest[i + 1];
225
225
  const directive = tokenValue(directiveToken);
226
226
  if (!directive || directiveToken.type !== "symbol") {
227
- pushError(errors, `Component ${statement.id} behavior '${kind}' directives must use symbol keys`, entry.loc);
227
+ pushError(errors, `Widget ${statement.id} behavior '${kind}' directives must use symbol keys`, entry.loc);
228
228
  continue;
229
229
  }
230
230
  if (!valueToken) {
231
- pushError(errors, `Component ${statement.id} behavior '${kind}' is missing a value for '${directive}'`, directiveToken.loc);
231
+ pushError(errors, `Widget ${statement.id} behavior '${kind}' is missing a value for '${directive}'`, directiveToken.loc);
232
232
  continue;
233
233
  }
234
234
  if (!allowedDirectives.has(directive)) {
235
- pushError(errors, `Component ${statement.id} behavior '${kind}' has unsupported directive '${directive}'`, directiveToken.loc);
235
+ pushError(errors, `Widget ${statement.id} behavior '${kind}' has unsupported directive '${directive}'`, directiveToken.loc);
236
236
  continue;
237
237
  }
238
238
 
239
239
  if (directive === "state" && !propNames.has(tokenValue(valueToken))) {
240
- pushError(errors, `Component ${statement.id} behavior '${kind}' references unknown prop '${tokenValue(valueToken)}' for '${directive}'`, valueToken.loc);
240
+ pushError(errors, `Widget ${statement.id} behavior '${kind}' references unknown prop '${tokenValue(valueToken)}' for '${directive}'`, valueToken.loc);
241
241
  }
242
242
  if (directive === "emits") {
243
243
  for (const eventName of tokenValues(valueToken)) {
244
244
  if (!eventNames.has(eventName)) {
245
- pushError(errors, `Component ${statement.id} behavior '${kind}' references unknown event '${eventName}' for '${directive}'`, valueToken.loc);
245
+ pushError(errors, `Widget ${statement.id} behavior '${kind}' references unknown event '${eventName}' for '${directive}'`, valueToken.loc);
246
246
  }
247
247
  }
248
248
  }
@@ -250,25 +250,25 @@ function validateComponentBehaviors(errors, statement, fieldMap, registry) {
250
250
  validateBehaviorActionReferences(errors, statement, registry, kind, directive, valueToken, eventNames);
251
251
  }
252
252
  if (kind === "selection" && directive === "mode" && !["single", "multi", "none"].includes(tokenValue(valueToken))) {
253
- pushError(errors, `Component ${statement.id} behavior 'selection' has invalid mode '${tokenValue(valueToken)}'`, valueToken.loc);
253
+ pushError(errors, `Widget ${statement.id} behavior 'selection' has invalid mode '${tokenValue(valueToken)}'`, valueToken.loc);
254
254
  }
255
255
  if (kind === "pagination" && directive === "mode" && !["cursor", "paged", "infinite", "none"].includes(tokenValue(valueToken))) {
256
- pushError(errors, `Component ${statement.id} behavior 'pagination' has invalid mode '${tokenValue(valueToken)}'`, valueToken.loc);
256
+ pushError(errors, `Widget ${statement.id} behavior 'pagination' has invalid mode '${tokenValue(valueToken)}'`, valueToken.loc);
257
257
  }
258
258
  }
259
259
  }
260
260
  }
261
261
 
262
- export function validateComponent(errors, statement, fieldMap, registry) {
263
- if (statement.kind !== "component") {
262
+ export function validateWidget(errors, statement, fieldMap, registry) {
263
+ if (statement.kind !== "widget") {
264
264
  return;
265
265
  }
266
266
 
267
- validateComponentCategory(errors, statement, fieldMap);
268
- validateComponentProps(errors, statement);
269
- validateComponentEvents(errors, statement, registry);
270
- validateComponentSlots(errors, statement);
271
- validateComponentBehaviors(errors, statement, fieldMap, registry);
267
+ validateWidgetCategory(errors, statement, fieldMap);
268
+ validateWidgetProps(errors, statement);
269
+ validateWidgetEvents(errors, statement, registry);
270
+ validateWidgetSlots(errors, statement);
271
+ validateWidgetBehaviors(errors, statement, fieldMap, registry);
272
272
  validateSymbolList(errors, statement, fieldMap, "patterns", UI_PATTERN_KINDS, "pattern");
273
273
  validateSymbolList(errors, statement, fieldMap, "regions", UI_REGION_KINDS, "region");
274
274
  }
@@ -119,9 +119,9 @@ function behaviorStatus({ hasDirectives, state, emits }) {
119
119
  }
120
120
 
121
121
  /**
122
- * Build projection-specific realizations for component behavior contracts.
122
+ * Build projection-specific realizations for widget behavior contracts.
123
123
  *
124
- * Components declare reusable behavior capabilities; projection ui_components
124
+ * Widgets declare reusable behavior capabilities; projection widget_bindings
125
125
  * bindings provide concrete data/event outcomes. This derived contract is the
126
126
  * normalized bridge agents and generators can use without inferring behavior
127
127
  * from stack code.
@@ -130,7 +130,7 @@ function behaviorStatus({ hasDirectives, state, emits }) {
130
130
  * @param {Record<string, any>} usage
131
131
  * @returns {Array<Record<string, any>>}
132
132
  */
133
- export function buildComponentBehaviorRealizations(contract, usage) {
133
+ export function buildWidgetBehaviorRealizations(contract, usage) {
134
134
  const props = propByName(contract);
135
135
  const eventNames = new Set((contract?.events || []).map((event) => event.id).filter(Boolean));
136
136
  return (contract?.behaviors || []).map((behavior) => {