@ai-matrx/alchemy 0.5.1 → 0.6.0

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.
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Matrx Alchemy — Declare types (CONTRACT §2.1).
3
+ *
4
+ * A declaration is plain JSON data (chair ruling N2): no functions, no classes,
5
+ * no React. Runtime behaviour lives in `MountedSurface` (`/declare/host`) and in
6
+ * handler registrations, never here. Zero React, zero `@ai-matrx/agents`.
7
+ */
8
+ type Json = null | boolean | number | string | Json[] | {
9
+ [key: string]: Json;
10
+ };
11
+ /** Unchanged from `ui_surface_value_type_chk`. */
12
+ type ValueType = "string" | "number" | "boolean" | "object" | "array" | "document";
13
+ type Classification = "ordinary" | "secret" | "credential";
14
+ /** Persisted (CONTRACT §2.7). Projection enforces it before any value leaves. */
15
+ interface Sensitivity {
16
+ /** false = never leaves through Operate. Default true. */
17
+ exportable: boolean;
18
+ /** secret/credential are always excluded; no caller can relax this. Default "ordinary". */
19
+ classification: Classification;
20
+ /** Pre-checked in the preparation workspace. Default true. */
21
+ includedByDefault: boolean;
22
+ }
23
+ interface ValueGroup {
24
+ key: string;
25
+ label: string;
26
+ sortOrder: number;
27
+ description?: string;
28
+ }
29
+ /** The four runtime slots a capture fills (absorbs page-capture). */
30
+ type LiveSlot = "selection" | "on-demand" | "errors" | "requests";
31
+ interface DeclaredValue {
32
+ name: string;
33
+ label: string;
34
+ description: string;
35
+ valueType: ValueType;
36
+ /** Registered content-ir kind key (DB: kind_key). */
37
+ valueKind?: string;
38
+ alwaysAvailable: boolean;
39
+ typicalCharCount: number;
40
+ autoContext?: boolean;
41
+ group?: string;
42
+ sortOrder?: number;
43
+ sensitivity?: Partial<Sensitivity>;
44
+ /** Filled at capture time from a live slot instead of the scope. */
45
+ liveSlot?: LiveSlot;
46
+ /** "request" values drop out of "Only the data". Default "data". */
47
+ role?: "data" | "request";
48
+ }
49
+ /** Where a write lands. "stream" = applied live, op by op, into an editable text. */
50
+ type WriteMode = "draft" | "entity" | "ui" | "stream";
51
+ /** Who may apply WITHOUT a click. "queued" = filed for an organization's review. */
52
+ type ApplyPolicy = "manual" | "ask" | "auto" | "queued";
53
+ /** The WidgetHandle text op set, unchanged in meaning. */
54
+ type StreamOp = "replace" | "insert_before" | "insert_after" | "prepend" | "append" | "patch";
55
+ interface WriteTarget {
56
+ name: string;
57
+ label: string;
58
+ description: string;
59
+ valueType: ValueType;
60
+ valueKind?: string;
61
+ /** Read twin (evidence loop). Required when patchable or mode === "stream". */
62
+ updatesValue?: string;
63
+ patchable?: boolean;
64
+ approvalComparison?: "text-replacement";
65
+ mode: WriteMode;
66
+ /** Required iff mode === "stream". */
67
+ streamOps?: readonly StreamOp[];
68
+ /** Default "manual". Binding overrides may only tighten. */
69
+ applyPolicy?: ApplyPolicy;
70
+ destination?: boolean;
71
+ /** Present when the target writes into an item rather than the screen. */
72
+ itemType?: string;
73
+ group?: string;
74
+ sortOrder?: number;
75
+ }
76
+ /** One kind of thing inside a screen (a row, a message, a code block, a card). */
77
+ interface ItemType {
78
+ name: string;
79
+ label: string;
80
+ description: string;
81
+ /** Value names that together identify one item. */
82
+ identity: readonly string[];
83
+ levels?: readonly string[];
84
+ entityType?: string;
85
+ values: readonly DeclaredValue[];
86
+ writeTargets?: readonly WriteTarget[];
87
+ }
88
+ type Readiness = "verified" | "partial" | "stub";
89
+ /** `ui_surface_execution_mode_check`, unchanged. */
90
+ type ExecutionMode = "python-stream" | "nextjs-stream" | "browser-realtime" | "local-runtime";
91
+ interface ConfigNamespaceDeclaration {
92
+ namespace: string;
93
+ label: string;
94
+ description: string;
95
+ }
96
+ interface EvidenceSourceDeclaration {
97
+ kind: "processed_document";
98
+ idValue: string;
99
+ fileIdValue?: string;
100
+ labelValue?: string;
101
+ representation?: "clean" | "raw";
102
+ }
103
+ /**
104
+ * One screen's declaration. Owning packages add fields (agent roles, client
105
+ * tools) as EXTENSION SLOTS — by interface extension or declaration merging —
106
+ * and register a `DeclarationExtension` so validation and the sync cover them.
107
+ */
108
+ interface SurfaceDeclaration {
109
+ surfaceName: string;
110
+ /** REQUIRED. `ui_surface.client_name`; must equal the surfaceName prefix. */
111
+ client: string;
112
+ /** REQUIRED. `ui_surface.execution_mode`. */
113
+ executionMode: ExecutionMode;
114
+ /** REQUIRED. `ui_surface.description` ("" only where the live row is empty). */
115
+ description: string;
116
+ label: string;
117
+ readiness: Readiness;
118
+ readinessNote?: string;
119
+ urlPattern?: string;
120
+ inheritsFrom?: string;
121
+ intro?: string;
122
+ overlayId?: string;
123
+ values: readonly DeclaredValue[];
124
+ groups?: readonly ValueGroup[];
125
+ writeTargets?: readonly WriteTarget[];
126
+ itemTypes?: readonly ItemType[];
127
+ liveSlots?: readonly LiveSlot[];
128
+ configNamespaces?: readonly ConfigNamespaceDeclaration[];
129
+ evidenceSources?: readonly EvidenceSourceDeclaration[];
130
+ skipBaselineValues?: boolean;
131
+ }
132
+ interface DeclarationIssue {
133
+ surfaceName: string;
134
+ /** "writeTargets/body/updatesValue" */
135
+ path: string;
136
+ sentence: string;
137
+ remedy: string;
138
+ /** Advisory issues are reported, never fatal (the ratchets). Default "error". */
139
+ severity?: "error" | "advisory";
140
+ }
141
+ /** A row the sync writes for an extension slot (e.g. agent roles, client tools). */
142
+ interface ExtensionRow {
143
+ table: string;
144
+ /** The row's key columns within its table (e.g. ["surface_name", "name"]). */
145
+ conflict: readonly string[];
146
+ row: Record<string, Json>;
147
+ }
148
+ interface DeclarationExtension<D extends SurfaceDeclaration = SurfaceDeclaration> {
149
+ key: string;
150
+ /** Package or app that owns the slot, shown in check failures. */
151
+ owner: string;
152
+ /**
153
+ * Lists this slot contributes to inheritance: a child inherits its parent's
154
+ * entries and overrides per identity (child wins).
155
+ */
156
+ inheritedLists?: readonly {
157
+ field: string;
158
+ identity(entry: unknown): string;
159
+ }[];
160
+ validate?(declaration: D, context: ValidationContext): readonly DeclarationIssue[];
161
+ toRows?(declaration: D): readonly ExtensionRow[];
162
+ }
163
+ interface ValidationContext {
164
+ /** Every registered declaration, resolved. */
165
+ all: readonly ResolvedSurfaceDeclaration[];
166
+ }
167
+ type ValueProvenance = {
168
+ kind: "own";
169
+ } | {
170
+ kind: "inherited";
171
+ from: string;
172
+ } | {
173
+ kind: "baseline";
174
+ };
175
+ type ResolvedValue = DeclaredValue & {
176
+ provenance: ValueProvenance;
177
+ groupKey: string;
178
+ /** Defaults filled. */
179
+ resolvedSensitivity: Sensitivity;
180
+ };
181
+ interface ResolvedSurfaceDeclaration extends SurfaceDeclaration {
182
+ contentHash: string;
183
+ values: readonly ResolvedValue[];
184
+ groups: readonly ValueGroup[];
185
+ }
186
+
187
+ type Resolved<D extends SurfaceDeclaration> = Omit<D, "values" | "groups"> & {
188
+ values: readonly (D["values"][number] & ResolvedValue)[];
189
+ groups: readonly ValueGroup[];
190
+ contentHash: string;
191
+ };
192
+
193
+ /**
194
+ * THE ONE SYNC (CONTRACT §2.7 "One sync path", chair ruling B1).
195
+ *
196
+ * `planSurfaceSync` maps resolved declarations onto the `ui` mirror rows; every
197
+ * sync path (the SQL emitter and the admin sync service) executes this plan and
198
+ * builds no rows of its own. Governance columns (`organization_id`,
199
+ * `visibility`) are INSERT-ONLY (chair ruling N5): a conflict never rewrites them.
200
+ *
201
+ * Schema generations: the columns added by the ALC-14 migration (item_type,
202
+ * sensitivity, live slots, content_hash) are emitted only when the caller says
203
+ * that migration is live (`schema`), so no emitter ever writes a column — or
204
+ * upserts against a key — the database does not have yet.
205
+ */
206
+
207
+ declare const GOVERNANCE_COLUMNS: readonly ["organization_id", "visibility"];
208
+ interface SyncSchema {
209
+ /** `ui_surface_value/write_target.item_type` exists and the conflict key includes it (re-key step 1). */
210
+ itemType?: boolean;
211
+ /** Sensitivity, kind, live slot and role columns exist on `ui_surface_value`. */
212
+ valueContract?: boolean;
213
+ /** `ui_surface.content_hash` exists. */
214
+ contentHash?: boolean;
215
+ }
216
+ interface SurfaceSyncPlanOptions<D extends SurfaceDeclaration> {
217
+ organizationId: string;
218
+ syncedFrom: string;
219
+ syncedBy?: string | null;
220
+ /** The address the host derives for a declaration without `urlPattern`. */
221
+ resolveUrlPattern?: (declaration: Resolved<D>) => string | undefined;
222
+ extensions?: readonly DeclarationExtension<D>[];
223
+ schema?: SyncSchema;
224
+ }
225
+ interface SurfaceRowPlan {
226
+ name: string;
227
+ /** Written only when the row does not exist yet. */
228
+ insert: Record<string, Json>;
229
+ /** Written on every sync (code owns these columns). */
230
+ update: Record<string, Json>;
231
+ }
232
+ interface SyncTablePlan {
233
+ table: string;
234
+ /** The ON CONFLICT target (a unique index or primary key on exactly these columns). */
235
+ conflict: readonly string[];
236
+ /** Columns set on insert and never on conflict. */
237
+ insertOnly: readonly string[];
238
+ rows: Record<string, Json>[];
239
+ }
240
+ interface SurfaceSyncPlan {
241
+ surfaces: SurfaceRowPlan[];
242
+ tables: SyncTablePlan[];
243
+ }
244
+ declare function planSurfaceSync<D extends SurfaceDeclaration>(declarations: readonly Resolved<D>[], options: SurfaceSyncPlanOptions<D>): SurfaceSyncPlan;
245
+ /** Every column a table's rows carry, in first-seen order. */
246
+ declare function planColumns(table: SyncTablePlan): string[];
247
+ /** Columns a conflict rewrites: everything except the key and the insert-only set. */
248
+ declare function conflictUpdateColumns(table: SyncTablePlan): string[];
249
+ declare function sqlLiteral(value: Json | undefined): string;
250
+ /** The plan as SQL: surface inserts, per-surface updates, and one upsert per table. */
251
+ declare function renderSurfaceSyncSql(plan: SurfaceSyncPlan): string;
252
+
253
+ export { GOVERNANCE_COLUMNS, type SurfaceRowPlan, type SurfaceSyncPlan, type SurfaceSyncPlanOptions, type SyncSchema, type SyncTablePlan, conflictUpdateColumns, planColumns, planSurfaceSync, renderSurfaceSyncSql, sqlLiteral };
package/dist/checks.js ADDED
@@ -0,0 +1,205 @@
1
+ // src/declare/keys.ts
2
+ function surfaceClientOf(surfaceName) {
3
+ return surfaceName.split("/")[0] ?? "";
4
+ }
5
+
6
+ // src/checks/sync.ts
7
+ var GOVERNANCE_COLUMNS = ["organization_id", "visibility"];
8
+ function childKey(schema) {
9
+ return schema?.itemType ? ["surface_name", "item_type", "name"] : ["surface_name", "name"];
10
+ }
11
+ function planSurfaceSync(declarations, options) {
12
+ const { organizationId, syncedFrom, schema } = options;
13
+ const syncedBy = options.syncedBy ?? null;
14
+ const provenance = { synced_by: syncedBy, synced_from: syncedFrom };
15
+ const governance = { organization_id: organizationId, visibility: "public" };
16
+ const surfaces = [];
17
+ const values = [];
18
+ const targets = [];
19
+ const itemTypes = [];
20
+ const extensionTables = /* @__PURE__ */ new Map();
21
+ for (const d of declarations) {
22
+ const urlPattern = d.urlPattern?.trim() || options.resolveUrlPattern?.(d) || null;
23
+ const intro = d.intro?.trim() || null;
24
+ const parent = d.inheritsFrom ?? null;
25
+ const client = surfaceClientOf(d.surfaceName);
26
+ const owned = {
27
+ client_name: client,
28
+ execution_mode: d.executionMode,
29
+ description: d.description,
30
+ label: d.label,
31
+ value_groups: d.groups ?? [],
32
+ readiness: d.readiness,
33
+ readiness_note: d.readinessNote?.trim() || null,
34
+ ...schema?.contentHash ? { content_hash: d.contentHash } : {}
35
+ };
36
+ const claimed = {
37
+ ...d.overlayId ? { overlay_id: d.overlayId } : {},
38
+ ...urlPattern ? { url_pattern: urlPattern } : {},
39
+ ...intro ? { intro } : {},
40
+ ...parent ? { parent_surface_name: parent } : {}
41
+ };
42
+ surfaces.push({
43
+ name: d.surfaceName,
44
+ insert: {
45
+ name: d.surfaceName,
46
+ ...owned,
47
+ overlay_id: d.overlayId ?? null,
48
+ url_pattern: urlPattern,
49
+ intro,
50
+ parent_surface_name: parent
51
+ },
52
+ update: { ...owned, ...claimed }
53
+ });
54
+ for (const v of d.values) {
55
+ values.push({
56
+ ...governance,
57
+ surface_name: d.surfaceName,
58
+ ...schema?.itemType ? { item_type: "" } : {},
59
+ name: v.name,
60
+ label: v.label,
61
+ description: v.description,
62
+ value_type: v.valueType,
63
+ always_available: v.alwaysAvailable,
64
+ typical_char_count: v.typicalCharCount,
65
+ sort_order: v.sortOrder ?? 1e3,
66
+ auto_context: v.autoContext ?? true,
67
+ group_key: v.groupKey ?? v.group ?? "general",
68
+ ...schema?.valueContract ? {
69
+ exportable: v.resolvedSensitivity.exportable,
70
+ classification: v.resolvedSensitivity.classification,
71
+ included_by_default: v.resolvedSensitivity.includedByDefault,
72
+ kind_key: v.valueKind ?? null,
73
+ live_slot: v.liveSlot ?? null,
74
+ role: v.role ?? "data"
75
+ } : {},
76
+ ...provenance
77
+ });
78
+ }
79
+ for (const t of d.writeTargets ?? []) {
80
+ targets.push({
81
+ ...governance,
82
+ surface_name: d.surfaceName,
83
+ ...schema?.itemType ? { item_type: t.itemType ?? "" } : {},
84
+ name: t.name,
85
+ label: t.label,
86
+ description: t.description,
87
+ value_type: t.valueType,
88
+ mode: t.mode,
89
+ updates_value: t.updatesValue ?? null,
90
+ group_key: t.group ?? "general",
91
+ sort_order: t.sortOrder ?? 1e3,
92
+ apply_policy: t.applyPolicy ?? "manual",
93
+ kind_key: t.valueKind ?? null,
94
+ ...provenance
95
+ });
96
+ }
97
+ if (schema?.itemType) {
98
+ for (const [position, item] of (d.itemTypes ?? []).entries()) {
99
+ itemTypes.push({
100
+ ...governance,
101
+ surface_name: d.surfaceName,
102
+ name: item.name,
103
+ label: item.label,
104
+ description: item.description,
105
+ identity: [...item.identity],
106
+ levels: item.levels ? [...item.levels] : null,
107
+ entity_type: item.entityType ?? null,
108
+ sort_order: position,
109
+ ...provenance
110
+ });
111
+ for (const v of item.values) {
112
+ values.push({
113
+ ...governance,
114
+ surface_name: d.surfaceName,
115
+ item_type: item.name,
116
+ name: v.name,
117
+ label: v.label,
118
+ description: v.description,
119
+ value_type: v.valueType,
120
+ always_available: v.alwaysAvailable,
121
+ typical_char_count: v.typicalCharCount,
122
+ sort_order: v.sortOrder ?? 1e3,
123
+ auto_context: v.autoContext ?? true,
124
+ group_key: v.group ?? "general",
125
+ ...provenance
126
+ });
127
+ }
128
+ }
129
+ }
130
+ for (const extension of options.extensions ?? []) {
131
+ for (const entry of extension.toRows?.(d) ?? []) {
132
+ const plan = extensionTables.get(entry.table) ?? {
133
+ table: entry.table,
134
+ conflict: entry.conflict,
135
+ insertOnly: GOVERNANCE_COLUMNS,
136
+ rows: []
137
+ };
138
+ plan.rows.push({ ...governance, ...entry.row, ...provenance });
139
+ extensionTables.set(entry.table, plan);
140
+ }
141
+ }
142
+ }
143
+ const tables = [
144
+ { table: "ui.ui_surface_value", conflict: childKey(schema), insertOnly: GOVERNANCE_COLUMNS, rows: values },
145
+ { table: "ui.ui_surface_write_target", conflict: childKey(schema), insertOnly: GOVERNANCE_COLUMNS, rows: targets },
146
+ ...schema?.itemType ? [{ table: "ui.ui_surface_item_type", conflict: ["surface_name", "name"], insertOnly: GOVERNANCE_COLUMNS, rows: itemTypes }] : [],
147
+ ...extensionTables.values()
148
+ ];
149
+ return { surfaces, tables: tables.filter((table) => table.rows.length > 0) };
150
+ }
151
+ function planColumns(table) {
152
+ const columns = [];
153
+ for (const row of table.rows) for (const key of Object.keys(row)) if (!columns.includes(key)) columns.push(key);
154
+ return columns;
155
+ }
156
+ function conflictUpdateColumns(table) {
157
+ return planColumns(table).filter((c) => !table.conflict.includes(c) && !table.insertOnly.includes(c));
158
+ }
159
+ function sqlLiteral(value) {
160
+ if (value === null || value === void 0) return "NULL";
161
+ if (typeof value === "boolean" || typeof value === "number") return String(value);
162
+ if (typeof value === "string") return `'${value.replace(/'/g, "''")}'`;
163
+ if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
164
+ return `ARRAY[${value.map((entry) => sqlLiteral(entry)).join(", ")}]::text[]`;
165
+ }
166
+ return `'${JSON.stringify(value).replace(/'/g, "''")}'::jsonb`;
167
+ }
168
+ function renderSurfaceSyncSql(plan) {
169
+ const out = [];
170
+ if (plan.surfaces.length > 0) {
171
+ const insertColumns = Object.keys(plan.surfaces[0].insert);
172
+ out.push("-- Register declared surfaces. Existing ui_client rows are required; this sync never creates clients.");
173
+ out.push(
174
+ `INSERT INTO ui.ui_surface (${insertColumns.join(", ")}) VALUES
175
+ ${plan.surfaces.map((s) => `(${insertColumns.map((c) => sqlLiteral(s.insert[c])).join(", ")})`).join(",\n")}
176
+ ON CONFLICT (name) DO NOTHING;`
177
+ );
178
+ out.push("", "-- Mirror declared surface metadata (code-owned columns only)");
179
+ out.push(
180
+ plan.surfaces.map(
181
+ (s) => `UPDATE ui.ui_surface SET ${Object.entries(s.update).map(([c, v]) => `${c} = ${sqlLiteral(v)}`).join(", ")}, updated_at = now() WHERE name = ${sqlLiteral(s.name)};`
182
+ ).join("\n")
183
+ );
184
+ }
185
+ for (const table of plan.tables) {
186
+ const columns = planColumns(table);
187
+ const sets = conflictUpdateColumns(table).map((c) => `${c} = EXCLUDED.${c}`);
188
+ out.push("", `-- Upsert ${table.table} (governance columns insert-only)`);
189
+ out.push(
190
+ `INSERT INTO ${table.table} (${columns.join(", ")}) VALUES
191
+ ${table.rows.map((row) => `(${columns.map((c) => sqlLiteral(row[c])).join(", ")})`).join(",\n")}
192
+ ON CONFLICT (${table.conflict.join(", ")}) DO UPDATE SET ${[...sets, "updated_at = now()"].join(", ")};`
193
+ );
194
+ }
195
+ return out.join("\n");
196
+ }
197
+ export {
198
+ GOVERNANCE_COLUMNS,
199
+ conflictUpdateColumns,
200
+ planColumns,
201
+ planSurfaceSync,
202
+ renderSurfaceSyncSql,
203
+ sqlLiteral
204
+ };
205
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/declare/keys.ts","../src/checks/sync.ts"],"sourcesContent":["import type { QualifiedValueKey } from \"./types\";\n\n/** Every value, write-target, item-type and group name (the DB CHECK rule). */\nexport const DECLARATION_NAME_RE = /^[a-z][a-z0-9_]*$/;\n\n/** \"<client>/<local>\" — the surface naming convention. */\nexport const SURFACE_NAME_RE = /^[a-z][a-z0-9-]*\\/[a-z0-9-/]+$/;\n\n/** A bare screen value (`status`) or an item value (`table_row.status`). */\nexport const QUALIFIED_VALUE_KEY_RE = /^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)?$/;\n\nexport interface ParsedValueKey {\n /** \"\" for a screen-level value. */\n itemType: string;\n name: string;\n}\n\n/** Split a qualified key; null when it is not a valid bare or qualified key. */\nexport function parseQualifiedValueKey(key: string): ParsedValueKey | null {\n if (!QUALIFIED_VALUE_KEY_RE.test(key)) return null;\n const dot = key.indexOf(\".\");\n if (dot === -1) return { itemType: \"\", name: key };\n return { itemType: key.slice(0, dot), name: key.slice(dot + 1) };\n}\n\nexport function qualifyValueKey(itemType: string, name: string): QualifiedValueKey {\n return itemType ? `${itemType}.${name}` : name;\n}\n\n/** The client that owns a surface: its name prefix. */\nexport function surfaceClientOf(surfaceName: string): string {\n return surfaceName.split(\"/\")[0] ?? \"\";\n}\n","/**\n * THE ONE SYNC (CONTRACT §2.7 \"One sync path\", chair ruling B1).\n *\n * `planSurfaceSync` maps resolved declarations onto the `ui` mirror rows; every\n * sync path (the SQL emitter and the admin sync service) executes this plan and\n * builds no rows of its own. Governance columns (`organization_id`,\n * `visibility`) are INSERT-ONLY (chair ruling N5): a conflict never rewrites them.\n *\n * Schema generations: the columns added by the ALC-14 migration (item_type,\n * sensitivity, live slots, content_hash) are emitted only when the caller says\n * that migration is live (`schema`), so no emitter ever writes a column — or\n * upserts against a key — the database does not have yet.\n */\nimport type { Resolved } from \"../declare/registry\";\nimport { surfaceClientOf } from \"../declare/keys\";\nimport type { DeclarationExtension, Json, SurfaceDeclaration } from \"../declare/types\";\n\nexport const GOVERNANCE_COLUMNS = [\"organization_id\", \"visibility\"] as const;\n\nexport interface SyncSchema {\n /** `ui_surface_value/write_target.item_type` exists and the conflict key includes it (re-key step 1). */\n itemType?: boolean;\n /** Sensitivity, kind, live slot and role columns exist on `ui_surface_value`. */\n valueContract?: boolean;\n /** `ui_surface.content_hash` exists. */\n contentHash?: boolean;\n}\n\nexport interface SurfaceSyncPlanOptions<D extends SurfaceDeclaration> {\n organizationId: string;\n syncedFrom: string;\n syncedBy?: string | null;\n /** The address the host derives for a declaration without `urlPattern`. */\n resolveUrlPattern?: (declaration: Resolved<D>) => string | undefined;\n extensions?: readonly DeclarationExtension<D>[];\n schema?: SyncSchema;\n}\n\nexport interface SurfaceRowPlan {\n name: string;\n /** Written only when the row does not exist yet. */\n insert: Record<string, Json>;\n /** Written on every sync (code owns these columns). */\n update: Record<string, Json>;\n}\n\nexport interface SyncTablePlan {\n table: string;\n /** The ON CONFLICT target (a unique index or primary key on exactly these columns). */\n conflict: readonly string[];\n /** Columns set on insert and never on conflict. */\n insertOnly: readonly string[];\n rows: Record<string, Json>[];\n}\n\nexport interface SurfaceSyncPlan {\n surfaces: SurfaceRowPlan[];\n tables: SyncTablePlan[];\n}\n\nfunction childKey(schema: SyncSchema | undefined): readonly string[] {\n return schema?.itemType ? [\"surface_name\", \"item_type\", \"name\"] : [\"surface_name\", \"name\"];\n}\n\nexport function planSurfaceSync<D extends SurfaceDeclaration>(\n declarations: readonly Resolved<D>[],\n options: SurfaceSyncPlanOptions<D>,\n): SurfaceSyncPlan {\n const { organizationId, syncedFrom, schema } = options;\n const syncedBy = options.syncedBy ?? null;\n const provenance = { synced_by: syncedBy, synced_from: syncedFrom };\n const governance = { organization_id: organizationId, visibility: \"public\" };\n const surfaces: SurfaceRowPlan[] = [];\n const values: Record<string, Json>[] = [];\n const targets: Record<string, Json>[] = [];\n const itemTypes: Record<string, Json>[] = [];\n const extensionTables = new Map<string, SyncTablePlan>();\n\n for (const d of declarations) {\n const urlPattern = d.urlPattern?.trim() || options.resolveUrlPattern?.(d) || null;\n const intro = d.intro?.trim() || null;\n const parent = d.inheritsFrom ?? null;\n const client = surfaceClientOf(d.surfaceName);\n const owned: Record<string, Json> = {\n client_name: client,\n execution_mode: d.executionMode,\n description: d.description,\n label: d.label,\n value_groups: (d.groups ?? []) as unknown as Json,\n readiness: d.readiness,\n readiness_note: d.readinessNote?.trim() || null,\n ...(schema?.contentHash ? { content_hash: d.contentHash } : {}),\n };\n // A declaration that does not claim these never clears a DB-authored value.\n const claimed: Record<string, Json> = {\n ...(d.overlayId ? { overlay_id: d.overlayId } : {}),\n ...(urlPattern ? { url_pattern: urlPattern } : {}),\n ...(intro ? { intro } : {}),\n ...(parent ? { parent_surface_name: parent } : {}),\n };\n surfaces.push({\n name: d.surfaceName,\n insert: {\n name: d.surfaceName,\n ...owned,\n overlay_id: d.overlayId ?? null,\n url_pattern: urlPattern,\n intro,\n parent_surface_name: parent,\n },\n update: { ...owned, ...claimed },\n });\n\n for (const v of d.values) {\n values.push({\n ...governance,\n surface_name: d.surfaceName,\n ...(schema?.itemType ? { item_type: \"\" } : {}),\n name: v.name,\n label: v.label,\n description: v.description,\n value_type: v.valueType,\n always_available: v.alwaysAvailable,\n typical_char_count: v.typicalCharCount,\n sort_order: v.sortOrder ?? 1000,\n auto_context: v.autoContext ?? true,\n group_key: v.groupKey ?? v.group ?? \"general\",\n ...(schema?.valueContract\n ? {\n exportable: v.resolvedSensitivity.exportable,\n classification: v.resolvedSensitivity.classification,\n included_by_default: v.resolvedSensitivity.includedByDefault,\n kind_key: v.valueKind ?? null,\n live_slot: v.liveSlot ?? null,\n role: v.role ?? \"data\",\n }\n : {}),\n ...provenance,\n });\n }\n\n for (const t of d.writeTargets ?? []) {\n targets.push({\n ...governance,\n surface_name: d.surfaceName,\n ...(schema?.itemType ? { item_type: t.itemType ?? \"\" } : {}),\n name: t.name,\n label: t.label,\n description: t.description,\n value_type: t.valueType,\n mode: t.mode,\n updates_value: t.updatesValue ?? null,\n group_key: t.group ?? \"general\",\n sort_order: t.sortOrder ?? 1000,\n apply_policy: t.applyPolicy ?? \"manual\",\n kind_key: t.valueKind ?? null,\n ...provenance,\n });\n }\n\n if (schema?.itemType) {\n for (const [position, item] of (d.itemTypes ?? []).entries()) {\n itemTypes.push({\n ...governance,\n surface_name: d.surfaceName,\n name: item.name,\n label: item.label,\n description: item.description,\n identity: [...item.identity],\n levels: item.levels ? [...item.levels] : null,\n entity_type: item.entityType ?? null,\n sort_order: position,\n ...provenance,\n });\n for (const v of item.values) {\n values.push({\n ...governance,\n surface_name: d.surfaceName,\n item_type: item.name,\n name: v.name,\n label: v.label,\n description: v.description,\n value_type: v.valueType,\n always_available: v.alwaysAvailable,\n typical_char_count: v.typicalCharCount,\n sort_order: v.sortOrder ?? 1000,\n auto_context: v.autoContext ?? true,\n group_key: v.group ?? \"general\",\n ...provenance,\n });\n }\n }\n }\n\n for (const extension of options.extensions ?? []) {\n for (const entry of extension.toRows?.(d as unknown as D) ?? []) {\n const plan = extensionTables.get(entry.table) ?? {\n table: entry.table,\n conflict: entry.conflict,\n insertOnly: GOVERNANCE_COLUMNS,\n rows: [],\n };\n plan.rows.push({ ...governance, ...entry.row, ...provenance });\n extensionTables.set(entry.table, plan);\n }\n }\n }\n\n const tables: SyncTablePlan[] = [\n { table: \"ui.ui_surface_value\", conflict: childKey(schema), insertOnly: GOVERNANCE_COLUMNS, rows: values },\n { table: \"ui.ui_surface_write_target\", conflict: childKey(schema), insertOnly: GOVERNANCE_COLUMNS, rows: targets },\n ...(schema?.itemType\n ? [{ table: \"ui.ui_surface_item_type\", conflict: [\"surface_name\", \"name\"], insertOnly: GOVERNANCE_COLUMNS, rows: itemTypes }]\n : []),\n ...extensionTables.values(),\n ];\n return { surfaces, tables: tables.filter((table) => table.rows.length > 0) };\n}\n\n/** Every column a table's rows carry, in first-seen order. */\nexport function planColumns(table: SyncTablePlan): string[] {\n const columns: string[] = [];\n for (const row of table.rows) for (const key of Object.keys(row)) if (!columns.includes(key)) columns.push(key);\n return columns;\n}\n\n/** Columns a conflict rewrites: everything except the key and the insert-only set. */\nexport function conflictUpdateColumns(table: SyncTablePlan): string[] {\n return planColumns(table).filter((c) => !table.conflict.includes(c) && !table.insertOnly.includes(c));\n}\n\nexport function sqlLiteral(value: Json | undefined): string {\n if (value === null || value === undefined) return \"NULL\";\n if (typeof value === \"boolean\" || typeof value === \"number\") return String(value);\n if (typeof value === \"string\") return `'${value.replace(/'/g, \"''\")}'`;\n if (Array.isArray(value) && value.every((entry) => typeof entry === \"string\")) {\n return `ARRAY[${value.map((entry) => sqlLiteral(entry)).join(\", \")}]::text[]`;\n }\n return `'${JSON.stringify(value).replace(/'/g, \"''\")}'::jsonb`;\n}\n\n/** The plan as SQL: surface inserts, per-surface updates, and one upsert per table. */\nexport function renderSurfaceSyncSql(plan: SurfaceSyncPlan): string {\n const out: string[] = [];\n if (plan.surfaces.length > 0) {\n const insertColumns = Object.keys(plan.surfaces[0]!.insert);\n out.push(\"-- Register declared surfaces. Existing ui_client rows are required; this sync never creates clients.\");\n out.push(\n `INSERT INTO ui.ui_surface (${insertColumns.join(\", \")}) VALUES\\n${plan.surfaces\n .map((s) => `(${insertColumns.map((c) => sqlLiteral(s.insert[c])).join(\", \")})`)\n .join(\",\\n\")}\\nON CONFLICT (name) DO NOTHING;`,\n );\n out.push(\"\", \"-- Mirror declared surface metadata (code-owned columns only)\");\n out.push(\n plan.surfaces\n .map(\n (s) =>\n `UPDATE ui.ui_surface SET ${Object.entries(s.update)\n .map(([c, v]) => `${c} = ${sqlLiteral(v)}`)\n .join(\", \")}, updated_at = now() WHERE name = ${sqlLiteral(s.name)};`,\n )\n .join(\"\\n\"),\n );\n }\n for (const table of plan.tables) {\n const columns = planColumns(table);\n const sets = conflictUpdateColumns(table).map((c) => `${c} = EXCLUDED.${c}`);\n out.push(\"\", `-- Upsert ${table.table} (governance columns insert-only)`);\n out.push(\n `INSERT INTO ${table.table} (${columns.join(\", \")}) VALUES\\n${table.rows\n .map((row) => `(${columns.map((c) => sqlLiteral(row[c])).join(\", \")})`)\n .join(\",\\n\")}\\nON CONFLICT (${table.conflict.join(\", \")}) DO UPDATE SET ${[...sets, \"updated_at = now()\"].join(\", \")};`,\n );\n }\n return out.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}