@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
@@ -0,0 +1,20 @@
1
+ // src/kernel/match-pairs.ts
2
+ var scalar = (v) => typeof v === "string" || typeof v === "number" ? String(v) : null;
3
+ function recordMatchesPairs(rec, pairs) {
4
+ return pairs.every((p) => scalar(rec[p.field]) === p.value);
5
+ }
6
+ async function findMatchingIdsByPairs(residualPairs, io) {
7
+ if (io.indexCandidates !== null && residualPairs.length === 0) return [...io.indexCandidates];
8
+ const ids = io.indexCandidates ?? await io.listIds();
9
+ const out = [];
10
+ for (const id of ids) {
11
+ const rec = await io.getRecord(id);
12
+ if (rec !== null && recordMatchesPairs(rec, residualPairs)) out.push(id);
13
+ }
14
+ return out;
15
+ }
16
+
17
+ export {
18
+ findMatchingIdsByPairs
19
+ };
20
+ //# sourceMappingURL=chunk-TFMM5DBB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kernel/match-pairs.ts"],"sourcesContent":["/**\n * Kernel-side matching primitives for composite `triggerBy` fan-out (#1249).\n *\n * Lives in the kernel (not `with-formula/derivations/`) so `collection.ts` can\n * import it statically without dragging derivations code into the floor\n * bundle — the module used to be dynamically imported from `collection.ts`\n * while also being statically imported by `registry.ts`/`dispatch.ts`,\n * which merged it into a chunk that `dist/index.js` ended up pulling in\n * statically.\n * @module\n */\n\nconst scalar = (v: unknown): string | null =>\n (typeof v === 'string' || typeof v === 'number') ? String(v) : null\n\n/** Conjunction over one candidate record, same coercion as tupleFromWritten. */\nexport function recordMatchesPairs(\n rec: Record<string, unknown>,\n pairs: ReadonlyArray<{ field: string; value: string }>,\n): boolean {\n return pairs.every((p) => scalar(rec[p.field]) === p.value)\n}\n\n/** Scan/filter core for composite fan-out. `residualPairs` are the pairs NOT\n * already decided by `indexCandidates` (the id set from an equality index for\n * ONE pair, or null when no pair is indexed). When the index alone decides\n * membership (`indexCandidates` non-null and no residual pairs left), this\n * returns the index's answer verbatim with ZERO record reads — matching the\n * old single-field `_findMatchingIds`' `[...hit]` fast path exactly. */\nexport async function findMatchingIdsByPairs(\n residualPairs: ReadonlyArray<{ field: string; value: string }>,\n io: {\n indexCandidates: ReadonlyArray<string> | null\n listIds: () => Promise<ReadonlyArray<string>>\n getRecord: (id: string) => Promise<Record<string, unknown> | null>\n },\n): Promise<string[]> {\n if (io.indexCandidates !== null && residualPairs.length === 0) return [...io.indexCandidates]\n const ids = io.indexCandidates ?? await io.listIds()\n const out: string[] = []\n for (const id of ids) {\n const rec = await io.getRecord(id)\n if (rec !== null && recordMatchesPairs(rec, residualPairs)) out.push(id)\n }\n return out\n}\n"],"mappings":";AAYA,IAAM,SAAS,CAAC,MACb,OAAO,MAAM,YAAY,OAAO,MAAM,WAAY,OAAO,CAAC,IAAI;AAG1D,SAAS,mBACd,KACA,OACS;AACT,SAAO,MAAM,MAAM,CAAC,MAAM,OAAO,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK;AAC5D;AAQA,eAAsB,uBACpB,eACA,IAKmB;AACnB,MAAI,GAAG,oBAAoB,QAAQ,cAAc,WAAW,EAAG,QAAO,CAAC,GAAG,GAAG,eAAe;AAC5F,QAAM,MAAM,GAAG,mBAAmB,MAAM,GAAG,QAAQ;AACnD,QAAM,MAAgB,CAAC;AACvB,aAAW,MAAM,KAAK;AACpB,UAAM,MAAM,MAAM,GAAG,UAAU,EAAE;AACjC,QAAI,QAAQ,QAAQ,mBAAmB,KAAK,aAAa,EAAG,KAAI,KAAK,EAAE;AAAA,EACzE;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,30 @@
1
+ // src/with-formula/derivations/trigger-match.ts
2
+ function normalizeTriggerBy(triggerBy) {
3
+ if (triggerBy === void 0) return [];
4
+ return triggerBy.map((t) => ({
5
+ collection: t.collection,
6
+ match: t.match ?? [{ from: "id", to: t.on }],
7
+ ...t.maxFanout !== void 0 ? { maxFanout: t.maxFanout } : {}
8
+ }));
9
+ }
10
+ var scalar = (v) => typeof v === "string" || typeof v === "number" ? String(v) : null;
11
+ function tupleFromWritten(match, writtenId, record) {
12
+ const out = [];
13
+ for (const pair of match) {
14
+ const v = pair.from === "id" ? writtenId : scalar(record?.[pair.from]);
15
+ if (v === null) return null;
16
+ out.push({ field: pair.to, value: v });
17
+ }
18
+ return out;
19
+ }
20
+ function sameTuple(a, b) {
21
+ if (a === null || b === null) return a === b;
22
+ return a.length === b.length && a.every((p, i) => p.field === b[i].field && p.value === b[i].value);
23
+ }
24
+
25
+ export {
26
+ normalizeTriggerBy,
27
+ tupleFromWritten,
28
+ sameTuple
29
+ };
30
+ //# sourceMappingURL=chunk-TPHAGLUH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-formula/derivations/trigger-match.ts"],"sourcesContent":["/**\n * Pure helpers behind composite `triggerBy` (#1249).\n *\n * A trigger entry is a CONJUNCTION of equality pairs: a source record\n * matches when EVERY pair satisfies String(source[to]) === String(written[from]).\n * `from: 'id'` reads the written record's id (winning over any stored field\n * named `id`, matching dispatch's `{ ...incoming, id }` convention). The\n * legacy `on` form normalizes to `[{ from: 'id', to: on }]` so everything\n * downstream has ONE shape.\n *\n * Scalar coercion is verbatim from `_findMatchingIds`: both sides must be\n * string | number; anything else fails the pair (never throws).\n * Spec: docs/superpowers/specs/2026-08-29-composite-triggerby-design.md §4-§5.\n * @module\n */\n\nexport interface MatchPair { readonly from: string; readonly to: string }\n\nexport interface NormalizedTrigger {\n readonly collection: string\n readonly match: ReadonlyArray<MatchPair>\n readonly maxFanout?: number\n}\n\ninterface RawTrigger {\n readonly collection: string\n readonly on?: string\n readonly match?: ReadonlyArray<MatchPair>\n readonly maxFanout?: number\n}\n\nexport function normalizeTriggerBy(triggerBy: ReadonlyArray<RawTrigger> | undefined): NormalizedTrigger[] {\n if (triggerBy === undefined) return []\n return triggerBy.map((t) => ({\n collection: t.collection,\n match: t.match ?? [{ from: 'id', to: t.on! }],\n ...(t.maxFanout !== undefined ? { maxFanout: t.maxFanout } : {}),\n }))\n}\n\nconst scalar = (v: unknown): string | null =>\n (typeof v === 'string' || typeof v === 'number') ? String(v) : null\n\n/**\n * The value tuple a written record presents to one trigger entry.\n * `null` means \"this record cannot address any source\" (a from-field is\n * absent or non-scalar) — a legitimate no-match, not an error.\n */\nexport function tupleFromWritten(\n match: ReadonlyArray<MatchPair>,\n writtenId: string,\n record: Record<string, unknown> | null,\n): Array<{ field: string; value: string }> | null {\n const out: Array<{ field: string; value: string }> = []\n for (const pair of match) {\n const v = pair.from === 'id' ? writtenId : scalar(record?.[pair.from])\n if (v === null) return null\n out.push({ field: pair.to, value: v })\n }\n return out\n}\n\n/** Any single component differing means NOT the same tuple (spec §7). */\nexport function sameTuple(\n a: Array<{ field: string; value: string }> | null,\n b: Array<{ field: string; value: string }> | null,\n): boolean {\n if (a === null || b === null) return a === b\n return a.length === b.length && a.every((p, i) => p.field === b[i]!.field && p.value === b[i]!.value)\n}\n\n/** Moved to the kernel (#1249) so `collection.ts` can import statically without\n * dragging derivations code into the floor bundle; re-exported here for\n * compatibility with existing consumers of this module. */\nexport { recordMatchesPairs, findMatchingIdsByPairs } from '../../kernel/match-pairs.js'\n"],"mappings":";AA+BO,SAAS,mBAAmB,WAAuE;AACxG,MAAI,cAAc,OAAW,QAAO,CAAC;AACrC,SAAO,UAAU,IAAI,CAAC,OAAO;AAAA,IAC3B,YAAY,EAAE;AAAA,IACd,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,MAAM,IAAI,EAAE,GAAI,CAAC;AAAA,IAC5C,GAAI,EAAE,cAAc,SAAY,EAAE,WAAW,EAAE,UAAU,IAAI,CAAC;AAAA,EAChE,EAAE;AACJ;AAEA,IAAM,SAAS,CAAC,MACb,OAAO,MAAM,YAAY,OAAO,MAAM,WAAY,OAAO,CAAC,IAAI;AAO1D,SAAS,iBACd,OACA,WACA,QACgD;AAChD,QAAM,MAA+C,CAAC;AACtD,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,KAAK,SAAS,OAAO,YAAY,OAAO,SAAS,KAAK,IAAI,CAAC;AACrE,QAAI,MAAM,KAAM,QAAO;AACvB,QAAI,KAAK,EAAE,OAAO,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EACvC;AACA,SAAO;AACT;AAGO,SAAS,UACd,GACA,GACS;AACT,MAAI,MAAM,QAAQ,MAAM,KAAM,QAAO,MAAM;AAC3C,SAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,CAAC,EAAG,SAAS,EAAE,UAAU,EAAE,CAAC,EAAG,KAAK;AACtG;","names":[]}
@@ -1,7 +1,9 @@
1
1
  import {
2
- fallbackDerivationName,
2
+ fallbackDerivationName
3
+ } from "./chunk-RXLYKVTF.js";
4
+ import {
3
5
  jsonSchemaToFields
4
- } from "./chunk-KHKKD62N.js";
6
+ } from "./chunk-VYYM3MEN.js";
5
7
  import {
6
8
  derivePersistedSchema
7
9
  } from "./chunk-URHRIAV6.js";
@@ -238,4 +240,4 @@ function summariseAggregateOp(value) {
238
240
  export {
239
241
  dumpVaultSchema
240
242
  };
241
- //# sourceMappingURL=chunk-A5WZEPXJ.js.map
243
+ //# sourceMappingURL=chunk-VGICZPVE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/with-shape/introspection/walk.ts"],"sourcesContent":["/**\n * Orchestrate the structural walk of a Vault, producing a\n * {@link VaultSchemaSnapshot}. Called from `Vault.dumpSchema()`.\n *\n * @module\n */\n\nimport { derivePersistedSchema } from '../persisted-schemas/derive.js'\nimport { loadPersistedSchema } from '../persisted-schemas/storage.js'\nimport { fallbackDerivationName } from './derivation-key.js'\nimport { jsonSchemaToFields } from './fields.js'\nimport type {\n CollectionConfig,\n CollectionDescriptor,\n CollectionStats,\n DumpSchemaOptions,\n FieldDescriptor,\n InternalCollectionStats,\n MaterializedViewDescriptor,\n OverlayViewDescriptor,\n DerivationDescriptor,\n VaultSchemaSnapshot,\n} from './types.js'\nimport type { Collection } from '../../kernel/collection.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { RefRegistry } from '../../kernel/refs.js'\nimport type { VaultMeta } from './meta.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\n\n/**\n * The minimal slice of Vault internal state the walker needs.\n * Exposed via `vault._introspectState()` to keep the public Vault\n * surface narrow.\n *\n * @internal\n */\nexport interface VaultIntrospectState {\n readonly name: string\n readonly adapter: NoydbStore\n readonly collectionCache: Map<string, Collection<unknown>>\n readonly refRegistry: RefRegistry\n readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n /** The active unlocked keyring — role/permissions/userId for access-scoped ops. */\n readonly keyring: UnlockedKeyring\n readonly subsystems: Record<string, boolean>\n /** Vault-level descriptive metadata, when set via `openVault({meta})`. */\n readonly vaultMeta?: VaultMeta\n // Typed loosely on purpose — these are private service registries\n // accessed only for \"is anything registered\" enumeration.\n readonly mvRegistry: unknown\n readonly overlayRegistry: unknown\n readonly derivationRegistry: unknown\n /**\n * Returns the registered schema-update strategy names for a collection,\n * or `undefined` when none are registered. Populated from\n * `vault.#schemaUpdateNames` in `_introspectState()`.\n */\n readonly getCollectionSchemaUpdateNames?: (col: string) => readonly string[] | undefined\n /**\n * Returns `true` when the collection has an archive policy registered\n * in `vault.archiveRegistry`, `false` otherwise. Populated from\n * `vault.archiveRegistry` in `_introspectState()`.\n */\n readonly hasCollectionArchive?: (col: string) => boolean\n}\n\nconst INTERNAL_PREFIX = '_'\n\n/** Reserved internal collections always present in any real vault. */\nconst KNOWN_INTERNAL_NAMES = ['_keyring', '_ledger', '_meta', '_schemas', '_deltas']\n\nexport async function dumpVaultSchema(\n vault: { _introspectState(): VaultIntrospectState },\n opts: DumpSchemaOptions,\n): Promise<VaultSchemaSnapshot> {\n const state = vault._introspectState()\n const sampleSize = opts.sampleSize ?? 50\n const withStats = opts.withStats === true\n\n // 1. User-facing collections — alphabetical for diff stability\n const cacheNames = [...state.collectionCache.keys()]\n const storageNames = (await safeListAllCollections(state.adapter, state.name))\n .filter((n) => !n.startsWith(INTERNAL_PREFIX))\n const allNames = Array.from(new Set([...cacheNames, ...storageNames])).sort()\n\n const collections: Record<string, CollectionDescriptor> = {}\n for (const name of allNames) {\n collections[name] = await describeCollection(state, name, sampleSize, withStats)\n }\n\n // 2. Materialized views (placeholder — real walk in MV slice 2 follow-up)\n const materializedViews = describeMVs(state.mvRegistry)\n // 3. Overlay views\n const overlayViews = describeOverlays(state.overlayRegistry)\n // 4. Derivations\n const derivations = describeDerivations(state.derivationRegistry)\n\n // 5. Internal collections — only with stats\n let internal: Record<string, InternalCollectionStats> | undefined\n if (withStats) {\n internal = {}\n for (const name of KNOWN_INTERNAL_NAMES) {\n const stats = await statsForCollection(state.adapter, state.name, name)\n if (stats.records > 0) {\n internal[name] = { records: stats.records, bytes: stats.bytes }\n }\n }\n }\n\n const snap: VaultSchemaSnapshot = {\n _noydb_snapshot: 1,\n vault: state.name,\n emittedAt: new Date().toISOString(),\n subsystems: state.subsystems,\n ...(state.vaultMeta !== undefined ? { meta: state.vaultMeta } : {}),\n collections,\n materializedViews,\n overlayViews,\n derivations,\n ...(internal !== undefined ? { internal } : {}),\n }\n return snap\n}\n\nasync function safeListAllCollections(adapter: NoydbStore, vault: string): Promise<string[]> {\n try {\n const snap = await adapter.loadAll(vault)\n return Object.keys(snap)\n } catch {\n return []\n }\n}\n\nasync function describeCollection(\n state: VaultIntrospectState,\n collectionName: string,\n sampleSize: number,\n withStats: boolean,\n): Promise<CollectionDescriptor> {\n let fields: Record<string, FieldDescriptor> = {}\n let validator: CollectionDescriptor['validator']\n\n const refsRaw = state.refRegistry.getOutbound(collectionName)\n const refs: CollectionDescriptor['refs'] = {}\n for (const [name, desc] of Object.entries(refsRaw)) {\n refs[name] = { target: desc.target, mode: desc.mode, ...(desc.isArray ? { isArray: true } : {}) }\n }\n\n // 1. Try the persisted Route B envelope first.\n try {\n const dek = await state.getDEK(collectionName)\n const persisted = await loadPersistedSchema(state.adapter, state.name, collectionName, dek)\n if (persisted) {\n validator = { kind: persisted.kind, source: 'persisted' }\n if (persisted.jsonSchema) {\n fields = jsonSchemaToFields(persisted.jsonSchema, 'persisted', refsRaw)\n }\n }\n } catch {\n // No DEK or decrypt failure — fall through to live-validator\n }\n\n const liveColl = state.collectionCache.get(collectionName)\n\n // 2. Try the live in-process validator (when no persisted envelope).\n if (!validator) {\n const schema = liveColl?.getSchema()\n if (schema) {\n try {\n const derived = await derivePersistedSchema(schema)\n validator = { kind: derived.kind, source: 'live-validator' }\n if (derived.jsonSchema) {\n fields = jsonSchemaToFields(derived.jsonSchema, 'live-validator', refsRaw)\n }\n } catch {\n // Derivation failed (e.g. missing peer-dep) — silently leave fields empty\n }\n }\n }\n\n // 3. Sampling fallback — deferred to a follow-up. For now: empty when no schema.\n if (Object.keys(fields).length === 0 && sampleSize > 0) {\n // Sampling path not implemented in baseline slice 2; reserved for follow-up.\n }\n\n // Populate collection-level meta and config from the live collection when available.\n const collMeta = liveColl?.getMeta()\n const collConfig = liveColl?.getConfig()\n\n // Thread vault-level config (archive + schemaUpdate) that the Collection cannot see.\n const archivePresent = state.hasCollectionArchive?.(collectionName) === true\n const schemaUpdateNames = state.getCollectionSchemaUpdateNames?.(collectionName)\n const hasSchemaUpdate = schemaUpdateNames !== undefined && schemaUpdateNames.length > 0\n\n // Merge Collection-level config with vault-level fields. Omit config entirely\n // when all sources are empty.\n let mergedConfig: CollectionConfig | undefined\n if (collConfig !== undefined || archivePresent || hasSchemaUpdate) {\n mergedConfig = {\n ...(collConfig ?? {}),\n ...(archivePresent ? { archive: true as const } : {}),\n ...(hasSchemaUpdate ? { schemaUpdate: schemaUpdateNames } : {}),\n }\n }\n\n const descriptor: CollectionDescriptor = {\n fields,\n indexes: liveColl?.getDeclaredIndexes() ?? [],\n refs,\n ...(validator ? { validator } : {}),\n ...(collMeta !== undefined ? { meta: collMeta } : {}),\n ...(mergedConfig !== undefined ? { config: mergedConfig } : {}),\n }\n if (withStats) {\n const stats = await statsForCollection(state.adapter, state.name, collectionName)\n ;(descriptor as { stats?: CollectionStats }).stats = stats\n }\n return descriptor\n}\n\nasync function statsForCollection(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n): Promise<CollectionStats> {\n const ids = await adapter.list(vault, collection)\n if (ids.length === 0) {\n return { records: 0, bytes: 0, bytesAvg: 0, bytesMin: 0, bytesMax: 0, oldest: '', newest: '' }\n }\n let total = 0\n let min = Number.POSITIVE_INFINITY\n let max = 0\n let oldest = '￿'\n let newest = ''\n for (const id of ids) {\n const env = await adapter.get(vault, collection, id)\n if (!env) continue\n const size = env._data.length\n total += size\n if (size < min) min = size\n if (size > max) max = size\n if (env._ts < oldest) oldest = env._ts\n if (env._ts > newest) newest = env._ts\n }\n return {\n records: ids.length,\n bytes: total,\n bytesAvg: Math.round(total / ids.length),\n bytesMin: min === Number.POSITIVE_INFINITY ? 0 : min,\n bytesMax: max,\n oldest: oldest === '￿' ? '' : oldest,\n newest,\n }\n}\n\nfunction describeMVs(registry: unknown): Record<string, MaterializedViewDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const items = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, MaterializedViewDescriptor> = {}\n // Each item is RegisteredMV { spec, dependencies, outputCollection, ... }\n for (const item of items) {\n const reg = item as {\n spec?: {\n name?: string\n unionSources?: ReadonlyArray<{ collection: string }>\n groupBy?: string | ReadonlyArray<string>\n aggregate?: Record<string, unknown>\n refresh?: string\n }\n dependencies?: ReadonlySet<string>\n }\n const spec = reg.spec\n if (!spec?.name) continue\n const sources = spec.unionSources\n ? spec.unionSources.map((u) => u.collection)\n : (reg.dependencies ? [...reg.dependencies].sort() : [])\n const groupBy = spec.groupBy\n ? Array.isArray(spec.groupBy) ? [...spec.groupBy] : [spec.groupBy]\n : undefined\n const aggregate = spec.aggregate ? Object.fromEntries(\n Object.entries(spec.aggregate).map(([k, v]) => [k, summariseAggregateOp(v)]),\n ) : undefined\n out[spec.name] = {\n sources,\n ...(groupBy ? { groupBy } : {}),\n ...(aggregate ? { aggregate } : {}),\n refresh: spec.refresh ?? 'eager',\n }\n }\n return out\n}\n\nfunction describeOverlays(registry: unknown): Record<string, OverlayViewDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const specs = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, OverlayViewDescriptor> = {}\n for (const spec of specs) {\n const s = spec as { name?: string; base?: string; overlay?: string }\n if (!s.name || !s.base || !s.overlay) continue\n out[s.name] = { base: s.base, overlay: s.overlay }\n }\n return out\n}\n\nfunction describeDerivations(registry: unknown): Record<string, DerivationDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const items = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, DerivationDescriptor> = {}\n const used = new Set<string>()\n // Pre-seed with every explicit name BEFORE keying any derivation, so an\n // unnamed derivation's fallback key never collides with (and clobbers) a\n // named one, regardless of registration order (#973).\n for (const item of items) {\n const reg = item as { spec?: { name?: string }; name?: string }\n const explicitName = (reg.spec ?? reg).name\n if (explicitName !== undefined) used.add(explicitName)\n }\n for (const item of items) {\n // `all()` on DerivationRegistry returns RegisteredStrategy objects:\n // { spec: DerivationSpec, strategyHash }. Read `spec` and fall\n // back to the item itself for forward-compat with other registries.\n const reg = item as { spec?: { name?: string; source?: string; outputs?: Record<string, { collection: string }> }; name?: string; source?: string; outputs?: Record<string, { collection: string }> }\n const s = reg.spec ?? reg\n if (!s.source) continue\n const outputCollections = s.outputs\n ? Object.values(s.outputs).map((o) => (o as { collection: string }).collection)\n : []\n // Named derivations key by their (vault-unique) `name`. Unnamed ones key\n // by the shared collision-safe fallback (sorted output collections, else\n // source, with a `#occurrence` suffix on collision) — the SAME algorithm\n // `listBehaviors` uses (`./derivation-key.ts`) so both surfaces key an\n // unnamed derivation identically.\n const key = s.name ?? fallbackDerivationName(outputCollections, s.source, used)\n if (s.name === undefined) used.add(key)\n out[key] = {\n source: s.source,\n outputs: outputCollections,\n ...(s.name !== undefined ? { name: s.name } : {}),\n }\n }\n return out\n}\n\nfunction listFromRegistry(reg: Record<string, unknown>): readonly unknown[] {\n // Registries expose different accessor methods; try the common ones.\n for (const method of ['all', 'list', 'specs', 'values']) {\n const fn = reg[method]\n if (typeof fn === 'function') {\n try {\n const out = (fn as () => unknown).call(reg)\n if (Array.isArray(out)) return out\n if (out && typeof (out as { values?: () => unknown }).values === 'function') {\n return [...(out as { values: () => Iterable<unknown> }).values()]\n }\n } catch {\n continue\n }\n }\n }\n return []\n}\n\nfunction summariseAggregateOp(value: unknown): string {\n if (value && typeof value === 'object') {\n const op = (value as { op?: string; kind?: string; field?: string }).op\n ?? (value as { kind?: string }).kind\n const field = (value as { field?: string }).field\n if (op && field) return `${op}(${field})`\n if (op) return op\n }\n return String(value)\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/with-shape/introspection/walk.ts"],"sourcesContent":["/**\n * Orchestrate the structural walk of a Vault, producing a\n * {@link VaultSchemaSnapshot}. Called from `Vault.dumpSchema()`.\n *\n * @module\n */\n\nimport { derivePersistedSchema } from '../persisted-schemas/derive.js'\nimport { loadPersistedSchema } from '../persisted-schemas/storage.js'\nimport { fallbackDerivationName } from './derivation-key.js'\nimport { jsonSchemaToFields } from './fields.js'\nimport type {\n CollectionConfig,\n CollectionDescriptor,\n CollectionStats,\n DumpSchemaOptions,\n FieldDescriptor,\n InternalCollectionStats,\n MaterializedViewDescriptor,\n OverlayViewDescriptor,\n DerivationDescriptor,\n VaultSchemaSnapshot,\n} from './types.js'\nimport type { Collection } from '../../kernel/collection.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { RefRegistry } from '../../kernel/refs.js'\nimport type { VaultMeta } from './meta.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\n\n/**\n * The minimal slice of Vault internal state the walker needs.\n * Exposed via `vault._introspectState()` to keep the public Vault\n * surface narrow.\n *\n * @internal\n */\nexport interface VaultIntrospectState {\n readonly name: string\n readonly adapter: NoydbStore\n readonly collectionCache: Map<string, Collection<unknown>>\n readonly refRegistry: RefRegistry\n readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n /** The active unlocked keyring — role/permissions/userId for access-scoped ops. */\n readonly keyring: UnlockedKeyring\n readonly subsystems: Record<string, boolean>\n /** Vault-level descriptive metadata, when set via `openVault({meta})`. */\n readonly vaultMeta?: VaultMeta\n // Typed loosely on purpose — these are private service registries\n // accessed only for \"is anything registered\" enumeration.\n readonly mvRegistry: unknown\n readonly overlayRegistry: unknown\n readonly derivationRegistry: unknown\n /**\n * Returns the registered schema-update strategy names for a collection,\n * or `undefined` when none are registered. Populated from\n * `vault.#schemaUpdateNames` in `_introspectState()`.\n */\n readonly getCollectionSchemaUpdateNames?: (col: string) => readonly string[] | undefined\n /**\n * Returns `true` when the collection has an archive policy registered\n * in `vault.archiveRegistry`, `false` otherwise. Populated from\n * `vault.archiveRegistry` in `_introspectState()`.\n */\n readonly hasCollectionArchive?: (col: string) => boolean\n}\n\nconst INTERNAL_PREFIX = '_'\n\n/** Reserved internal collections always present in any real vault. */\nconst KNOWN_INTERNAL_NAMES = ['_keyring', '_ledger', '_meta', '_schemas', '_deltas']\n\nexport async function dumpVaultSchema(\n vault: { _introspectState(): VaultIntrospectState },\n opts: DumpSchemaOptions,\n): Promise<VaultSchemaSnapshot> {\n const state = vault._introspectState()\n const sampleSize = opts.sampleSize ?? 50\n const withStats = opts.withStats === true\n\n // 1. User-facing collections — alphabetical for diff stability\n const cacheNames = [...state.collectionCache.keys()]\n const storageNames = (await safeListAllCollections(state.adapter, state.name))\n .filter((n) => !n.startsWith(INTERNAL_PREFIX))\n const allNames = Array.from(new Set([...cacheNames, ...storageNames])).sort()\n\n const collections: Record<string, CollectionDescriptor> = {}\n for (const name of allNames) {\n collections[name] = await describeCollection(state, name, sampleSize, withStats)\n }\n\n // 2. Materialized views (placeholder — real walk in MV slice 2 follow-up)\n const materializedViews = describeMVs(state.mvRegistry)\n // 3. Overlay views\n const overlayViews = describeOverlays(state.overlayRegistry)\n // 4. Derivations\n const derivations = describeDerivations(state.derivationRegistry)\n\n // 5. Internal collections — only with stats\n let internal: Record<string, InternalCollectionStats> | undefined\n if (withStats) {\n internal = {}\n for (const name of KNOWN_INTERNAL_NAMES) {\n const stats = await statsForCollection(state.adapter, state.name, name)\n if (stats.records > 0) {\n internal[name] = { records: stats.records, bytes: stats.bytes }\n }\n }\n }\n\n const snap: VaultSchemaSnapshot = {\n _noydb_snapshot: 1,\n vault: state.name,\n emittedAt: new Date().toISOString(),\n subsystems: state.subsystems,\n ...(state.vaultMeta !== undefined ? { meta: state.vaultMeta } : {}),\n collections,\n materializedViews,\n overlayViews,\n derivations,\n ...(internal !== undefined ? { internal } : {}),\n }\n return snap\n}\n\nasync function safeListAllCollections(adapter: NoydbStore, vault: string): Promise<string[]> {\n try {\n const snap = await adapter.loadAll(vault)\n return Object.keys(snap)\n } catch {\n return []\n }\n}\n\nasync function describeCollection(\n state: VaultIntrospectState,\n collectionName: string,\n sampleSize: number,\n withStats: boolean,\n): Promise<CollectionDescriptor> {\n let fields: Record<string, FieldDescriptor> = {}\n let validator: CollectionDescriptor['validator']\n\n const refsRaw = state.refRegistry.getOutbound(collectionName)\n const refs: CollectionDescriptor['refs'] = {}\n for (const [name, desc] of Object.entries(refsRaw)) {\n refs[name] = { target: desc.target, mode: desc.mode, ...(desc.isArray ? { isArray: true } : {}) }\n }\n\n // 1. Try the persisted Route B envelope first.\n try {\n const dek = await state.getDEK(collectionName)\n const persisted = await loadPersistedSchema(state.adapter, state.name, collectionName, dek)\n if (persisted) {\n validator = { kind: persisted.kind, source: 'persisted' }\n if (persisted.jsonSchema) {\n fields = jsonSchemaToFields(persisted.jsonSchema, 'persisted', refsRaw)\n }\n }\n } catch {\n // No DEK or decrypt failure — fall through to live-validator\n }\n\n const liveColl = state.collectionCache.get(collectionName)\n\n // 2. Try the live in-process validator (when no persisted envelope).\n if (!validator) {\n const schema = liveColl?.getSchema()\n if (schema) {\n try {\n const derived = await derivePersistedSchema(schema)\n validator = { kind: derived.kind, source: 'live-validator' }\n if (derived.jsonSchema) {\n fields = jsonSchemaToFields(derived.jsonSchema, 'live-validator', refsRaw)\n }\n } catch {\n // Derivation failed (e.g. missing peer-dep) — silently leave fields empty\n }\n }\n }\n\n // 3. Sampling fallback — deferred to a follow-up. For now: empty when no schema.\n if (Object.keys(fields).length === 0 && sampleSize > 0) {\n // Sampling path not implemented in baseline slice 2; reserved for follow-up.\n }\n\n // Populate collection-level meta and config from the live collection when available.\n const collMeta = liveColl?.getMeta()\n const collConfig = liveColl?.getConfig()\n\n // Thread vault-level config (archive + schemaUpdate) that the Collection cannot see.\n const archivePresent = state.hasCollectionArchive?.(collectionName) === true\n const schemaUpdateNames = state.getCollectionSchemaUpdateNames?.(collectionName)\n const hasSchemaUpdate = schemaUpdateNames !== undefined && schemaUpdateNames.length > 0\n\n // Merge Collection-level config with vault-level fields. Omit config entirely\n // when all sources are empty.\n let mergedConfig: CollectionConfig | undefined\n if (collConfig !== undefined || archivePresent || hasSchemaUpdate) {\n mergedConfig = {\n ...(collConfig ?? {}),\n ...(archivePresent ? { archive: true as const } : {}),\n ...(hasSchemaUpdate ? { schemaUpdate: schemaUpdateNames } : {}),\n }\n }\n\n const descriptor: CollectionDescriptor = {\n fields,\n indexes: liveColl?.getDeclaredIndexes() ?? [],\n refs,\n ...(validator ? { validator } : {}),\n ...(collMeta !== undefined ? { meta: collMeta } : {}),\n ...(mergedConfig !== undefined ? { config: mergedConfig } : {}),\n }\n if (withStats) {\n const stats = await statsForCollection(state.adapter, state.name, collectionName)\n ;(descriptor as { stats?: CollectionStats }).stats = stats\n }\n return descriptor\n}\n\nasync function statsForCollection(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n): Promise<CollectionStats> {\n const ids = await adapter.list(vault, collection)\n if (ids.length === 0) {\n return { records: 0, bytes: 0, bytesAvg: 0, bytesMin: 0, bytesMax: 0, oldest: '', newest: '' }\n }\n let total = 0\n let min = Number.POSITIVE_INFINITY\n let max = 0\n let oldest = '￿'\n let newest = ''\n for (const id of ids) {\n const env = await adapter.get(vault, collection, id)\n if (!env) continue\n const size = env._data.length\n total += size\n if (size < min) min = size\n if (size > max) max = size\n if (env._ts < oldest) oldest = env._ts\n if (env._ts > newest) newest = env._ts\n }\n return {\n records: ids.length,\n bytes: total,\n bytesAvg: Math.round(total / ids.length),\n bytesMin: min === Number.POSITIVE_INFINITY ? 0 : min,\n bytesMax: max,\n oldest: oldest === '￿' ? '' : oldest,\n newest,\n }\n}\n\nfunction describeMVs(registry: unknown): Record<string, MaterializedViewDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const items = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, MaterializedViewDescriptor> = {}\n // Each item is RegisteredMV { spec, dependencies, outputCollection, ... }\n for (const item of items) {\n const reg = item as {\n spec?: {\n name?: string\n unionSources?: ReadonlyArray<{ collection: string }>\n groupBy?: string | ReadonlyArray<string>\n aggregate?: Record<string, unknown>\n refresh?: string\n }\n dependencies?: ReadonlySet<string>\n }\n const spec = reg.spec\n if (!spec?.name) continue\n const sources = spec.unionSources\n ? spec.unionSources.map((u) => u.collection)\n : (reg.dependencies ? [...reg.dependencies].sort() : [])\n const groupBy = spec.groupBy\n ? Array.isArray(spec.groupBy) ? [...spec.groupBy] : [spec.groupBy]\n : undefined\n const aggregate = spec.aggregate ? Object.fromEntries(\n Object.entries(spec.aggregate).map(([k, v]) => [k, summariseAggregateOp(v)]),\n ) : undefined\n out[spec.name] = {\n sources,\n ...(groupBy ? { groupBy } : {}),\n ...(aggregate ? { aggregate } : {}),\n refresh: spec.refresh ?? 'eager',\n }\n }\n return out\n}\n\nfunction describeOverlays(registry: unknown): Record<string, OverlayViewDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const specs = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, OverlayViewDescriptor> = {}\n for (const spec of specs) {\n const s = spec as { name?: string; base?: string; overlay?: string }\n if (!s.name || !s.base || !s.overlay) continue\n out[s.name] = { base: s.base, overlay: s.overlay }\n }\n return out\n}\n\nfunction describeDerivations(registry: unknown): Record<string, DerivationDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const items = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, DerivationDescriptor> = {}\n const used = new Set<string>()\n // Pre-seed with every explicit name BEFORE keying any derivation, so an\n // unnamed derivation's fallback key never collides with (and clobbers) a\n // named one, regardless of registration order (#973).\n for (const item of items) {\n const reg = item as { spec?: { name?: string }; name?: string }\n const explicitName = (reg.spec ?? reg).name\n if (explicitName !== undefined) used.add(explicitName)\n }\n for (const item of items) {\n // `all()` on DerivationRegistry returns RegisteredStrategy objects:\n // { spec: DerivationSpec, strategyHash }. Read `spec` and fall\n // back to the item itself for forward-compat with other registries.\n const reg = item as { spec?: { name?: string; source?: string; outputs?: Record<string, { collection: string }> }; name?: string; source?: string; outputs?: Record<string, { collection: string }> }\n const s = reg.spec ?? reg\n if (!s.source) continue\n const outputCollections = s.outputs\n ? Object.values(s.outputs).map((o) => (o as { collection: string }).collection)\n : []\n // Named derivations key by their (vault-unique) `name`. Unnamed ones key\n // by the shared collision-safe fallback (sorted output collections, else\n // source, with a `#occurrence` suffix on collision) — the SAME algorithm\n // `listBehaviors` uses (`./derivation-key.ts`) so both surfaces key an\n // unnamed derivation identically.\n const key = s.name ?? fallbackDerivationName(outputCollections, s.source, used)\n if (s.name === undefined) used.add(key)\n out[key] = {\n source: s.source,\n outputs: outputCollections,\n ...(s.name !== undefined ? { name: s.name } : {}),\n }\n }\n return out\n}\n\nfunction listFromRegistry(reg: Record<string, unknown>): readonly unknown[] {\n // Registries expose different accessor methods; try the common ones.\n for (const method of ['all', 'list', 'specs', 'values']) {\n const fn = reg[method]\n if (typeof fn === 'function') {\n try {\n const out = (fn as () => unknown).call(reg)\n if (Array.isArray(out)) return out\n if (out && typeof (out as { values?: () => unknown }).values === 'function') {\n return [...(out as { values: () => Iterable<unknown> }).values()]\n }\n } catch {\n continue\n }\n }\n }\n return []\n}\n\nfunction summariseAggregateOp(value: unknown): string {\n if (value && typeof value === 'object') {\n const op = (value as { op?: string; kind?: string; field?: string }).op\n ?? (value as { kind?: string }).kind\n const field = (value as { field?: string }).field\n if (op && field) return `${op}(${field})`\n if (op) return op\n }\n return String(value)\n}\n"],"mappings":"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","names":[]}
@@ -92,21 +92,7 @@ function jsonSchemaToFields(jsonSchema, source, refs) {
92
92
  return fields;
93
93
  }
94
94
 
95
- // src/with-shape/introspection/derivation-key.ts
96
- function fallbackDerivationName(outputCollections, source, used) {
97
- const base = outputCollections.length > 0 ? [...outputCollections].sort().join("+") : source;
98
- if (!used.has(base)) return base;
99
- let occurrence = 1;
100
- let candidate = `${base}#${occurrence}`;
101
- while (used.has(candidate)) {
102
- occurrence++;
103
- candidate = `${base}#${occurrence}`;
104
- }
105
- return candidate;
106
- }
107
-
108
95
  export {
109
- jsonSchemaToFields,
110
- fallbackDerivationName
96
+ jsonSchemaToFields
111
97
  };
112
- //# sourceMappingURL=chunk-KHKKD62N.js.map
98
+ //# sourceMappingURL=chunk-VYYM3MEN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-shape/introspection/fields.ts"],"sourcesContent":["/**\n * Map a JSON Schema (Draft 2020-12 produced by `zod-to-json-schema`) into\n * the {@link FieldDescriptor} map consumed by {@link VaultSchemaSnapshot}.\n *\n * Used by both the persisted-schema path (decrypted envelope body) and the\n * live-validator path (in-process derivation).\n *\n * @module\n */\n\nimport type { FieldDescriptor, FieldSource } from './types.js'\n\ninterface JsonSchemaShape {\n type?: string | string[]\n properties?: Record<string, JsonSchemaShape>\n required?: string[]\n enum?: unknown[]\n const?: unknown\n minLength?: number\n maxLength?: number\n pattern?: string\n minimum?: number\n maximum?: number\n exclusiveMinimum?: number\n exclusiveMaximum?: number\n format?: string\n items?: JsonSchemaShape\n oneOf?: JsonSchemaShape[]\n anyOf?: JsonSchemaShape[]\n}\n\nfunction jsonSchemaType(node: JsonSchemaShape): string {\n if (Array.isArray(node.type)) {\n const non = node.type.filter((t) => t !== 'null')\n return non[0] ?? 'opaque'\n }\n if (node.enum && Array.isArray(node.enum)) return 'enum'\n // JSON Schema `const` is used by zod-to-json-schema for z.literal(...)\n if (node.const !== undefined) return 'enum'\n if (typeof node.type === 'string') return node.type\n return 'opaque'\n}\n\n// #657 — zod v4's native `toJSONSchema()` injects a ±Number.MAX_SAFE_INTEGER\n// bound on every `.int()` field (a JS-safe-integer representability fact,\n// not authored validation intent — `type: 'integer'` already carries it).\n// Empirically verified: `z.number().int()` → `{type:'integer', minimum:\n// -9007199254740991, maximum:9007199254740991}`, and — critically — zod\n// emits NO metadata distinguishing that derived bound from an authored one\n// at the exact same value (`z.number().min(-9007199254740991)\n// .max(9007199254740991)` on a plain, non-int number produces a\n// byte-identical minimum/maximum). Without that distinguishing signal, a\n// value-based filter gated on `type === 'integer'` is the pragmatic call:\n// it never touches a non-int field's authored bound (whatever its value),\n// and an authored `.int().min(Number.MAX_SAFE_INTEGER)` is pathological\n// either way, so folding it into the same omission is an acceptable trade.\nfunction isIntSentinel(node: JsonSchemaShape, value: number): boolean {\n return node.type === 'integer' && Math.abs(value) === Number.MAX_SAFE_INTEGER\n}\n\nfunction constraintsFor(node: JsonSchemaShape): Record<string, unknown> | undefined {\n const out: Record<string, unknown> = {}\n if (node.enum) out.values = node.enum\n // JSON Schema `const` (used by zod-to-json-schema for z.literal) — treat like a single-value enum\n if (node.const !== undefined) out.values = [node.const]\n if (node.minLength !== undefined) out.minLength = node.minLength\n if (node.maxLength !== undefined) out.maxLength = node.maxLength\n if (node.pattern !== undefined) out.pattern = node.pattern\n if (node.format !== undefined) out.format = node.format\n if (node.minimum !== undefined && !isIntSentinel(node, node.minimum)) out.minimum = node.minimum\n if (node.maximum !== undefined && !isIntSentinel(node, node.maximum)) out.maximum = node.maximum\n if (node.exclusiveMinimum !== undefined) out.gt = node.exclusiveMinimum\n if (node.exclusiveMaximum !== undefined) out.lt = node.exclusiveMaximum\n return Object.keys(out).length === 0 ? undefined : out\n}\n\n/**\n * Extract fields from a single JSON Schema object node (must have `properties`).\n * Used by both the plain-object path and the per-member extraction in the union path.\n */\nfunction extractObjectFields(\n obj: JsonSchemaShape,\n source: FieldSource,\n refs?: Record<string, { target: string; mode: string }>,\n): { fields: Record<string, FieldDescriptor>; required: Set<string> } {\n const required = new Set(Array.isArray(obj.required) ? obj.required : [])\n const fields: Record<string, FieldDescriptor> = {}\n if (!obj.properties || typeof obj.properties !== 'object') return { fields, required }\n\n for (const [name, node] of Object.entries(obj.properties)) {\n const descriptor: FieldDescriptor = {\n type: jsonSchemaType(node),\n source,\n ...(required.has(name) ? {} : { optional: true }),\n ...(refs?.[name] ? { references: `${refs[name].target}.id` } : {}),\n }\n const constraints = constraintsFor(node)\n if (constraints) (descriptor as { constraints?: Record<string, unknown> }).constraints = constraints\n fields[name] = descriptor\n }\n\n return { fields, required }\n}\n\n/**\n * Merge field descriptors from multiple union members.\n *\n * Merge rules:\n * - Emit the UNION of all field names so no member's fields are hidden.\n * - A field is required (optional=false/absent) only when it is required in\n * EVERY member; otherwise it is marked optional=true.\n * - For the discriminator field (appears in every member with a `const` value),\n * collect the const values from all members and surface them as constraints.values\n * so the caller can see the full literal set.\n * - For conflicting types across members, first-seen wins.\n */\nfunction mergeUnionMembers(\n members: JsonSchemaShape[],\n source: FieldSource,\n refs?: Record<string, { target: string; mode: string }>,\n): Record<string, FieldDescriptor> {\n if (members.length === 0) return {}\n\n // Extract per-member fields + required sets\n const extracted = members\n .filter((m) => m.properties && typeof m.properties === 'object')\n .map((m) => extractObjectFields(m, source, refs))\n\n if (extracted.length === 0) return {}\n\n // Collect all field names across all members\n const allNames = new Set<string>()\n for (const { fields } of extracted) {\n for (const name of Object.keys(fields)) allNames.add(name)\n }\n\n const memberCount = extracted.length\n const out: Record<string, FieldDescriptor> = {}\n\n for (const name of allNames) {\n // Which members contain this field?\n const presentIn = extracted.filter(({ fields }) => name in fields)\n const requiredInAll = presentIn.length === memberCount\n && presentIn.every(({ required }) => required.has(name))\n\n // Use first-seen descriptor as the base (presentIn is always non-empty here since\n // we iterate names that appeared in at least one member's fields)\n const base = presentIn[0]!.fields[name]!\n\n // For discriminator-like fields: collect const/enum values across all members\n // to surface the full literal set in constraints.values\n const allValues: unknown[] = []\n for (const { fields } of presentIn) {\n const fd = fields[name]\n if (fd?.constraints?.values && Array.isArray(fd.constraints.values)) {\n for (const v of fd.constraints.values) {\n if (!allValues.includes(v)) allValues.push(v)\n }\n }\n }\n\n const mergedConstraints: Record<string, unknown> | undefined =\n allValues.length > 0\n ? { ...(base.constraints ?? {}), values: allValues }\n : base.constraints\n\n const descriptor: FieldDescriptor = {\n type: base.type,\n source,\n ...(requiredInAll ? {} : { optional: true }),\n ...(base.references ? { references: base.references } : {}),\n }\n if (mergedConstraints) {\n (descriptor as { constraints?: Record<string, unknown> }).constraints = mergedConstraints\n }\n out[name] = descriptor\n }\n\n return out\n}\n\nexport function jsonSchemaToFields(\n jsonSchema: unknown,\n source: FieldSource,\n refs?: Record<string, { target: string; mode: string }>,\n): Record<string, FieldDescriptor> {\n if (!jsonSchema || typeof jsonSchema !== 'object') return {}\n const root = jsonSchema as JsonSchemaShape\n\n // Handle discriminated unions: zod-to-json-schema emits anyOf/oneOf for z.discriminatedUnion\n const unionMembers = root.anyOf ?? root.oneOf\n if (Array.isArray(unionMembers) && unionMembers.length > 0) {\n return mergeUnionMembers(unionMembers, source, refs)\n }\n\n if (!root.properties || typeof root.properties !== 'object') return {}\n\n const { fields } = extractObjectFields(root, source, refs)\n return fields\n}\n"],"mappings":"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","names":[]}