zerodrift 1.0.3 → 1.1.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.
Files changed (56) hide show
  1. package/README.md +15 -3
  2. package/dist/core/BaseModel.d.ts +2 -2
  3. package/dist/core/BaseModel.js +4 -4
  4. package/dist/core/BaseSSEConnection.d.ts +9 -3
  5. package/dist/core/BaseSSEConnection.js +23 -3
  6. package/dist/core/CompoundIndexFetcher.d.ts +2 -2
  7. package/dist/core/CompoundIndexFetcher.js +3 -3
  8. package/dist/core/Database.d.ts +2 -1
  9. package/dist/core/Database.js +1 -1
  10. package/dist/core/LazyCollection.d.ts +2 -2
  11. package/dist/core/LazyCollection.js +1 -1
  12. package/dist/core/LazyOwnedCollection.d.ts +2 -2
  13. package/dist/core/LazyOwnedCollection.js +1 -1
  14. package/dist/core/MemoryAdapter.d.ts +1 -1
  15. package/dist/core/MemoryAdapter.js +1 -1
  16. package/dist/core/ModelRegistry.d.ts +1 -1
  17. package/dist/core/ModelRegistry.js +2 -2
  18. package/dist/core/ModelStream.d.ts +4 -4
  19. package/dist/core/ModelStream.js +3 -3
  20. package/dist/core/ObjectPool.d.ts +2 -2
  21. package/dist/core/ObjectPool.js +2 -2
  22. package/dist/core/Store.d.ts +4 -4
  23. package/dist/core/Store.js +1 -1
  24. package/dist/core/StoreManager.d.ts +23 -12
  25. package/dist/core/StoreManager.js +25 -13
  26. package/dist/core/SyncConnection.d.ts +6 -6
  27. package/dist/core/SyncConnection.js +11 -10
  28. package/dist/core/Transaction.d.ts +2 -2
  29. package/dist/core/Transaction.js +1 -1
  30. package/dist/core/TransactionQueue.d.ts +6 -6
  31. package/dist/core/TransactionQueue.js +4 -4
  32. package/dist/core/decorators.d.ts +1 -1
  33. package/dist/core/decorators.js +4 -4
  34. package/dist/core/index.d.ts +16 -16
  35. package/dist/core/index.js +9 -9
  36. package/dist/core/internal.d.ts +13 -13
  37. package/dist/core/internal.js +11 -11
  38. package/dist/core/observability.js +1 -1
  39. package/dist/core/refAccessors.d.ts +2 -2
  40. package/dist/core/types.d.ts +1 -1
  41. package/dist/react/index.d.ts +22 -12
  42. package/dist/react/index.js +31 -23
  43. package/dist/schema/builders.d.ts +1 -1
  44. package/dist/schema/builders.js +1 -1
  45. package/dist/schema/compile.d.ts +1 -1
  46. package/dist/schema/compile.js +6 -6
  47. package/dist/schema/createStore.d.ts +5 -5
  48. package/dist/schema/createStore.js +4 -4
  49. package/dist/schema/extend.d.ts +2 -2
  50. package/dist/schema/index.d.ts +13 -13
  51. package/dist/schema/index.js +7 -7
  52. package/dist/schema/infer.d.ts +10 -1
  53. package/dist/schema/types.d.ts +1 -1
  54. package/dist/schema/zod.d.ts +125 -12
  55. package/dist/schema/zod.js +24 -2
  56. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import type { z } from "zod";
2
- import type { AnyFieldBuilder, EntityDef, FieldBuilder } from "./types";
2
+ import type { AnyFieldBuilder, EntityDef, FieldBuilder, FieldKind, FieldMeta } from "./types.js";
3
3
  /**
4
4
  * Convert any Zod schema into the equivalent schema-first `FieldBuilder`.
5
5
  * Handles the common nullable / optional / default modifiers; anything more
@@ -10,31 +10,111 @@ import type { AnyFieldBuilder, EntityDef, FieldBuilder } from "./types";
10
10
  * caller to have installed `zod`.
11
11
  */
12
12
  export declare function fromZod<Z extends z.ZodType>(zSchema: Z): FieldBuilder<z.infer<Z>>;
13
+ /**
14
+ * Maps Zod's `_zod.def.type` discriminator to a schema `FieldKind`. Mirrors
15
+ * the runtime `PRIMITIVE_KIND` map; anything not covered collapses to `"json"`,
16
+ * matching `fromZod`'s fallback.
17
+ */
18
+ type ZodKindFromTypeName<T> = T extends "string" ? "string" : T extends "number" | "int" ? "number" : T extends "boolean" ? "boolean" : T extends "date" ? "date" : "json";
19
+ /**
20
+ * Type-level analogue of `fromZod`'s runtime walker: peels off `nullable` /
21
+ * `optional` / `default` wrappers, stamping each on the accumulator, then
22
+ * collapses the leaf to a kind via `ZodKindFromTypeName`. The result is
23
+ * intersected with `FieldMeta` so `IsOptionalCreateField` sees the same
24
+ * `{kind, optional, default}` flags the runtime produces — keeping
25
+ * create-input optionality aligned for `.optional()` and `.default(...)`
26
+ * Zod fields, not just `id`.
27
+ */
28
+ type ZodToFieldMeta<Z, Accum = Record<never, never>> = Z extends {
29
+ _zod: {
30
+ def: {
31
+ type: "nullable";
32
+ innerType: infer I;
33
+ };
34
+ };
35
+ } ? ZodToFieldMeta<I, Accum & {
36
+ nullable: true;
37
+ }> : Z extends {
38
+ _zod: {
39
+ def: {
40
+ type: "optional";
41
+ innerType: infer I;
42
+ };
43
+ };
44
+ } ? ZodToFieldMeta<I, Accum & {
45
+ optional: true;
46
+ }> : Z extends {
47
+ _zod: {
48
+ def: {
49
+ type: "default";
50
+ innerType: infer I;
51
+ };
52
+ };
53
+ } ? ZodToFieldMeta<I, Accum & {
54
+ default: unknown;
55
+ }> : Z extends {
56
+ _zod: {
57
+ def: {
58
+ type: infer T extends string;
59
+ };
60
+ };
61
+ } ? Accum & {
62
+ kind: ZodKindFromTypeName<T>;
63
+ } : Accum;
64
+ /** Empty meta marker — convention-driven flags (autoIndex) layer on top.
65
+ * `id` is excluded because the storage layer treats the PK specially and
66
+ * never materializes a secondary index for it; the empty-string suffix is
67
+ * excluded so a defaulted/blank config value doesn't index every field. */
68
+ type AutoIndexMeta<K, AI extends string | undefined> = K extends "id" ? Record<never, never> : AI extends "" ? Record<never, never> : AI extends string ? K extends `${string}${AI}` ? {
69
+ indexed: true;
70
+ } : Record<never, never> : Record<never, never>;
71
+ /**
72
+ * Auto-derived `FieldBuilder` type for a Zod-object key. The `id` key is
73
+ * special-cased to carry `{kind: "id"}` (the runtime routes `id` through
74
+ * `fields.id()` regardless of the Zod-declared id type). Every other key
75
+ * walks its Zod schema via `ZodToFieldMeta` so PK, optional, and default
76
+ * flags all flow into the field's create-input optionality the same way.
77
+ * `AI` is the opts.autoIndex suffix — matching keys pick up `{indexed: true}`.
78
+ */
79
+ type AutoFieldFromZod<K, ZS, AI extends string | undefined = undefined> = K extends "id" ? FieldBuilder<string, FieldMeta & {
80
+ kind: "id";
81
+ } & AutoIndexMeta<K, AI>> : ZS extends z.ZodType ? FieldBuilder<z.infer<ZS>, FieldMeta & ZodToFieldMeta<ZS> & AutoIndexMeta<K, AI>> : FieldBuilder<unknown, FieldMeta & {
82
+ kind: FieldKind;
83
+ }>;
13
84
  /**
14
85
  * Per-field override for `entityFromZod`. Either a chaining function
15
86
  * (modifies the auto-derived `FieldBuilder`) or a full `FieldBuilder`
16
87
  * (replaces it — useful for FKs and other shapes Zod can't model).
17
88
  */
18
89
  export type EntityFromZodFieldOverride<AutoT = unknown> = AnyFieldBuilder | ((auto: FieldBuilder<AutoT>) => AnyFieldBuilder);
19
- type EntityFromZodFieldOverrides<Z extends z.ZodObject> = {
20
- [K in keyof z.infer<Z> & string]?: AnyFieldBuilder | ((auto: FieldBuilder<z.infer<Z>[K]>) => unknown);
90
+ type EntityFromZodFieldOverrides<Z extends z.ZodObject, AI extends string | undefined = undefined> = {
91
+ [K in keyof Z["shape"] & string]?: AnyFieldBuilder | ((auto: AutoFieldFromZod<K, Z["shape"][K], AI>) => unknown);
92
+ };
93
+ /**
94
+ * Maps any `opts.fields` key that isn't declared on the Zod object to a
95
+ * branded error-string type. The intersection with the override map then
96
+ * forces TS to surface "is not assignable to type \"Error: 'foo' is not a
97
+ * field…\"" — naming the offender — instead of the bare "not assignable to
98
+ * type 'never'" the previous `Record<…, never>` form produced.
99
+ */
100
+ type NoExtraZodFieldKeys<Z extends z.ZodObject, F> = {
101
+ [K in keyof F as K extends keyof Z["shape"] ? never : K]: `Error: '${K & string}' is not a field declared on the Zod object passed to entityFromZod`;
21
102
  };
22
- type NoExtraZodFieldKeys<Z extends z.ZodObject, F> = Record<Exclude<keyof F, keyof z.infer<Z> & string>, never>;
23
103
  /**
24
104
  * Resolve the field type contributed by an override entry. Functions are
25
105
  * unwrapped via their inferred return type so chained modifiers like
26
106
  * `.indexed()` carry their narrowed `M` into the entity's inferred fields;
27
107
  * direct `FieldBuilder` overrides are used as-is. When no override is
28
- * provided the auto-derived `FieldBuilder<AutoT>` from Zod stands.
108
+ * provided the auto-derived `Auto` field stands.
29
109
  */
30
- type FieldFromOverride<O, AutoT> = O extends (...args: never[]) => infer R ? R extends FieldBuilder<infer RT, infer RM> ? [unknown] extends [RT] ? FieldBuilder<AutoT, RM> : R : FieldBuilder<AutoT> : O extends AnyFieldBuilder ? O : FieldBuilder<AutoT>;
110
+ type FieldFromOverride<O, Auto> = O extends (...args: never[]) => infer R ? R extends FieldBuilder<infer RT, infer RM> ? [unknown] extends [RT] ? Auto extends FieldBuilder<infer AT, FieldMeta> ? FieldBuilder<AT, RM> : never : R : Auto : O extends AnyFieldBuilder ? O : Auto;
31
111
  /**
32
112
  * Per-key merge of the Zod-inferred fields with `opts.fields` overrides.
33
113
  * Override metadata (`.indexed()`, refId target, …) propagates into the
34
114
  * entity's TS type so downstream helpers like `IndexedFieldKeys` see them.
35
115
  */
36
- type MergedFieldsFromZodObject<Z extends z.ZodObject, F> = {
37
- [K in keyof z.infer<Z>]: K extends keyof F ? FieldFromOverride<F[K], z.infer<Z>[K]> : FieldBuilder<z.infer<Z>[K]>;
116
+ type MergedFieldsFromZodObject<Z extends z.ZodObject, F, Om extends readonly string[] = readonly [], AI extends string | undefined = undefined> = {
117
+ [K in keyof Z["shape"] as K extends Om[number] ? never : K]: K extends keyof F ? FieldFromOverride<F[K], AutoFieldFromZod<K, Z["shape"][K], AI>> : AutoFieldFromZod<K, Z["shape"][K], AI>;
38
118
  };
39
119
  /** Non-`fields` portion of the opts — shared across the public type and
40
120
  * the function's inferred-`F` signature. Tracks `EntityDef` so any new
@@ -56,8 +136,15 @@ export interface EntityFromZodOpts<Z extends z.ZodObject = z.ZodObject> extends
56
136
  * });
57
137
  */
58
138
  fields?: {
59
- [K in keyof z.infer<Z> & string]?: EntityFromZodFieldOverride<z.infer<Z>[K]>;
139
+ [K in keyof Z["shape"] & string]?: AnyFieldBuilder | ((auto: AutoFieldFromZod<K, Z["shape"][K]>) => AnyFieldBuilder);
60
140
  };
141
+ /** Fields whose Zod name ends with this suffix pick up `.indexed()` —
142
+ * intended for the common FK-naming convention (`/ID$/` → `autoIndex: "ID"`).
143
+ * Suppressed for any key that supplies a builder-form override. */
144
+ autoIndex?: string;
145
+ /** Field names to drop entirely from the produced entity. Common when the
146
+ * source Zod was generated from a DTO that carries transport-only keys. */
147
+ omit?: readonly string[];
61
148
  }
62
149
  /**
63
150
  * Convert a `z.object({ ... })` into an `EntityDef`. Each key on the Zod
@@ -84,7 +171,33 @@ export interface EntityFromZodOpts<Z extends z.ZodObject = z.ZodObject> extends
84
171
  * shape and validation; `link(...)` remains the source of truth for the
85
172
  * graph.
86
173
  */
87
- export declare function entityFromZod<Z extends z.ZodObject, const F = Record<never, never>>(zSchema: Z, opts: EntityFromZodOptsBase & {
88
- fields?: F & EntityFromZodFieldOverrides<Z> & NoExtraZodFieldKeys<Z, F>;
89
- }): EntityDef<MergedFieldsFromZodObject<Z, F>>;
174
+ export declare function entityFromZod<Z extends z.ZodObject, const F = Record<never, never>, const Om extends readonly (keyof Z["shape"] & string)[] = readonly [], const AI extends string | undefined = undefined>(zSchema: Z, opts: EntityFromZodOptsBase & {
175
+ fields?: F & EntityFromZodFieldOverrides<Z, AI> & NoExtraZodFieldKeys<Z, F>;
176
+ autoIndex?: AI;
177
+ omit?: Om;
178
+ }): EntityDef<MergedFieldsFromZodObject<Z, F, Om, AI>>;
179
+ /**
180
+ * Map a whole `{key: ZodObject}` module (e.g. an OpenAPI-generated barrel)
181
+ * into an entities record by calling `entityFromZod` per key with shared
182
+ * opts. The entity key in the returned record is the input key; the registry
183
+ * name is auto-derived (PascalCase of the key) by `compileSchema` — no need
184
+ * to spell `name` per entity.
185
+ *
186
+ * const entities = entitiesFromZod(generatedZods, {
187
+ * loadStrategy: LoadStrategy.Eager,
188
+ * autoIndex: "ID",
189
+ * omit: ["createdAt", "updatedAt"],
190
+ * });
191
+ * const schema = defineSchema({ entities, links: { ... } });
192
+ *
193
+ * Per-entity overrides are not threaded — drop down to `entityFromZod` for
194
+ * the handful that need a custom `fields` map or distinct `loadStrategy`.
195
+ */
196
+ type EntitiesFromZodResult<Zods extends Record<string, z.ZodObject>, Om extends readonly string[], AI extends string | undefined> = {
197
+ [K in keyof Zods]: EntityDef<MergedFieldsFromZodObject<Zods[K], Record<never, never>, Om, AI>>;
198
+ };
199
+ export declare function entitiesFromZod<Zods extends Record<string, z.ZodObject>, const Om extends readonly string[] = readonly [], const AI extends string | undefined = undefined>(zods: Zods, opts: Pick<EntityFromZodOptsBase, "loadStrategy" | "usedForPartialIndexes"> & {
200
+ autoIndex?: AI;
201
+ omit?: Om;
202
+ }): EntitiesFromZodResult<Zods, Om, AI>;
90
203
  export {};
@@ -1,4 +1,4 @@
1
- import { fields, entity, rebuildFieldBuilder } from "./builders";
1
+ import { fields, entity, rebuildFieldBuilder } from "./builders.js";
2
2
  const PRIMITIVE_KIND = new Map([
3
3
  ["string", fields.string],
4
4
  ["number", fields.number],
@@ -82,9 +82,19 @@ export function fromZod(zSchema) {
82
82
  */
83
83
  export function entityFromZod(zSchema, opts) {
84
84
  const overrides = (opts.fields ?? {});
85
+ const omitted = new Set(opts.omit ?? []);
86
+ const autoIndexSuffix = opts.autoIndex != null && opts.autoIndex !== "" ? opts.autoIndex : null;
85
87
  const fieldsRecord = {};
86
88
  for (const [key, fieldSchema] of Object.entries(zSchema.shape)) {
87
- const auto = key === "id" ? fields.id() : fromZod(fieldSchema);
89
+ if (omitted.has(key)) {
90
+ continue;
91
+ }
92
+ let auto = key === "id" ? fields.id() : fromZod(fieldSchema);
93
+ if (autoIndexSuffix != null &&
94
+ key !== "id" &&
95
+ key.endsWith(autoIndexSuffix)) {
96
+ auto = auto.indexed();
97
+ }
88
98
  const override = overrides[key];
89
99
  fieldsRecord[key] =
90
100
  typeof override === "function"
@@ -99,3 +109,15 @@ export function entityFromZod(zSchema, opts) {
99
109
  fields: fieldsRecord,
100
110
  });
101
111
  }
112
+ export function entitiesFromZod(zods, opts) {
113
+ const out = {};
114
+ for (const [key, zod] of Object.entries(zods)) {
115
+ out[key] = entityFromZod(zod, {
116
+ loadStrategy: opts.loadStrategy,
117
+ usedForPartialIndexes: opts.usedForPartialIndexes,
118
+ autoIndex: opts.autoIndex,
119
+ omit: opts.omit,
120
+ });
121
+ }
122
+ return out;
123
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zerodrift",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "description": "A TypeScript local-first sync engine: synchronous in-memory reads, optimistic writes, realtime SSE sync, offline IndexedDB persistence. Runs in the browser and in Node.",
6
6
  "license": "MIT",