@rippledb/zod 0.1.4 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,97 @@
1
1
  import { z } from 'zod';
2
+ import type { SchemaDescriptor, DescriptorSchema } from '@rippledb/core';
3
+ /**
4
+ * Helper type to validate that overrides only contain valid entities and fields.
5
+ * When used with `as const`, this helps catch extra properties even when assigned to variables.
6
+ */
7
+ type ValidateOverrides<S extends DescriptorSchema, O> = [
8
+ keyof O
9
+ ] extends [never] ? O : keyof O extends keyof S ? {
10
+ [E in keyof O]: E extends keyof S ? keyof O[E] extends keyof S[E] ? unknown : never : never;
11
+ }[keyof O] extends never ? never : O : never;
12
+ /**
13
+ * Typed overrides for Zod schemas - constrained to valid entities and fields.
14
+ */
15
+ export type ZodOverrides<S extends DescriptorSchema> = {
16
+ [E in keyof S]?: {
17
+ [F in keyof S[E]]?: z.ZodTypeAny;
18
+ };
19
+ };
20
+ /**
21
+ * Generated Zod schemas for each entity.
22
+ */
23
+ export type ZodSchemas<S extends DescriptorSchema> = {
24
+ [E in keyof S]: z.ZodObject<{
25
+ [F in keyof S[E]]: z.ZodTypeAny;
26
+ }>;
27
+ };
28
+ /**
29
+ * Schema descriptor with typed Zod schema access.
30
+ */
31
+ export type SchemaDescriptorWithZod<S extends DescriptorSchema> = SchemaDescriptor<S> & {
32
+ /**
33
+ * Auto-generated Zod schemas for each entity.
34
+ * Access via `schema.zod.entityName.parse(data)`.
35
+ */
36
+ readonly zod: ZodSchemas<S>;
37
+ };
38
+ /**
39
+ * Wraps a SchemaDescriptor with auto-generated Zod schemas.
40
+ *
41
+ * The Zod schemas are generated from the field descriptors in the schema.
42
+ * You can optionally provide overrides to customize individual field schemas
43
+ * (e.g., to add refinements like `.min()`, `.max()`, `.email()`, etc.).
44
+ *
45
+ * @param schema - The schema descriptor to wrap
46
+ * @param overrides - Optional Zod schema overrides for specific fields
47
+ * @returns A schema descriptor with typed `.zod` access
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * import { defineSchema, s } from '@rippledb/core';
52
+ * import { withZod } from '@rippledb/zod';
53
+ * import { z } from 'zod';
54
+ *
55
+ * const schema = defineSchema({
56
+ * todos: {
57
+ * id: s.string(),
58
+ * title: s.string(),
59
+ * done: s.boolean(),
60
+ * },
61
+ * users: {
62
+ * id: s.string(),
63
+ * name: s.string(),
64
+ * email: s.string(),
65
+ * },
66
+ * });
67
+ *
68
+ * // Basic usage - auto-generate all Zod schemas
69
+ * const schemaWithZod = withZod(schema);
70
+ *
71
+ * // Validate data
72
+ * const todo = schemaWithZod.zod.todos.parse({ id: '1', title: 'Test', done: false });
73
+ *
74
+ * // With overrides - add refinements
75
+ * const schemaWithRefinements = withZod(schema, {
76
+ * todos: {
77
+ * title: z.string().min(1).max(100),
78
+ * },
79
+ * users: {
80
+ * email: z.string().email(),
81
+ * },
82
+ * });
83
+ * ```
84
+ */
85
+ export declare function withZod<S extends DescriptorSchema, O extends ZodOverrides<S>>(schema: SchemaDescriptor<S>, overrides?: O & ValidateOverrides<S, O>): SchemaDescriptorWithZod<S>;
86
+ /**
87
+ * Generates Zod schemas from a schema descriptor without wrapping it.
88
+ * Use this if you only need the Zod schemas without the extended descriptor.
89
+ *
90
+ * @param schema - The schema descriptor
91
+ * @param overrides - Optional Zod schema overrides
92
+ * @returns Generated Zod schemas for each entity
93
+ */
94
+ export declare function generateZodSchemas<S extends DescriptorSchema, O extends ZodOverrides<S>>(schema: SchemaDescriptor<S>, overrides?: O & ValidateOverrides<S, O>): ZodSchemas<S>;
2
95
  /**
3
96
  * Zod schema for HLC (Hybrid Logical Clock) timestamps.
4
97
  * Format: "{wallTimeMs}:{counter}:{nodeId}"
@@ -257,4 +350,5 @@ export type PullRequestInput = z.infer<typeof pullRequestSchema>;
257
350
  export type PullResponseInput = z.infer<typeof pullResponseSchema>;
258
351
  export type AppendRequestInput = z.infer<typeof appendRequestSchema>;
259
352
  export type AppendResultInput = z.infer<typeof appendResultSchema>;
353
+ export {};
260
354
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,SAAS,aAAyD,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,gBAAgB,iCAA+B,CAAC;AAE7D;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAUtF;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;EAQvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;EAI5B,CAAC;AAEH;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC;;;;;;;;;GAK/E;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAyC,CAAC;AAEzE;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC;;;;;;;;;;;;GAMhF;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA0C,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAC;AAGH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AACjD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AACvD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,68 @@
1
1
  // src/index.ts
2
2
  import { z } from "zod";
3
+ function fieldDescriptorToZod(field) {
4
+ let schema;
5
+ switch (field._type) {
6
+ case "string":
7
+ schema = z.string();
8
+ break;
9
+ case "number":
10
+ schema = z.number();
11
+ break;
12
+ case "boolean":
13
+ schema = z.boolean();
14
+ break;
15
+ case "enum": {
16
+ const enumField = field;
17
+ if (!enumField.values || enumField.values.length === 0) {
18
+ schema = z.never();
19
+ } else if (enumField.values.length === 1) {
20
+ schema = z.literal(enumField.values[0]);
21
+ } else {
22
+ schema = z.enum(enumField.values);
23
+ }
24
+ break;
25
+ }
26
+ default:
27
+ schema = z.unknown();
28
+ }
29
+ if (field._optional) {
30
+ schema = schema.optional();
31
+ }
32
+ return schema;
33
+ }
34
+ function withZod(schema, overrides) {
35
+ const zodSchemas = {};
36
+ for (const entityName of schema.entities) {
37
+ const entityDescriptor = schema.schema[entityName];
38
+ const entityOverridesObj = overrides?.[entityName];
39
+ const shape = {};
40
+ for (const fieldName of Object.keys(entityDescriptor)) {
41
+ const fieldDescriptor = entityDescriptor[fieldName];
42
+ shape[fieldName] = entityOverridesObj?.[fieldName] ?? fieldDescriptorToZod(fieldDescriptor);
43
+ }
44
+ zodSchemas[entityName] = z.object(shape);
45
+ }
46
+ const extendedSchema = schema.extend("zod", zodSchemas);
47
+ return {
48
+ ...extendedSchema,
49
+ zod: zodSchemas
50
+ };
51
+ }
52
+ function generateZodSchemas(schema, overrides) {
53
+ const zodSchemas = {};
54
+ for (const entityName of schema.entities) {
55
+ const entityDescriptor = schema.schema[entityName];
56
+ const entityOverridesObj = overrides?.[entityName];
57
+ const shape = {};
58
+ for (const fieldName of Object.keys(entityDescriptor)) {
59
+ const fieldDescriptor = entityDescriptor[fieldName];
60
+ shape[fieldName] = entityOverridesObj?.[fieldName] ?? fieldDescriptorToZod(fieldDescriptor);
61
+ }
62
+ zodSchemas[entityName] = z.object(shape);
63
+ }
64
+ return zodSchemas;
65
+ }
3
66
  var hlcSchema = z.string().regex(/^\d+:\d+:.+$/, "Invalid HLC format");
4
67
  var changeKindSchema = z.enum(["upsert", "delete"]);
5
68
  function createChangeSchema(patchSchema) {
@@ -54,8 +117,10 @@ export {
54
117
  createAppendRequestSchema,
55
118
  createChangeSchema,
56
119
  createPullResponseSchema,
120
+ generateZodSchemas,
57
121
  hlcSchema,
58
122
  pullRequestSchema,
59
- pullResponseSchema
123
+ pullResponseSchema,
124
+ withZod
60
125
  };
61
126
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Zod schema for HLC (Hybrid Logical Clock) timestamps.\n * Format: \"{wallTimeMs}:{counter}:{nodeId}\"\n */\nexport const hlcSchema = z.string().regex(/^\\d+:\\d+:.+$/, 'Invalid HLC format');\n\n/**\n * Zod schema for change kind (upsert or delete).\n */\nexport const changeKindSchema = z.enum(['upsert', 'delete']);\n\n/**\n * Creates a Zod schema for a Change object.\n * \n * @example\n * ```ts\n * const todoChangeSchema = createChangeSchema(z.object({\n * id: z.string(),\n * title: z.string(),\n * done: z.boolean().optional(),\n * }));\n * ```\n */\nexport function createChangeSchema<T extends z.ZodObject<z.ZodRawShape>>(patchSchema: T) {\n return z.object({\n stream: z.string(),\n entity: z.string(),\n entityId: z.string(),\n kind: changeKindSchema,\n patch: patchSchema.partial(),\n tags: z.record(z.string(), hlcSchema),\n hlc: hlcSchema,\n });\n}\n\n/**\n * Generic change schema when patch structure is unknown.\n */\nexport const changeSchema = z.object({\n stream: z.string(),\n entity: z.string(),\n entityId: z.string(),\n kind: changeKindSchema,\n patch: z.record(z.string(), z.unknown()),\n tags: z.record(z.string(), hlcSchema),\n hlc: hlcSchema,\n});\n\n/**\n * Zod schema for PullRequest.\n */\nexport const pullRequestSchema = z.object({\n stream: z.string(),\n cursor: z.string().nullable(),\n limit: z.number().int().positive().optional(),\n});\n\n/**\n * Creates a Zod schema for PullResponse with typed changes.\n */\nexport function createPullResponseSchema<T extends z.ZodTypeAny>(changeSchema: T) {\n return z.object({\n changes: z.array(changeSchema),\n nextCursor: z.string().nullable(),\n });\n}\n\n/**\n * Generic pull response schema.\n */\nexport const pullResponseSchema = createPullResponseSchema(changeSchema);\n\n/**\n * Creates a Zod schema for AppendRequest with typed changes.\n */\nexport function createAppendRequestSchema<T extends z.ZodTypeAny>(changeSchema: T) {\n return z.object({\n stream: z.string(),\n idempotencyKey: z.string().optional(),\n changes: z.array(changeSchema),\n });\n}\n\n/**\n * Generic append request schema.\n */\nexport const appendRequestSchema = createAppendRequestSchema(changeSchema);\n\n/**\n * Zod schema for AppendResult.\n */\nexport const appendResultSchema = z.object({\n accepted: z.number().int().nonnegative(),\n hlc: hlcSchema.optional(),\n});\n\n// Re-export zod types for convenience\nexport type HlcInput = z.infer<typeof hlcSchema>;\nexport type ChangeKindInput = z.infer<typeof changeKindSchema>;\nexport type ChangeInput = z.infer<typeof changeSchema>;\nexport type PullRequestInput = z.infer<typeof pullRequestSchema>;\nexport type PullResponseInput = z.infer<typeof pullResponseSchema>;\nexport type AppendRequestInput = z.infer<typeof appendRequestSchema>;\nexport type AppendResultInput = z.infer<typeof appendResultSchema>;\n"],"mappings":";AAAA,SAAS,SAAS;AAMX,IAAM,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,oBAAoB;AAKvE,IAAM,mBAAmB,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC;AAcpD,SAAS,mBAAyD,aAAgB;AACvF,SAAO,EAAE,OAAO;AAAA,IACd,QAAQ,EAAE,OAAO;AAAA,IACjB,QAAQ,EAAE,OAAO;AAAA,IACjB,UAAU,EAAE,OAAO;AAAA,IACnB,MAAM;AAAA,IACN,OAAO,YAAY,QAAQ;AAAA,IAC3B,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS;AAAA,IACpC,KAAK;AAAA,EACP,CAAC;AACH;AAKO,IAAM,eAAe,EAAE,OAAO;AAAA,EACnC,QAAQ,EAAE,OAAO;AAAA,EACjB,QAAQ,EAAE,OAAO;AAAA,EACjB,UAAU,EAAE,OAAO;AAAA,EACnB,MAAM;AAAA,EACN,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACvC,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS;AAAA,EACpC,KAAK;AACP,CAAC;AAKM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,QAAQ,EAAE,OAAO;AAAA,EACjB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAC9C,CAAC;AAKM,SAAS,yBAAiDA,eAAiB;AAChF,SAAO,EAAE,OAAO;AAAA,IACd,SAAS,EAAE,MAAMA,aAAY;AAAA,IAC7B,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,CAAC;AACH;AAKO,IAAM,qBAAqB,yBAAyB,YAAY;AAKhE,SAAS,0BAAkDA,eAAiB;AACjF,SAAO,EAAE,OAAO;AAAA,IACd,QAAQ,EAAE,OAAO;AAAA,IACjB,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,IACpC,SAAS,EAAE,MAAMA,aAAY;AAAA,EAC/B,CAAC;AACH;AAKO,IAAM,sBAAsB,0BAA0B,YAAY;AAKlE,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACvC,KAAK,UAAU,SAAS;AAC1B,CAAC;","names":["changeSchema"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport type {\n SchemaDescriptor,\n DescriptorSchema,\n FieldDescriptorLike,\n} from '@rippledb/core';\n\n// ============================================================================\n// withZod - Auto-generate Zod schemas from field descriptors\n// ============================================================================\n\n/**\n * Helper type to validate that overrides only contain valid entities and fields.\n * When used with `as const`, this helps catch extra properties even when assigned to variables.\n */\ntype ValidateOverrides<S extends DescriptorSchema, O> =\n // Special case: empty overrides object is always valid\n [keyof O] extends [never]\n ? O\n : // Check that all entity keys in O are valid entities in S\n keyof O extends keyof S\n ? // For each entity, check that all field keys are valid\n {\n [E in keyof O]: E extends keyof S\n ? keyof O[E] extends keyof S[E]\n ? unknown // Valid\n : never // Invalid - has extra fields\n : never; // Invalid - entity doesn't exist\n }[keyof O] extends never\n ? never // At least one entity has invalid fields\n : O // All valid\n : never; // Has extra entities\n\n/**\n * Typed overrides for Zod schemas - constrained to valid entities and fields.\n */\nexport type ZodOverrides<S extends DescriptorSchema> = {\n [E in keyof S]?: {\n [F in keyof S[E]]?: z.ZodTypeAny;\n };\n};\n\n/**\n * Generated Zod schemas for each entity.\n */\nexport type ZodSchemas<S extends DescriptorSchema> = {\n [E in keyof S]: z.ZodObject<{\n [F in keyof S[E]]: z.ZodTypeAny;\n }>;\n};\n\n/**\n * Schema descriptor with typed Zod schema access.\n */\nexport type SchemaDescriptorWithZod<S extends DescriptorSchema> = SchemaDescriptor<S> & {\n /**\n * Auto-generated Zod schemas for each entity.\n * Access via `schema.zod.entityName.parse(data)`.\n */\n readonly zod: ZodSchemas<S>;\n};\n\n/**\n * Converts a field descriptor to a Zod schema.\n */\nfunction fieldDescriptorToZod(field: FieldDescriptorLike): z.ZodTypeAny {\n let schema: z.ZodTypeAny;\n\n switch (field._type) {\n case 'string':\n schema = z.string();\n break;\n case 'number':\n schema = z.number();\n break;\n case 'boolean':\n schema = z.boolean();\n break;\n case 'enum': {\n // Cast to access the values property on enum fields\n const enumField = field as unknown as { values: readonly string[] };\n if (!enumField.values || enumField.values.length === 0) {\n schema = z.never();\n } else if (enumField.values.length === 1) {\n schema = z.literal(enumField.values[0]);\n } else {\n schema = z.enum(enumField.values as [string, ...string[]]);\n }\n break;\n }\n default:\n schema = z.unknown();\n }\n\n if (field._optional) {\n schema = schema.optional();\n }\n\n return schema;\n}\n\n/**\n * Wraps a SchemaDescriptor with auto-generated Zod schemas.\n * \n * The Zod schemas are generated from the field descriptors in the schema.\n * You can optionally provide overrides to customize individual field schemas\n * (e.g., to add refinements like `.min()`, `.max()`, `.email()`, etc.).\n * \n * @param schema - The schema descriptor to wrap\n * @param overrides - Optional Zod schema overrides for specific fields\n * @returns A schema descriptor with typed `.zod` access\n * \n * @example\n * ```ts\n * import { defineSchema, s } from '@rippledb/core';\n * import { withZod } from '@rippledb/zod';\n * import { z } from 'zod';\n * \n * const schema = defineSchema({\n * todos: {\n * id: s.string(),\n * title: s.string(),\n * done: s.boolean(),\n * },\n * users: {\n * id: s.string(),\n * name: s.string(),\n * email: s.string(),\n * },\n * });\n * \n * // Basic usage - auto-generate all Zod schemas\n * const schemaWithZod = withZod(schema);\n * \n * // Validate data\n * const todo = schemaWithZod.zod.todos.parse({ id: '1', title: 'Test', done: false });\n * \n * // With overrides - add refinements\n * const schemaWithRefinements = withZod(schema, {\n * todos: {\n * title: z.string().min(1).max(100),\n * },\n * users: {\n * email: z.string().email(),\n * },\n * });\n * ```\n */\nexport function withZod<S extends DescriptorSchema, O extends ZodOverrides<S>>(\n schema: SchemaDescriptor<S>,\n overrides?: O & ValidateOverrides<S, O>,\n): SchemaDescriptorWithZod<S> {\n const zodSchemas = {} as Record<string, z.ZodObject<Record<string, z.ZodTypeAny>>>;\n\n for (const entityName of schema.entities) {\n const entityDescriptor = schema.schema[entityName];\n const entityOverridesObj = overrides?.[entityName as keyof S] as Record<string, z.ZodTypeAny> | undefined;\n const shape: Record<string, z.ZodTypeAny> = {};\n\n for (const fieldName of Object.keys(entityDescriptor)) {\n const fieldDescriptor = entityDescriptor[fieldName];\n // Use override if provided, otherwise generate from descriptor\n shape[fieldName] = entityOverridesObj?.[fieldName] ?? fieldDescriptorToZod(fieldDescriptor);\n }\n\n zodSchemas[entityName] = z.object(shape);\n }\n\n // Extend the schema with the zod extension\n const extendedSchema = schema.extend('zod', zodSchemas);\n\n return {\n ...extendedSchema,\n zod: zodSchemas as ZodSchemas<S>,\n };\n}\n\n/**\n * Generates Zod schemas from a schema descriptor without wrapping it.\n * Use this if you only need the Zod schemas without the extended descriptor.\n * \n * @param schema - The schema descriptor\n * @param overrides - Optional Zod schema overrides\n * @returns Generated Zod schemas for each entity\n */\nexport function generateZodSchemas<S extends DescriptorSchema, O extends ZodOverrides<S>>(\n schema: SchemaDescriptor<S>,\n overrides?: O & ValidateOverrides<S, O>,\n): ZodSchemas<S> {\n const zodSchemas = {} as Record<string, z.ZodObject<Record<string, z.ZodTypeAny>>>;\n\n for (const entityName of schema.entities) {\n const entityDescriptor = schema.schema[entityName];\n const entityOverridesObj = overrides?.[entityName as keyof S] as Record<string, z.ZodTypeAny> | undefined;\n const shape: Record<string, z.ZodTypeAny> = {};\n\n for (const fieldName of Object.keys(entityDescriptor)) {\n const fieldDescriptor = entityDescriptor[fieldName];\n shape[fieldName] = entityOverridesObj?.[fieldName] ?? fieldDescriptorToZod(fieldDescriptor);\n }\n\n zodSchemas[entityName] = z.object(shape);\n }\n\n return zodSchemas as ZodSchemas<S>;\n}\n\n// ============================================================================\n// Existing schemas for RippleDB protocol types\n// ============================================================================\n\n/**\n * Zod schema for HLC (Hybrid Logical Clock) timestamps.\n * Format: \"{wallTimeMs}:{counter}:{nodeId}\"\n */\nexport const hlcSchema = z.string().regex(/^\\d+:\\d+:.+$/, 'Invalid HLC format');\n\n/**\n * Zod schema for change kind (upsert or delete).\n */\nexport const changeKindSchema = z.enum(['upsert', 'delete']);\n\n/**\n * Creates a Zod schema for a Change object.\n * \n * @example\n * ```ts\n * const todoChangeSchema = createChangeSchema(z.object({\n * id: z.string(),\n * title: z.string(),\n * done: z.boolean().optional(),\n * }));\n * ```\n */\nexport function createChangeSchema<T extends z.ZodObject<z.ZodRawShape>>(patchSchema: T) {\n return z.object({\n stream: z.string(),\n entity: z.string(),\n entityId: z.string(),\n kind: changeKindSchema,\n patch: patchSchema.partial(),\n tags: z.record(z.string(), hlcSchema),\n hlc: hlcSchema,\n });\n}\n\n/**\n * Generic change schema when patch structure is unknown.\n */\nexport const changeSchema = z.object({\n stream: z.string(),\n entity: z.string(),\n entityId: z.string(),\n kind: changeKindSchema,\n patch: z.record(z.string(), z.unknown()),\n tags: z.record(z.string(), hlcSchema),\n hlc: hlcSchema,\n});\n\n/**\n * Zod schema for PullRequest.\n */\nexport const pullRequestSchema = z.object({\n stream: z.string(),\n cursor: z.string().nullable(),\n limit: z.number().int().positive().optional(),\n});\n\n/**\n * Creates a Zod schema for PullResponse with typed changes.\n */\nexport function createPullResponseSchema<T extends z.ZodTypeAny>(changeSchema: T) {\n return z.object({\n changes: z.array(changeSchema),\n nextCursor: z.string().nullable(),\n });\n}\n\n/**\n * Generic pull response schema.\n */\nexport const pullResponseSchema = createPullResponseSchema(changeSchema);\n\n/**\n * Creates a Zod schema for AppendRequest with typed changes.\n */\nexport function createAppendRequestSchema<T extends z.ZodTypeAny>(changeSchema: T) {\n return z.object({\n stream: z.string(),\n idempotencyKey: z.string().optional(),\n changes: z.array(changeSchema),\n });\n}\n\n/**\n * Generic append request schema.\n */\nexport const appendRequestSchema = createAppendRequestSchema(changeSchema);\n\n/**\n * Zod schema for AppendResult.\n */\nexport const appendResultSchema = z.object({\n accepted: z.number().int().nonnegative(),\n hlc: hlcSchema.optional(),\n});\n\n// Re-export zod types for convenience\nexport type HlcInput = z.infer<typeof hlcSchema>;\nexport type ChangeKindInput = z.infer<typeof changeKindSchema>;\nexport type ChangeInput = z.infer<typeof changeSchema>;\nexport type PullRequestInput = z.infer<typeof pullRequestSchema>;\nexport type PullResponseInput = z.infer<typeof pullResponseSchema>;\nexport type AppendRequestInput = z.infer<typeof appendRequestSchema>;\nexport type AppendResultInput = z.infer<typeof appendResultSchema>;\n"],"mappings":"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","names":["changeSchema"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test-d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test-d.d.ts","sourceRoot":"","sources":["../src/index.test-d.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rippledb/zod",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "Zod schemas for runtime validation of RippleDB types",
6
6
  "private": false,
@@ -32,7 +32,7 @@
32
32
  "README.md"
33
33
  ],
34
34
  "dependencies": {
35
- "@rippledb/core": "0.1.1"
35
+ "@rippledb/core": "0.2.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "zod": "^3.0.0"
@@ -43,7 +43,7 @@
43
43
  "typescript": "^5.9.3",
44
44
  "vitest": "^3.2.4",
45
45
  "zod": "^3.24.0",
46
- "@rippledb/server": "0.1.1"
46
+ "@rippledb/server": "0.1.2"
47
47
  },
48
48
  "tsup": {
49
49
  "entry": [