@noy-db/hub 0.7.0-pre.11 → 0.7.0-pre.12

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 (62) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/as/index.js +3 -2
  3. package/dist/as/index.js.map +1 -1
  4. package/dist/{backup-VASRVLDZ.js → backup-R6A4VZRX.js} +4 -4
  5. package/dist/cargo/index.js +2 -2
  6. package/dist/{chunk-UB5CRTFF.js → chunk-7BE7ZDVQ.js} +379 -1
  7. package/dist/chunk-7BE7ZDVQ.js.map +1 -0
  8. package/dist/{chunk-ESVREVWN.js → chunk-7NTBVFGD.js} +2 -2
  9. package/dist/{chunk-T6PSWLAR.js → chunk-BUE2D7PX.js} +25 -2
  10. package/dist/chunk-BUE2D7PX.js.map +1 -0
  11. package/dist/{chunk-S5AMPTNE.js → chunk-E5OHKSBD.js} +2 -2
  12. package/dist/{chunk-OEA5NVSU.js → chunk-H7XCIM3Z.js} +59 -6
  13. package/dist/chunk-H7XCIM3Z.js.map +1 -0
  14. package/dist/chunk-KL7AS3HV.js +84 -0
  15. package/dist/chunk-KL7AS3HV.js.map +1 -0
  16. package/dist/chunk-RXLYKVTF.js +17 -0
  17. package/dist/chunk-RXLYKVTF.js.map +1 -0
  18. package/dist/chunk-TFMM5DBB.js +20 -0
  19. package/dist/chunk-TFMM5DBB.js.map +1 -0
  20. package/dist/chunk-TPHAGLUH.js +30 -0
  21. package/dist/chunk-TPHAGLUH.js.map +1 -0
  22. package/dist/{chunk-A5WZEPXJ.js → chunk-VGICZPVE.js} +5 -3
  23. package/dist/{chunk-A5WZEPXJ.js.map → chunk-VGICZPVE.js.map} +1 -1
  24. package/dist/{chunk-KHKKD62N.js → chunk-VYYM3MEN.js} +2 -16
  25. package/dist/chunk-VYYM3MEN.js.map +1 -0
  26. package/dist/{chunk-I6DGKPTY.js → chunk-XMF3GKUR.js} +60 -412
  27. package/dist/chunk-XMF3GKUR.js.map +1 -0
  28. package/dist/derivations/index.js +17 -6
  29. package/dist/dispatch-XEBHUGBV.js +250 -0
  30. package/dist/dispatch-XEBHUGBV.js.map +1 -0
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +19 -19
  33. package/dist/introspection/index.js +3 -2
  34. package/dist/kernel/collection.d.ts +11 -6
  35. package/dist/kernel/match-pairs.d.ts +30 -0
  36. package/dist/{noydb-QJFZAH34.js → noydb-JJXPICIC.js} +9 -7
  37. package/dist/pod/index.js +9 -7
  38. package/dist/registry-BZQIVNNO.js +29 -0
  39. package/dist/{walk-G3CZIL4L.js → walk-P3E6QSNC.js} +4 -3
  40. package/dist/with-formula/derivations/dispatch.d.ts +16 -1
  41. package/dist/with-formula/derivations/registry.d.ts +15 -0
  42. package/dist/with-formula/derivations/trigger-match.d.ts +52 -0
  43. package/dist/with-formula/derivations/types.d.ts +23 -0
  44. package/dist/with-shape/introspection/behaviors.d.ts +7 -0
  45. package/dist/with-shape/introspection/describe.d.ts +10 -0
  46. package/package.json +3 -3
  47. package/dist/chunk-I6DGKPTY.js.map +0 -1
  48. package/dist/chunk-KHKKD62N.js.map +0 -1
  49. package/dist/chunk-OEA5NVSU.js.map +0 -1
  50. package/dist/chunk-T6PSWLAR.js.map +0 -1
  51. package/dist/chunk-UB5CRTFF.js.map +0 -1
  52. package/dist/chunk-YPMW26SL.js +0 -81
  53. package/dist/chunk-YPMW26SL.js.map +0 -1
  54. package/dist/dispatch-IM52HDXT.js +0 -194
  55. package/dist/dispatch-IM52HDXT.js.map +0 -1
  56. package/dist/registry-FZXPQRAB.js +0 -18
  57. /package/dist/{backup-VASRVLDZ.js.map → backup-R6A4VZRX.js.map} +0 -0
  58. /package/dist/{chunk-ESVREVWN.js.map → chunk-7NTBVFGD.js.map} +0 -0
  59. /package/dist/{chunk-S5AMPTNE.js.map → chunk-E5OHKSBD.js.map} +0 -0
  60. /package/dist/{noydb-QJFZAH34.js.map → noydb-JJXPICIC.js.map} +0 -0
  61. /package/dist/{registry-FZXPQRAB.js.map → registry-BZQIVNNO.js.map} +0 -0
  62. /package/dist/{walk-G3CZIL4L.js.map → walk-P3E6QSNC.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog — hub
2
2
 
3
+ ## 0.7.0-pre.12
4
+
5
+ ### Minor Changes
6
+
7
+ - `withDerivation`'s `triggerBy` accepts a multi-field `match` form (#1249):
8
+ `{ collection, match: [{ from, to }] }` fans a write out to every source
9
+ record where ALL pairs satisfy `String(source[to]) === String(written[from])`.
10
+ `from: 'id'` reads the written record's id, making the existing `on` form the
11
+ single-pair special case (it is unchanged and stays supported). This makes
12
+ shared-key ("reverse") relationships and composite keys like
13
+ `(clientId, cycle)` expressible without denormalising a synthetic key.
14
+
15
+ Also, for BOTH forms:
16
+
17
+ - a LOCAL UPDATE that changes any matched field fans out on old-match ∪
18
+ new-match, so records addressed by the previous value no longer go
19
+ silently stale; a sync-applied wave write or a tiers restore, which don't
20
+ thread the prior record, fan out on the new tuple only;
21
+ - a parent DELETE now fans out using the tombstoned record's values —
22
+ previously deletes fired no triggers at all, leaving matched sources
23
+ stale. The fan-out runs after the delete commits, so cap/strict errors
24
+ can surface from `delete()` (same as the existing rollup-on-delete
25
+ precedent);
26
+ - match fields are validated against the collections' enumerable field sets at
27
+ `vault.collection()` (the #1253 pattern): a provable typo throws instead of
28
+ silently matching nothing forever; TS-generic collections stay unguarded by
29
+ design.
30
+
31
+ `maxFanout` caps the unioned matched set per written event.
32
+
33
+ ### Patch Changes
34
+
35
+ - `schemaFieldKeys` now unwraps Zod 3 `ZodEffects` created by object-level
36
+ `.refine()`/`.superRefine()` (reported by the pilot): the wrapper hides
37
+ `.shape`, so both field-typo guards — #1253's `fieldMeta` validation and
38
+ #1249's `triggerBy` match validation — were silent for exactly the schemas
39
+ most worth guarding. Unwrapping follows refinement effects only: a
40
+ `.transform()`/`.preprocess()` changes the output shape, so those stay
41
+ silent by design. Zod 4 keeps `.shape` through `.refine()` and is unaffected.
42
+
3
43
  ## 0.7.0-pre.11
4
44
 
5
45
  ### Patch Changes
package/dist/as/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import "../chunk-UBSFPGVJ.js";
2
- import "../chunk-A5WZEPXJ.js";
2
+ import "../chunk-VGICZPVE.js";
3
3
  import {
4
4
  applyListProjection
5
5
  } from "../chunk-VG2ZSFYD.js";
6
- import "../chunk-KHKKD62N.js";
6
+ import "../chunk-RXLYKVTF.js";
7
+ import "../chunk-VYYM3MEN.js";
7
8
  import "../chunk-URHRIAV6.js";
8
9
  import "../chunk-Y2NBBT6K.js";
9
10
  import "../chunk-WWNYSV7S.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/port/as/active.ts"],"sourcesContent":["/**\n * `withFormats()` — the service that consumes the `as-*` port.\n *\n * Everything with a correctness or security consequence lives here rather\n * than in each format: the capability gate, the vault read, and redaction.\n * A {@link NoydbFormat} receives records and returns bytes.\n *\n * That is the whole point of the inversion (ADR 0004). The conformance kit it\n * replaces could only check implementors who ran it; a format that cannot\n * reach a vault is gated whether its author cooperated or not.\n *\n * @module\n */\nimport type { NoydbFormat, ImportPlan } from './types.js'\nimport type {\n FormatsStrategy,\n FormatsContext,\n FormatExportOptions,\n FormatImportOptions,\n} from '../with/formats-strategy.js'\nimport type { ExportChunk } from '../../kernel/types.js'\nimport type { Vault } from '../../kernel/vault.js'\nimport type { ImportPolicy } from './types.js'\nimport { applyListProjection } from '../../with-shape/introspection/index.js'\nimport type { CollectionDescription, ListProjectionOptions } from '../../with-shape/introspection/index.js'\nimport { ValidationError } from '../../kernel/errors.js'\n\n/** Apply the redaction projection hub owns, so a format never asks for it. */\nfunction redactChunk(\n chunk: ExportChunk,\n ctx: FormatsContext,\n redact: FormatExportOptions['redact'],\n): ExportChunk {\n if (redact === undefined) return chunk\n const described = ctx.describe(chunk.collection) as CollectionDescription | undefined\n if (!described) {\n throw new Error(\n `vault.export: redaction was requested but '${chunk.collection}' has no description to redact against. ` +\n 'Open the collection with its classifiedFields / fieldMeta options first.',\n )\n }\n const opts: ListProjectionOptions | undefined =\n redact === true ? undefined : { sensitivity: redact.sensitivity as never }\n const records = chunk.records.map((r) =>\n applyListProjection(described, r as Record<string, unknown>, opts),\n )\n return { ...chunk, records: records as typeof chunk.records }\n}\n\n/**\n * Derive the narrow, format-facing capability from the vault.\n *\n * Lives here rather than on `Vault` because `kernel-surface` said so, and it\n * was right: this is service capability, not spine. The spine keeps two\n * delegating methods and nothing else.\n */\nfunction contextFor(vault: Vault): FormatsContext {\n return {\n // The empty-id guards are NOT defensive padding. The\n // plaintext tier is gated per-format, so a missing id means the calling\n // binding is broken — and without this it would reach the gate, miss the\n // allowlist, and surface as `ExportCapabilityError`: \"the owner did not\n // grant this\", which sends the reader to the keyring instead of to the\n // format. A loud TypeError names the real fault.\n assertCanExport: (tier, format) => {\n if (tier === 'bundle') return vault.assertCanExport('bundle')\n if (!format) throw new TypeError(\"assertCanExport('plaintext') requires a non-empty format id\")\n vault.assertCanExport('plaintext', format)\n },\n assertCanImport: (tier, format) => {\n if (tier === 'bundle') return vault.assertCanImport('bundle')\n if (!format) throw new TypeError(\"assertCanImport('plaintext') requires a non-empty format id\")\n vault.assertCanImport('plaintext', format)\n },\n // ⚠️ Filtered HERE, not passed to exportStream — `ExportStreamOptions`\n // has no `collections` field, so the option I first passed was silently\n // dropped and hub read every collection. Caught by an as-json test\n // expecting ['invoices'] and getting ['invoices','payments'].\n //\n // Post-read filtering is correct but not free: hub still DECRYPTS the\n // collections the caller excluded. Pushing the filter down into\n // exportStream is the real fix and is a hub change beyond this port.\n chunks: async function* (collections) {\n const wanted = collections ? new Set(collections) : null\n for await (const chunk of vault.exportStream({ granularity: 'collection' })) {\n if (wanted && !wanted.has(chunk.collection)) continue\n yield chunk\n }\n },\n // NOT swallowed. A caller who asked for redaction and silently got\n // unredacted output is the exact failure this port exists to remove — a\n // security duty that fails quiet. If the description cannot be read, the\n // export fails instead.\n describe: (collection) => vault.collection(collection).describe() as never,\n plan: async (records, policy: ImportPolicy, formatId: string, idKey?: string) => {\n const { diffVault } = await import('../../with-cargo/vault-diff.js')\n const plan = await diffVault(vault, records as never, idKey ? { idKey } : {})\n return {\n plan,\n policy,\n // The apply walk every as-* package had independently: added always,\n // modified unless insert-only, deleted only on replace — in one\n // transaction so a partial failure rolls back. Owned once, here.\n apply: async () => {\n await vault.noydb.transaction((tx) => {\n const txVault = tx.vault(vault.name)\n const reason = `import:${formatId}`\n for (const e of plan.added) {\n txVault.collection(e.collection).put(e.id, e.record as never, { reason })\n }\n if (policy !== 'insert-only') {\n for (const e of plan.modified) {\n txVault.collection(e.collection).put(e.id, e.record as never, { reason })\n }\n }\n if (policy === 'replace') {\n for (const e of plan.deleted) txVault.collection(e.collection).delete(e.id)\n }\n })\n },\n }\n },\n }\n}\n\n/**\n * Enable `vault.export()` / `vault.import()`.\n *\n * ```ts\n * const db = await createNoydb({ store, user, secret, formatsStrategy: withFormats() })\n * const csv = await vault.export(asCsv(), { collections: ['invoices'] })\n * ```\n */\nexport function withFormats(): FormatsStrategy {\n return {\n enabled: true,\n\n async exportWith<Out extends string | Uint8Array>(\n vault: Vault,\n format: NoydbFormat<Out>,\n options: FormatExportOptions = {},\n ): Promise<Out> {\n const ctx = contextFor(vault)\n // FIRST, and before a single record is read. The format is not in scope\n // yet and could not reach the vault if it were.\n if (format.tier === 'bundle') ctx.assertCanExport('bundle')\n else ctx.assertCanExport('plaintext', format.id)\n\n const chunks: ExportChunk[] = []\n for await (const chunk of ctx.chunks(options.collections)) {\n chunks.push(redactChunk(chunk, ctx, options.redact))\n }\n return format.encode(chunks)\n },\n\n async importWith<Out extends string | Uint8Array>(\n vault: Vault,\n format: NoydbFormat<Out>,\n input: Out,\n options: FormatImportOptions = {},\n ): Promise<ImportPlan> {\n const ctx = contextFor(vault)\n // Mirrors the export side: the import gate is two-tier as well, and a\n // bundle format importing under the plaintext tier would be a silent\n // downgrade in the other direction.\n if (format.tier === 'bundle') ctx.assertCanImport('bundle')\n else ctx.assertCanImport('plaintext', format.id)\n if (!format.decode) {\n throw new ValidationError(\n `vault.import: the '${format.id}' format is export-only — it declares no decode(). ` +\n 'Pick a format that round-trips, or write the records directly.',\n )\n }\n const decoded = await format.decode(input)\n const candidate: Record<string, readonly unknown[]> = {}\n for (const chunk of decoded) {\n const target = chunk.collection || options.collection\n if (!target) {\n throw new ValidationError(\n `vault.import: the '${format.id}' input names no collection and none was supplied. ` +\n 'Pass { collection } for formats whose payload is collection-less.',\n )\n }\n candidate[target] = [...(candidate[target] ?? []), ...chunk.records]\n }\n return ctx.plan(candidate, options.policy ?? 'merge', format.id, options.idKey)\n },\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/port/as/active.ts"],"sourcesContent":["/**\n * `withFormats()` — the service that consumes the `as-*` port.\n *\n * Everything with a correctness or security consequence lives here rather\n * than in each format: the capability gate, the vault read, and redaction.\n * A {@link NoydbFormat} receives records and returns bytes.\n *\n * That is the whole point of the inversion (ADR 0004). The conformance kit it\n * replaces could only check implementors who ran it; a format that cannot\n * reach a vault is gated whether its author cooperated or not.\n *\n * @module\n */\nimport type { NoydbFormat, ImportPlan } from './types.js'\nimport type {\n FormatsStrategy,\n FormatsContext,\n FormatExportOptions,\n FormatImportOptions,\n} from '../with/formats-strategy.js'\nimport type { ExportChunk } from '../../kernel/types.js'\nimport type { Vault } from '../../kernel/vault.js'\nimport type { ImportPolicy } from './types.js'\nimport { applyListProjection } from '../../with-shape/introspection/index.js'\nimport type { CollectionDescription, ListProjectionOptions } from '../../with-shape/introspection/index.js'\nimport { ValidationError } from '../../kernel/errors.js'\n\n/** Apply the redaction projection hub owns, so a format never asks for it. */\nfunction redactChunk(\n chunk: ExportChunk,\n ctx: FormatsContext,\n redact: FormatExportOptions['redact'],\n): ExportChunk {\n if (redact === undefined) return chunk\n const described = ctx.describe(chunk.collection) as CollectionDescription | undefined\n if (!described) {\n throw new Error(\n `vault.export: redaction was requested but '${chunk.collection}' has no description to redact against. ` +\n 'Open the collection with its classifiedFields / fieldMeta options first.',\n )\n }\n const opts: ListProjectionOptions | undefined =\n redact === true ? undefined : { sensitivity: redact.sensitivity as never }\n const records = chunk.records.map((r) =>\n applyListProjection(described, r as Record<string, unknown>, opts),\n )\n return { ...chunk, records: records as typeof chunk.records }\n}\n\n/**\n * Derive the narrow, format-facing capability from the vault.\n *\n * Lives here rather than on `Vault` because `kernel-surface` said so, and it\n * was right: this is service capability, not spine. The spine keeps two\n * delegating methods and nothing else.\n */\nfunction contextFor(vault: Vault): FormatsContext {\n return {\n // The empty-id guards are NOT defensive padding. The\n // plaintext tier is gated per-format, so a missing id means the calling\n // binding is broken — and without this it would reach the gate, miss the\n // allowlist, and surface as `ExportCapabilityError`: \"the owner did not\n // grant this\", which sends the reader to the keyring instead of to the\n // format. A loud TypeError names the real fault.\n assertCanExport: (tier, format) => {\n if (tier === 'bundle') return vault.assertCanExport('bundle')\n if (!format) throw new TypeError(\"assertCanExport('plaintext') requires a non-empty format id\")\n vault.assertCanExport('plaintext', format)\n },\n assertCanImport: (tier, format) => {\n if (tier === 'bundle') return vault.assertCanImport('bundle')\n if (!format) throw new TypeError(\"assertCanImport('plaintext') requires a non-empty format id\")\n vault.assertCanImport('plaintext', format)\n },\n // ⚠️ Filtered HERE, not passed to exportStream — `ExportStreamOptions`\n // has no `collections` field, so the option I first passed was silently\n // dropped and hub read every collection. Caught by an as-json test\n // expecting ['invoices'] and getting ['invoices','payments'].\n //\n // Post-read filtering is correct but not free: hub still DECRYPTS the\n // collections the caller excluded. Pushing the filter down into\n // exportStream is the real fix and is a hub change beyond this port.\n chunks: async function* (collections) {\n const wanted = collections ? new Set(collections) : null\n for await (const chunk of vault.exportStream({ granularity: 'collection' })) {\n if (wanted && !wanted.has(chunk.collection)) continue\n yield chunk\n }\n },\n // NOT swallowed. A caller who asked for redaction and silently got\n // unredacted output is the exact failure this port exists to remove — a\n // security duty that fails quiet. If the description cannot be read, the\n // export fails instead.\n describe: (collection) => vault.collection(collection).describe() as never,\n plan: async (records, policy: ImportPolicy, formatId: string, idKey?: string) => {\n const { diffVault } = await import('../../with-cargo/vault-diff.js')\n const plan = await diffVault(vault, records as never, idKey ? { idKey } : {})\n return {\n plan,\n policy,\n // The apply walk every as-* package had independently: added always,\n // modified unless insert-only, deleted only on replace — in one\n // transaction so a partial failure rolls back. Owned once, here.\n apply: async () => {\n await vault.noydb.transaction((tx) => {\n const txVault = tx.vault(vault.name)\n const reason = `import:${formatId}`\n for (const e of plan.added) {\n txVault.collection(e.collection).put(e.id, e.record as never, { reason })\n }\n if (policy !== 'insert-only') {\n for (const e of plan.modified) {\n txVault.collection(e.collection).put(e.id, e.record as never, { reason })\n }\n }\n if (policy === 'replace') {\n for (const e of plan.deleted) txVault.collection(e.collection).delete(e.id)\n }\n })\n },\n }\n },\n }\n}\n\n/**\n * Enable `vault.export()` / `vault.import()`.\n *\n * ```ts\n * const db = await createNoydb({ store, user, secret, formatsStrategy: withFormats() })\n * const csv = await vault.export(asCsv(), { collections: ['invoices'] })\n * ```\n */\nexport function withFormats(): FormatsStrategy {\n return {\n enabled: true,\n\n async exportWith<Out extends string | Uint8Array>(\n vault: Vault,\n format: NoydbFormat<Out>,\n options: FormatExportOptions = {},\n ): Promise<Out> {\n const ctx = contextFor(vault)\n // FIRST, and before a single record is read. The format is not in scope\n // yet and could not reach the vault if it were.\n if (format.tier === 'bundle') ctx.assertCanExport('bundle')\n else ctx.assertCanExport('plaintext', format.id)\n\n const chunks: ExportChunk[] = []\n for await (const chunk of ctx.chunks(options.collections)) {\n chunks.push(redactChunk(chunk, ctx, options.redact))\n }\n return format.encode(chunks)\n },\n\n async importWith<Out extends string | Uint8Array>(\n vault: Vault,\n format: NoydbFormat<Out>,\n input: Out,\n options: FormatImportOptions = {},\n ): Promise<ImportPlan> {\n const ctx = contextFor(vault)\n // Mirrors the export side: the import gate is two-tier as well, and a\n // bundle format importing under the plaintext tier would be a silent\n // downgrade in the other direction.\n if (format.tier === 'bundle') ctx.assertCanImport('bundle')\n else ctx.assertCanImport('plaintext', format.id)\n if (!format.decode) {\n throw new ValidationError(\n `vault.import: the '${format.id}' format is export-only — it declares no decode(). ` +\n 'Pick a format that round-trips, or write the records directly.',\n )\n }\n const decoded = await format.decode(input)\n const candidate: Record<string, readonly unknown[]> = {}\n for (const chunk of decoded) {\n const target = chunk.collection || options.collection\n if (!target) {\n throw new ValidationError(\n `vault.import: the '${format.id}' input names no collection and none was supplied. ` +\n 'Pass { collection } for formats whose payload is collection-less.',\n )\n }\n candidate[target] = [...(candidate[target] ?? []), ...chunk.records]\n }\n return ctx.plan(candidate, options.policy ?? 'merge', format.id, options.idKey)\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  MANIFEST_COLLECTION
3
3
  } from "./chunk-EPFF7LOX.js";
4
- import {
5
- SCHEMAS_COLLECTION
6
- } from "./chunk-WWNYSV7S.js";
7
4
  import {
8
5
  SEQUENCE_COLLECTION
9
6
  } from "./chunk-MMOZQTXV.js";
7
+ import {
8
+ SCHEMAS_COLLECTION
9
+ } from "./chunk-WWNYSV7S.js";
10
10
  import {
11
11
  PERIODS_COLLECTION,
12
12
  PERIOD_ARCHIVES_COLLECTION,
@@ -262,4 +262,4 @@ export {
262
262
  loadVault,
263
263
  verifyBackupIntegrity
264
264
  };
265
- //# sourceMappingURL=backup-VASRVLDZ.js.map
265
+ //# sourceMappingURL=backup-R6A4VZRX.js.map
@@ -5,7 +5,7 @@ import {
5
5
  describeExtraction,
6
6
  unsealDeks,
7
7
  withCargo
8
- } from "../chunk-ESVREVWN.js";
8
+ } from "../chunk-7NTBVFGD.js";
9
9
  import {
10
10
  extractPartition,
11
11
  walkClosure
@@ -29,10 +29,10 @@ import {
29
29
  isQuorum,
30
30
  runDrainBarrier
31
31
  } from "../chunk-VTFVLCUH.js";
32
- import "../chunk-WWNYSV7S.js";
33
32
  import {
34
33
  CustodyApi
35
34
  } from "../chunk-Y4MJQ2UG.js";
35
+ import "../chunk-WWNYSV7S.js";
36
36
  import "../chunk-PKJ43CMD.js";
37
37
  import "../chunk-MLO75SFJ.js";
38
38
  import {
@@ -1,8 +1,382 @@
1
+ import {
2
+ jsonSchemaToFields
3
+ } from "./chunk-VYYM3MEN.js";
4
+ import {
5
+ derivePersistedSchema,
6
+ isZod4Schema
7
+ } from "./chunk-URHRIAV6.js";
8
+ import {
9
+ loadPersistedSchema
10
+ } from "./chunk-WWNYSV7S.js";
11
+ import {
12
+ isStaticDictDescriptor
13
+ } from "./chunk-TRGTKX5E.js";
1
14
  import {
2
15
  DerivationCycleError,
3
16
  MaterializedViewCycleError
4
17
  } from "./chunk-7O3Y4PPD.js";
5
18
 
19
+ // src/with-shape/introspection/field-meta.ts
20
+ var FieldMetaUnknownFieldError = class extends Error {
21
+ constructor(collection, key) {
22
+ super(`fieldMeta for collection "${collection}" references unknown field "${key}". Declare it in the schema/config or remove the fieldMeta entry.`);
23
+ this.collection = collection;
24
+ this.key = key;
25
+ this.name = "FieldMetaUnknownFieldError";
26
+ }
27
+ collection;
28
+ key;
29
+ };
30
+ function validateFieldMetaKeys(collection, fieldMeta, knownFields) {
31
+ for (const key of Object.keys(fieldMeta)) {
32
+ if (!knownFields.has(key)) throw new FieldMetaUnknownFieldError(collection, key);
33
+ }
34
+ }
35
+ function humanizeFieldKey(key) {
36
+ return key.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim().split(/\s+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
37
+ }
38
+ function resolveFieldMeta(key, inputs) {
39
+ const { channel, zodMeta, inferred } = inputs;
40
+ const pick = (k) => channel?.[k] ?? zodMeta?.[k] ?? inferred?.[k];
41
+ const description = pick("description");
42
+ const semanticType = pick("semanticType");
43
+ const unit = pick("unit");
44
+ const sensitivity = pick("sensitivity");
45
+ const aggregate = pick("aggregate");
46
+ const aliases = pick("aliases");
47
+ const displayFor = pick("displayFor");
48
+ const widget = pick("widget");
49
+ const group = pick("group");
50
+ const order = pick("order");
51
+ return {
52
+ label: pick("label") ?? humanizeFieldKey(key),
53
+ ...description !== void 0 ? { description } : {},
54
+ ...semanticType !== void 0 ? { semanticType } : {},
55
+ ...unit !== void 0 ? { unit } : {},
56
+ ...sensitivity !== void 0 ? { sensitivity } : {},
57
+ ...aggregate !== void 0 ? { aggregate } : {},
58
+ ...aliases !== void 0 ? { aliases } : {},
59
+ ...displayFor !== void 0 ? { displayFor } : {},
60
+ ...widget !== void 0 ? { widget } : {},
61
+ ...group !== void 0 ? { group } : {},
62
+ ...order !== void 0 ? { order } : {}
63
+ };
64
+ }
65
+
66
+ // src/with-shape/introspection/describe.ts
67
+ var ZOD_META_KEYS = /* @__PURE__ */ new Set([
68
+ "label",
69
+ "description",
70
+ "unit",
71
+ "semanticType",
72
+ "sensitivity",
73
+ "aggregate",
74
+ "aliases",
75
+ "displayFor",
76
+ "group",
77
+ "order"
78
+ ]);
79
+ function schemaFieldKeys(schema) {
80
+ let s = schema;
81
+ for (let depth = 0; depth < 10; depth++) {
82
+ if (s === null || typeof s !== "object") return void 0;
83
+ const shape = s.shape;
84
+ if (shape !== null && typeof shape === "object") {
85
+ const keys = Object.keys(shape);
86
+ return keys.length > 0 ? keys : void 0;
87
+ }
88
+ const def = s._def;
89
+ if (def?.schema !== void 0 && def.effect?.type === "refinement") {
90
+ s = def.schema;
91
+ continue;
92
+ }
93
+ return void 0;
94
+ }
95
+ return void 0;
96
+ }
97
+ async function deriveZodFields(schema) {
98
+ const envelope = await derivePersistedSchema(schema);
99
+ if (!envelope.jsonSchema) return {};
100
+ const isZod4 = isZod4Schema(schema);
101
+ const jsonSchema = envelope.jsonSchema;
102
+ const fieldDescriptors = jsonSchemaToFields(jsonSchema, "live-validator");
103
+ const propertiesRaw = jsonSchema["properties"];
104
+ const result = {};
105
+ for (const [key, descriptor] of Object.entries(fieldDescriptors)) {
106
+ let meta;
107
+ if (isZod4 && propertiesRaw) {
108
+ const prop = propertiesRaw[key];
109
+ if (prop) {
110
+ const extracted = {};
111
+ let hasAny = false;
112
+ for (const mk of ZOD_META_KEYS) {
113
+ if (mk in prop) {
114
+ extracted[mk] = prop[mk];
115
+ hasAny = true;
116
+ }
117
+ }
118
+ if (hasAny) meta = extracted;
119
+ }
120
+ }
121
+ result[key] = {
122
+ type: descriptor.type,
123
+ optional: descriptor.optional === true,
124
+ ...descriptor.constraints !== void 0 ? { constraints: descriptor.constraints } : {},
125
+ ...meta !== void 0 ? { meta } : {}
126
+ };
127
+ }
128
+ return result;
129
+ }
130
+ async function resolveDescribeFieldIds(store, vaultName, collectionName, getDEK) {
131
+ let dek;
132
+ try {
133
+ dek = await getDEK(collectionName);
134
+ } catch {
135
+ return {};
136
+ }
137
+ const persisted = await loadPersistedSchema(store, vaultName, collectionName, dek);
138
+ return persisted?.fieldIds !== void 0 ? { fieldIds: persisted.fieldIds } : {};
139
+ }
140
+ function deriveWidget(opts) {
141
+ if (opts.resolvedWidget !== void 0) return opts.resolvedWidget;
142
+ switch (opts.semanticType) {
143
+ case "date":
144
+ case "datetime":
145
+ return "date";
146
+ case "currency":
147
+ return "money";
148
+ case "entity":
149
+ return "ref-select";
150
+ case "url":
151
+ return "url";
152
+ case "email":
153
+ return "email";
154
+ case "percent":
155
+ return "number";
156
+ }
157
+ if (opts.dict !== void 0 || opts.lookup !== void 0) return "select";
158
+ if (opts.blob !== void 0) return "file";
159
+ if (opts.type === "boolean") return "checkbox";
160
+ if (opts.type === "number") return "number";
161
+ return "text";
162
+ }
163
+ function buildDescription(input) {
164
+ const { collection, fieldMeta, moneyFields, dictKeyFields, lookupFields, computed, refs, zodFields, schema, dictLabels, meta, i18nFields, classified, taint, fieldIds } = input;
165
+ const lookupFragments = input.viaFragments?.["lookup"]?.lookupFields;
166
+ const blobFragments = input.viaFragments?.["blob"]?.blobFields;
167
+ const validatorKeys = zodFields !== void 0 && Object.keys(zodFields).length > 0 ? Object.keys(zodFields) : schemaFieldKeys(schema);
168
+ const canEnumerateFields = validatorKeys !== void 0;
169
+ if (canEnumerateFields && fieldMeta !== void 0) {
170
+ const knownFields = /* @__PURE__ */ new Set([
171
+ ...Object.keys(moneyFields ?? {}),
172
+ ...Object.keys(dictKeyFields ?? {}),
173
+ ...Object.keys(refs),
174
+ ...Object.keys(computed ?? {}),
175
+ ...validatorKeys ?? [],
176
+ ...Object.keys(i18nFields ?? {}),
177
+ ...Object.keys(lookupFields ?? {}),
178
+ ...Object.keys(blobFragments ?? {}),
179
+ ...Object.keys(classified ?? {})
180
+ ]);
181
+ validateFieldMetaKeys(collection, fieldMeta, knownFields);
182
+ }
183
+ const allKeys = /* @__PURE__ */ new Set([
184
+ ...Object.keys(moneyFields ?? {}),
185
+ ...Object.keys(dictKeyFields ?? {}),
186
+ ...Object.keys(lookupFields ?? {}),
187
+ ...Object.keys(refs),
188
+ ...Object.keys(computed ?? {}),
189
+ ...Object.keys(fieldMeta ?? {}),
190
+ ...Object.keys(zodFields ?? {}),
191
+ ...Object.keys(i18nFields ?? {}),
192
+ ...Object.keys(classified ?? {}),
193
+ ...Object.keys(blobFragments ?? {})
194
+ ]);
195
+ const fields = [];
196
+ for (const key of [...allKeys].sort()) {
197
+ const zod = zodFields?.[key];
198
+ const money = moneyFields?.[key];
199
+ const dict = dictKeyFields?.[key];
200
+ const lookupDesc = lookupFields?.[key];
201
+ const refDesc = refs[key];
202
+ const isComputed = computed !== void 0 && key in computed;
203
+ const i18nDesc = i18nFields?.[key];
204
+ const cls = classified?.[key];
205
+ const blobFragmentEntry = blobFragments?.[key];
206
+ const taintPosture = taint?.postures.get(key);
207
+ let type = zod?.type ?? "unknown";
208
+ const inferred = {};
209
+ let moneyBlock;
210
+ let dictBlock;
211
+ let refBlock;
212
+ let lookupBlock;
213
+ let blobBlock;
214
+ if (money) {
215
+ type = "number";
216
+ inferred.semanticType = "currency";
217
+ inferred.aggregate = "sum";
218
+ const currency = money.soleCurrency();
219
+ const scale = currency !== void 0 ? money.scaleFor(currency) : void 0;
220
+ moneyBlock = {
221
+ mode: money.mode,
222
+ ...currency !== void 0 ? { currency } : {},
223
+ ...scale !== void 0 ? { scale } : {},
224
+ ...money.rounding !== void 0 ? { rounding: money.rounding } : {}
225
+ };
226
+ } else if (refDesc) {
227
+ type = refDesc.isArray ? "array" : "string";
228
+ inferred.semanticType = "entity";
229
+ refBlock = {
230
+ target: refDesc.target,
231
+ mode: refDesc.mode,
232
+ ...refDesc.isArray ? { isArray: true } : {}
233
+ };
234
+ } else if (dict) {
235
+ type = "enum";
236
+ if (isStaticDictDescriptor(dict)) {
237
+ const displayLocale = dict.displayLocale;
238
+ const values = dict.keys.map((k) => {
239
+ const localeMap = dict.table[k];
240
+ const label = displayLocale !== void 0 ? localeMap?.[displayLocale] ?? void 0 : void 0;
241
+ return label !== void 0 ? { value: k, label } : { value: k };
242
+ });
243
+ dictBlock = { name: dict.name, static: true, values };
244
+ } else {
245
+ const labelMap = dictLabels?.[dict.name];
246
+ const labelMapHasEntries = labelMap !== void 0 && Object.keys(labelMap).length > 0;
247
+ if (labelMapHasEntries) {
248
+ const values = Object.entries(labelMap).map(([value, label]) => ({ value, label }));
249
+ dictBlock = { name: dict.name, static: false, values };
250
+ } else if (dict.keys !== void 0) {
251
+ const values = dict.keys.map((k) => {
252
+ const label = dict.labels?.[k];
253
+ return label !== void 0 ? { value: k, label } : { value: k };
254
+ });
255
+ dictBlock = { name: dict.name, static: false, values };
256
+ } else {
257
+ dictBlock = { name: dict.name, static: false };
258
+ }
259
+ }
260
+ } else if (lookupDesc) {
261
+ type = "enum";
262
+ const lookupLabelMap = dictLabels?.[lookupDesc.dimension];
263
+ const lookupLabelMapHasEntries = lookupLabelMap !== void 0 && Object.keys(lookupLabelMap).length > 0;
264
+ if (lookupDesc.table !== void 0) {
265
+ const displayLocale = lookupDesc.displayLocale;
266
+ const table = lookupDesc.table;
267
+ const values = (lookupDesc.keys ?? Object.keys(table)).map((k) => {
268
+ const localeMap = table[k];
269
+ const label = displayLocale !== void 0 ? localeMap?.[displayLocale] ?? void 0 : void 0;
270
+ return label !== void 0 ? { value: k, label } : { value: k };
271
+ });
272
+ dictBlock = { name: lookupDesc.dimension, static: true, values };
273
+ } else if (lookupLabelMapHasEntries) {
274
+ const values = Object.entries(lookupLabelMap).map(([value, label]) => ({ value, label }));
275
+ dictBlock = { name: lookupDesc.dimension, static: false, values };
276
+ } else if (lookupDesc.keys !== void 0) {
277
+ const values = lookupDesc.keys.map((k) => {
278
+ const label = lookupDesc.labels?.[k];
279
+ return label !== void 0 ? { value: k, label } : { value: k };
280
+ });
281
+ dictBlock = { name: lookupDesc.dimension, static: false, values };
282
+ } else {
283
+ dictBlock = { name: lookupDesc.dimension, static: false };
284
+ }
285
+ } else if (i18nDesc) {
286
+ type = "string";
287
+ } else if (isComputed) {
288
+ } else if (blobFragmentEntry !== void 0) {
289
+ type = "blob";
290
+ blobBlock = { ...blobFragmentEntry, queryable: "none" };
291
+ }
292
+ const lookupFragmentEntry = lookupFragments?.[key];
293
+ if (lookupFragmentEntry !== void 0) {
294
+ lookupBlock = {
295
+ ...lookupFragmentEntry.dimension !== void 0 ? { dimension: lookupFragmentEntry.dimension } : {},
296
+ backing: lookupFragmentEntry.backing,
297
+ vocabulary: lookupFragmentEntry.vocabulary,
298
+ key: lookupFragmentEntry.key,
299
+ onDelete: lookupFragmentEntry.onDelete,
300
+ ...lookupFragmentEntry.altKeys !== void 0 ? { altKeys: lookupFragmentEntry.altKeys } : {},
301
+ ...lookupFragmentEntry.present !== void 0 ? { present: lookupFragmentEntry.present } : {},
302
+ ...lookupFragmentEntry.sortBy !== void 0 ? { sortBy: lookupFragmentEntry.sortBy } : {},
303
+ ...lookupFragmentEntry.keys !== void 0 ? { keys: lookupFragmentEntry.keys } : {}
304
+ };
305
+ }
306
+ if (cls !== void 0) inferred.sensitivity = cls.sensitivity;
307
+ const channelEntry = fieldMeta?.[key];
308
+ const zodMeta = zod?.meta;
309
+ const resolved = resolveFieldMeta(key, {
310
+ ...channelEntry !== void 0 ? { channel: channelEntry } : {},
311
+ ...zodMeta !== void 0 ? { zodMeta } : {},
312
+ inferred
313
+ });
314
+ let i18nBlock;
315
+ if (i18nDesc !== void 0) {
316
+ const locales = i18nDesc.options.languages;
317
+ const densify = i18nDesc.options.densifyOnWrite;
318
+ i18nBlock = {
319
+ ...locales !== void 0 ? { locales } : {},
320
+ ...densify !== void 0 ? { densify } : {}
321
+ };
322
+ }
323
+ const widget = deriveWidget({
324
+ type,
325
+ ...resolved.semanticType !== void 0 ? { semanticType: resolved.semanticType } : {},
326
+ ...dictBlock !== void 0 ? { dict: dictBlock } : {},
327
+ ...lookupBlock !== void 0 ? { lookup: lookupBlock } : {},
328
+ ...blobBlock !== void 0 ? { blob: blobBlock } : {},
329
+ ...resolved.widget !== void 0 ? { resolvedWidget: resolved.widget } : {}
330
+ });
331
+ const editable = !isComputed && key !== "id" && blobBlock === void 0;
332
+ const field = {
333
+ key,
334
+ ...fieldIds?.[key] !== void 0 ? { id: fieldIds[key] } : {},
335
+ type,
336
+ optional: zod?.optional ?? false,
337
+ ...zod?.constraints !== void 0 ? { constraints: zod.constraints } : {},
338
+ label: resolved.label,
339
+ ...resolved.description !== void 0 ? { description: resolved.description } : {},
340
+ ...resolved.group !== void 0 ? { group: resolved.group } : {},
341
+ ...resolved.order !== void 0 ? { order: resolved.order } : {},
342
+ ...resolved.semanticType !== void 0 ? { semanticType: resolved.semanticType } : {},
343
+ ...resolved.unit !== void 0 ? { unit: resolved.unit } : {},
344
+ ...resolved.sensitivity !== void 0 ? { sensitivity: resolved.sensitivity } : {},
345
+ ...resolved.aggregate !== void 0 ? { aggregate: resolved.aggregate } : {},
346
+ ...resolved.aliases !== void 0 ? { aliases: resolved.aliases } : {},
347
+ ...resolved.displayFor !== void 0 ? { displayFor: resolved.displayFor } : {},
348
+ ...refBlock !== void 0 ? { ref: refBlock } : {},
349
+ ...moneyBlock !== void 0 ? { money: moneyBlock } : {},
350
+ ...dictBlock !== void 0 ? { dict: dictBlock } : {},
351
+ ...lookupBlock !== void 0 ? { lookup: lookupBlock } : {},
352
+ ...blobBlock !== void 0 ? { blob: blobBlock } : {},
353
+ ...isComputed ? { computed: true } : {},
354
+ ...i18nBlock !== void 0 ? { i18n: i18nBlock } : {},
355
+ widget,
356
+ editable,
357
+ ...cls !== void 0 ? {
358
+ classified: {
359
+ preset: cls.preset,
360
+ storage: cls.storage,
361
+ list: cls.list.kind === "omit" ? "omit" : cls.list.kind === "mask" ? { mask: cls.list.pattern } : { rider: cls.list.rider },
362
+ ...cls.equatable === true ? { equatable: true } : {}
363
+ }
364
+ } : {},
365
+ ...taintPosture !== void 0 ? { taint: { posture: taintPosture, forcedBy: taint?.provenance?.get(key) ?? [] } } : {}
366
+ };
367
+ fields.push(field);
368
+ }
369
+ const collectionMeta = {
370
+ ...meta,
371
+ label: meta?.label ?? humanizeFieldKey(collection)
372
+ };
373
+ return {
374
+ collection,
375
+ fields,
376
+ meta: collectionMeta
377
+ };
378
+ }
379
+
6
380
  // src/kernel/via/graph.ts
7
381
  var DEFAULT_POSTURE = { encryptedAtRest: "envelope", queryable: "full", exportable: true, forgettable: false };
8
382
  var QUERYABLE_RANK = {
@@ -338,7 +712,11 @@ var ViaGraph = class {
338
712
  };
339
713
 
340
714
  export {
715
+ schemaFieldKeys,
716
+ deriveZodFields,
717
+ resolveDescribeFieldIds,
718
+ buildDescription,
341
719
  DEFAULT_POSTURE,
342
720
  ViaGraph
343
721
  };
344
- //# sourceMappingURL=chunk-UB5CRTFF.js.map
722
+ //# sourceMappingURL=chunk-7BE7ZDVQ.js.map