@voidhash/mimic 1.0.0-beta.13 → 1.0.0-beta.15

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Struct.mjs","names":["OperationDefinition.make","snapshot: Record<string, unknown>","Operation.fromDefinition","newState: InferStructState<TFields>","initialState: Record<string, unknown>","OperationPath.pathsOverlap","partialFields: Record<string, AnyPrimitive>"],"sources":["../../src/primitives/Struct.ts"],"sourcesContent":["import { Schema } from \"effect\";\nimport * as OperationDefinition from \"../OperationDefinition\";\nimport * as Operation from \"../Operation\";\nimport * as OperationPath from \"../OperationPath\";\nimport * as ProxyEnvironment from \"../ProxyEnvironment\";\nimport * as Transform from \"../Transform\";\nimport type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot, NeedsValue, InferUpdateInput, InferSetInput } from \"../Primitive\";\nimport { ValidationError } from \"../Primitive\";\nimport { runValidators, applyDefaults } from \"./shared\";\n\n// =============================================================================\n// Struct Set Input Types\n// =============================================================================\n\n/**\n * Determines if a field is required for set() operations.\n * A field is required if: TRequired is true AND THasDefault is false\n */\ntype IsRequiredForSet<T> = T extends Primitive<any, any, true, false> ? true : false;\n\n/**\n * Extract keys of fields that are required for set() (required without default).\n */\ntype RequiredSetKeys<TFields extends Record<string, AnyPrimitive>> = {\n [K in keyof TFields]: IsRequiredForSet<TFields[K]> extends true ? K : never;\n}[keyof TFields];\n\n/**\n * Extract keys of fields that are optional for set() (has default OR not required).\n */\ntype OptionalSetKeys<TFields extends Record<string, AnyPrimitive>> = {\n [K in keyof TFields]: IsRequiredForSet<TFields[K]> extends true ? never : K;\n}[keyof TFields];\n\n/**\n * Compute the input type for set() operations on a struct.\n * Required fields (required without default) must be provided.\n * Optional fields (has default or not required) can be omitted.\n * Uses each field's TSetInput type to handle nested structs correctly.\n */\nexport type StructSetInput<TFields extends Record<string, AnyPrimitive>> = \n { readonly [K in RequiredSetKeys<TFields>]: InferSetInput<TFields[K]> } &\n { readonly [K in OptionalSetKeys<TFields>]?: InferSetInput<TFields[K]> };\n\n/**\n * Input type for set() - respects required/default status of the struct.\n * If the struct is required without a default, the value must be provided.\n * The value itself uses StructSetInput which handles field-level required/default logic.\n */\ntype InferStructSetInput<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean, THasDefault extends boolean> = \n NeedsValue<StructSetInput<TFields>, TRequired, THasDefault>;\n\n/**\n * Input type for update() - always partial since update only modifies specified fields.\n * For nested structs, allows recursive partial updates.\n */\ntype InferStructUpdateInput<TFields extends Record<string, AnyPrimitive>> = StructUpdateValue<TFields>;\n\n\n/**\n * Maps a schema definition to its state type.\n * { name: StringPrimitive, age: NumberPrimitive } -> { name: string, age: number }\n */\nexport type InferStructState<TFields extends Record<string, AnyPrimitive>> = {\n readonly [K in keyof TFields]: InferState<TFields[K]>;\n};\n\n/**\n * Maps a schema definition to its snapshot type.\n * Each field's snapshot type is inferred from the field primitive.\n */\nexport type InferStructSnapshot<TFields extends Record<string, AnyPrimitive>> = {\n readonly [K in keyof TFields]: InferSnapshot<TFields[K]>;\n};\n\n/**\n * Maps a schema definition to a partial update type.\n * Uses each field's TUpdateInput type, which handles nested updates recursively.\n */\nexport type StructUpdateValue<TFields extends Record<string, AnyPrimitive>> = {\n readonly [K in keyof TFields]?: InferUpdateInput<TFields[K]>;\n};\n\n/**\n * Transforms a primitive type to make it optional (TRequired = false).\n * Preserves the primitive structure while changing the required flag.\n */\nexport type MakeOptional<T extends AnyPrimitive> = T extends Primitive<infer S, infer P, any, infer H, infer SI, infer UI>\n ? Primitive<S, P, false, H, SI, UI>\n : T;\n\n/**\n * Maps each field in a struct to its optional version.\n * All fields become optional (TRequired = false).\n */\nexport type PartialFields<TFields extends Record<string, AnyPrimitive>> = {\n [K in keyof TFields]: MakeOptional<TFields[K]>;\n};\n\n/**\n * Maps a schema definition to its proxy type.\n * Provides nested field access + get()/set()/toSnapshot() methods for the whole struct.\n */\nexport type StructProxy<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean = false, THasDefault extends boolean = false> = {\n readonly [K in keyof TFields]: InferProxy<TFields[K]>;\n} & {\n /** Gets the entire struct value */\n get(): MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;\n /** Sets the entire struct value (only fields that are required without defaults must be provided) */\n set(value: InferStructSetInput<TFields, TRequired, THasDefault>): void;\n /** Updates only the specified fields (partial update, handles nested structs recursively) */\n update(value: InferStructUpdateInput<TFields>): void;\n /** Returns a readonly snapshot of the struct for rendering */\n toSnapshot(): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault>;\n};\n\ninterface StructPrimitiveSchema<TFields extends Record<string, AnyPrimitive>> {\n readonly required: boolean;\n readonly defaultValue: InferStructState<TFields> | undefined;\n readonly fields: TFields;\n readonly validators: readonly Validator<InferStructState<TFields>>[];\n}\n\nexport class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean = false, THasDefault extends boolean = false>\n implements Primitive<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>, TRequired, THasDefault, InferStructSetInput<TFields, TRequired, THasDefault>, InferStructUpdateInput<TFields>>\n{\n readonly _tag = \"StructPrimitive\" as const;\n readonly _State!: InferStructState<TFields>;\n readonly _Proxy!: StructProxy<TFields, TRequired, THasDefault>;\n readonly _TRequired!: TRequired;\n readonly _THasDefault!: THasDefault;\n readonly TSetInput!: InferStructSetInput<TFields, TRequired, THasDefault>;\n readonly TUpdateInput!: InferStructUpdateInput<TFields>;\n\n private readonly _schema: StructPrimitiveSchema<TFields>;\n\n private readonly _opDefinitions = {\n set: OperationDefinition.make({\n kind: \"struct.set\" as const,\n payload: Schema.Unknown,\n target: Schema.Unknown,\n apply: (payload) => payload,\n deduplicable: true,\n }),\n };\n\n constructor(schema: StructPrimitiveSchema<TFields>) {\n this._schema = schema;\n }\n\n /** Mark this struct as required */\n required(): StructPrimitive<TFields, true, THasDefault> {\n return new StructPrimitive({\n ...this._schema,\n required: true,\n });\n }\n\n /** Set a default value for this struct */\n default(defaultValue: StructSetInput<TFields>): StructPrimitive<TFields, TRequired, true> {\n // Apply defaults to the provided value\n const merged = applyDefaults(this as AnyPrimitive, defaultValue as Partial<InferStructState<TFields>>) as InferStructState<TFields>;\n return new StructPrimitive({\n ...this._schema,\n defaultValue: merged,\n });\n }\n\n /** Get the fields schema */\n get fields(): TFields {\n return this._schema.fields;\n }\n\n /** Add a custom validation rule (useful for cross-field validation) */\n refine(fn: (value: InferStructState<TFields>) => boolean, message: string): StructPrimitive<TFields, TRequired, THasDefault> {\n return new StructPrimitive({\n ...this._schema,\n validators: [...this._schema.validators, { validate: fn, message }],\n });\n }\n\n /** Extend this struct with additional fields */\n extend<TNewFields extends Record<string, AnyPrimitive>>(\n newFields: TNewFields\n ): StructPrimitive<TFields & TNewFields, TRequired, THasDefault> {\n return new StructPrimitive<TFields & TNewFields, TRequired, THasDefault>({\n required: this._schema.required,\n defaultValue: undefined,\n fields: { ...this._schema.fields, ...newFields } as TFields & TNewFields,\n validators: [],\n });\n }\n\n /** Make all properties of this struct optional (TRequired = false for all fields) */\n partial(): StructPrimitive<PartialFields<TFields>, TRequired, THasDefault> {\n const partialFields: Record<string, AnyPrimitive> = {};\n for (const key in this._schema.fields) {\n const field = this._schema.fields[key]!;\n // Create a new field that is not required (optional)\n partialFields[key] = this._makeFieldOptional(field);\n }\n return new StructPrimitive<PartialFields<TFields>, TRequired, THasDefault>({\n required: this._schema.required,\n defaultValue: undefined,\n fields: partialFields as PartialFields<TFields>,\n validators: [],\n });\n }\n\n private _makeFieldOptional(field: AnyPrimitive): AnyPrimitive {\n // Create a new primitive with required: false\n // We access the _schema property if available, otherwise return as-is\n const fieldAny = field as any;\n if (fieldAny._schema && typeof fieldAny._schema === \"object\") {\n const Constructor = field.constructor as new (schema: any) => AnyPrimitive;\n return new Constructor({\n ...fieldAny._schema,\n required: false,\n });\n }\n return field;\n }\n\n readonly _internal: PrimitiveInternal<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>> = {\n createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StructProxy<TFields, TRequired, THasDefault> => {\n const fields = this._schema.fields;\n const defaultValue = this._schema.defaultValue;\n\n // Helper to build a snapshot by calling toSnapshot on each field\n const buildSnapshot = (): InferStructSnapshot<TFields> | undefined => {\n const state = env.getState(operationPath);\n \n // Build snapshot from field proxies (they handle their own defaults)\n const snapshot: Record<string, unknown> = {};\n let hasAnyDefinedField = false;\n \n for (const key in fields) {\n const fieldPrimitive = fields[key]!;\n const fieldPath = operationPath.append(key);\n const fieldProxy = fieldPrimitive._internal.createProxy(env, fieldPath);\n const fieldSnapshot = (fieldProxy as { toSnapshot(): unknown }).toSnapshot();\n snapshot[key] = fieldSnapshot;\n if (fieldSnapshot !== undefined) {\n hasAnyDefinedField = true;\n }\n }\n \n // Return undefined only if there's no state, no struct default, and no field snapshots\n if (state === undefined && defaultValue === undefined && !hasAnyDefinedField) {\n return undefined;\n }\n \n return snapshot as InferStructSnapshot<TFields>;\n };\n\n // Create the base object with get/set/update/toSnapshot methods\n const base = {\n get: (): MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault> => {\n const state = env.getState(operationPath) as InferStructState<TFields> | undefined;\n return (state ?? defaultValue) as MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;\n },\n set: (value: InferStructSetInput<TFields, TRequired, THasDefault>) => {\n // Apply defaults for missing fields\n const merged = applyDefaults(this as AnyPrimitive, value as Partial<InferStructState<TFields>>) as InferStructState<TFields>;\n env.addOperation(\n Operation.fromDefinition(operationPath, this._opDefinitions.set, merged)\n );\n },\n update: (value: InferStructUpdateInput<TFields>) => {\n for (const key in value) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n const fieldValue = value[key as keyof TFields];\n if (fieldValue === undefined) continue; // Skip undefined values\n\n const fieldPrimitive = fields[key as keyof TFields];\n if (!fieldPrimitive) continue; // Skip unknown fields\n\n const fieldPath = operationPath.append(key);\n const fieldProxy = fieldPrimitive._internal.createProxy(env, fieldPath);\n\n // Check if this is a nested struct and value is a plain object (partial update)\n if (\n fieldPrimitive._tag === \"StructPrimitive\" &&\n typeof fieldValue === \"object\" &&\n fieldValue !== null &&\n !Array.isArray(fieldValue)\n ) {\n // Recursively update nested struct\n (fieldProxy as { update: (v: unknown) => void }).update(fieldValue);\n } else {\n // Set the field value directly\n (fieldProxy as { set: (v: unknown) => void }).set(fieldValue);\n }\n }\n }\n },\n toSnapshot: (): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault> => {\n const snapshot = buildSnapshot();\n return snapshot as MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault>;\n },\n };\n\n // Use a JavaScript Proxy to intercept field access\n return new globalThis.Proxy(base as StructProxy<TFields, TRequired, THasDefault>, {\n get: (target, prop, _receiver) => {\n // Return base methods (get, set, update, toSnapshot)\n if (prop === \"get\") {\n return target.get;\n }\n if (prop === \"set\") {\n return target.set;\n }\n if (prop === \"update\") {\n return target.update;\n }\n if (prop === \"toSnapshot\") {\n return target.toSnapshot;\n }\n\n // Handle symbol properties (like Symbol.toStringTag)\n if (typeof prop === \"symbol\") {\n return undefined;\n }\n\n // Check if prop is a field in the schema\n if (prop in fields) {\n const fieldPrimitive = fields[prop as keyof TFields]!;\n const fieldPath = operationPath.append(prop as string);\n return fieldPrimitive._internal.createProxy(env, fieldPath);\n }\n\n return undefined;\n },\n has: (_target, prop) => {\n if (prop === \"get\" || prop === \"set\" || prop === \"update\" || prop === \"toSnapshot\") return true;\n if (typeof prop === \"string\" && prop in fields) return true;\n return false;\n },\n });\n },\n\n applyOperation: (\n state: InferStructState<TFields> | undefined,\n operation: Operation.Operation<any, any, any>\n ): InferStructState<TFields> => {\n const path = operation.path;\n const tokens = path.toTokens().filter((t: string) => t !== \"\");\n\n let newState: InferStructState<TFields>;\n\n // If path is empty or root, this is a struct.set operation\n if (tokens.length === 0) {\n if (operation.kind !== \"struct.set\") {\n throw new ValidationError(`StructPrimitive root cannot apply operation of kind: ${operation.kind}`);\n }\n\n const payload = operation.payload;\n if (typeof payload !== \"object\" || payload === null) {\n throw new ValidationError(`StructPrimitive.set requires an object payload`);\n }\n\n newState = payload as InferStructState<TFields>;\n } else {\n // Otherwise, delegate to the appropriate field primitive\n const fieldName = tokens[0] as keyof TFields;\n if (!(fieldName in this._schema.fields)) {\n throw new ValidationError(`Unknown field: ${globalThis.String(fieldName)}`);\n }\n\n const fieldPrimitive = this._schema.fields[fieldName]!;\n const remainingPath = path.shift();\n const fieldOperation = {\n ...operation,\n path: remainingPath,\n };\n\n // Get the current field state\n const currentState = state ?? ({} as InferStructState<TFields>);\n const currentFieldState = currentState[fieldName] as InferState<typeof fieldPrimitive> | undefined;\n\n // Apply the operation to the field\n const newFieldState = fieldPrimitive._internal.applyOperation(currentFieldState, fieldOperation);\n\n // Build updated state\n newState = {\n ...currentState,\n [fieldName]: newFieldState,\n };\n }\n\n // Run validators on the new state\n runValidators(newState, this._schema.validators);\n\n return newState;\n },\n\n getInitialState: (): InferStructState<TFields> | undefined => {\n if (this._schema.defaultValue !== undefined) {\n return this._schema.defaultValue;\n }\n\n // Build initial state from field defaults\n const fields = this._schema.fields;\n const initialState: Record<string, unknown> = {};\n let hasAnyDefault = false;\n\n for (const key in fields) {\n const fieldDefault = fields[key]!._internal.getInitialState();\n if (fieldDefault !== undefined) {\n initialState[key] = fieldDefault;\n hasAnyDefault = true;\n }\n }\n\n return hasAnyDefault ? (initialState as InferStructState<TFields>) : undefined;\n },\n\n transformOperation: (\n clientOp: Operation.Operation<any, any, any>,\n serverOp: Operation.Operation<any, any, any>\n ): Transform.TransformResult => {\n const clientPath = clientOp.path;\n const serverPath = serverOp.path;\n\n // If paths don't overlap at all, no transformation needed\n if (!OperationPath.pathsOverlap(clientPath, serverPath)) {\n return { type: \"transformed\", operation: clientOp };\n }\n\n const clientTokens = clientPath.toTokens().filter((t: string) => t !== \"\");\n const serverTokens = serverPath.toTokens().filter((t: string) => t !== \"\");\n\n // If both are at root level (struct.set operations)\n if (clientTokens.length === 0 && serverTokens.length === 0) {\n // Client wins (last-write-wins)\n return { type: \"transformed\", operation: clientOp };\n }\n\n // If server set entire struct and client is updating a field\n if (serverTokens.length === 0 && serverOp.kind === \"struct.set\") {\n // Client's field operation proceeds - optimistic update\n // Server will validate/reject if needed\n return { type: \"transformed\", operation: clientOp };\n }\n\n // If client set entire struct and server is updating a field\n if (clientTokens.length === 0 && clientOp.kind === \"struct.set\") {\n // Client's struct.set supersedes server's field update\n return { type: \"transformed\", operation: clientOp };\n }\n\n // Both operations target fields\n if (clientTokens.length > 0 && serverTokens.length > 0) {\n const clientField = clientTokens[0] as keyof TFields;\n const serverField = serverTokens[0] as keyof TFields;\n\n // Different fields - no conflict\n if (clientField !== serverField) {\n return { type: \"transformed\", operation: clientOp };\n }\n\n // Same field - delegate to field primitive\n const fieldPrimitive = this._schema.fields[clientField];\n if (!fieldPrimitive) {\n return { type: \"transformed\", operation: clientOp };\n }\n\n const clientOpForField = {\n ...clientOp,\n path: clientOp.path.shift(),\n };\n const serverOpForField = {\n ...serverOp,\n path: serverOp.path.shift(),\n };\n\n const result = fieldPrimitive._internal.transformOperation(clientOpForField, serverOpForField);\n\n if (result.type === \"transformed\") {\n // Restore the original path\n return {\n type: \"transformed\",\n operation: {\n ...result.operation,\n path: clientOp.path,\n },\n };\n }\n\n return result;\n }\n\n // Default: no transformation needed\n return { type: \"transformed\", operation: clientOp };\n },\n };\n}\n\n/** Creates a new StructPrimitive with the given fields */\nexport const Struct = <TFields extends Record<string, AnyPrimitive>>(\n fields: TFields\n): StructPrimitive<TFields, false, false> =>\n new StructPrimitive({ required: false, defaultValue: undefined, fields, validators: [] });\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"Struct.mjs","names":["OperationDefinition.make","snapshot: Record<string, unknown>","Operation.fromDefinition","newState: InferStructState<TFields>","initialState: Record<string, unknown>","OperationPath.pathsOverlap","partialFields: Record<string, AnyPrimitive>"],"sources":["../../src/primitives/Struct.ts"],"sourcesContent":["import { Schema } from \"effect\";\nimport * as OperationDefinition from \"../OperationDefinition\";\nimport * as Operation from \"../Operation\";\nimport * as OperationPath from \"../OperationPath\";\nimport * as ProxyEnvironment from \"../ProxyEnvironment\";\nimport * as Transform from \"../Transform\";\nimport type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot, NeedsValue, InferUpdateInput, InferSetInput } from \"../Primitive\";\nimport { ValidationError } from \"../Primitive\";\nimport { runValidators, applyDefaults, primitiveAllowsNullValue } from \"./shared\";\n\n// =============================================================================\n// Struct Set Input Types\n// =============================================================================\n\n/**\n * Determines if a field is required for set() operations.\n * A field is required if: TRequired is true AND THasDefault is false\n */\ntype IsRequiredForSet<T> = T extends Primitive<any, any, true, false> ? true : false;\n\n/**\n * Extract keys of fields that are required for set() (required without default).\n */\ntype RequiredSetKeys<TFields extends Record<string, AnyPrimitive>> = {\n [K in keyof TFields]: IsRequiredForSet<TFields[K]> extends true ? K : never;\n}[keyof TFields];\n\n/**\n * Extract keys of fields that are optional for set() (has default OR not required).\n */\ntype OptionalSetKeys<TFields extends Record<string, AnyPrimitive>> = {\n [K in keyof TFields]: IsRequiredForSet<TFields[K]> extends true ? never : K;\n}[keyof TFields];\n\n/**\n * Compute the input type for set() operations on a struct.\n * Required fields (required without default) must be provided.\n * Optional fields (has default or not required) can be omitted.\n * Uses each field's TSetInput type to handle nested structs correctly.\n */\nexport type StructSetInput<TFields extends Record<string, AnyPrimitive>> = \n { readonly [K in RequiredSetKeys<TFields>]: InferSetInput<TFields[K]> } &\n { readonly [K in OptionalSetKeys<TFields>]?: InferSetInput<TFields[K]> };\n\n/**\n * Input type for set() - respects required/default status of the struct.\n * If the struct is required without a default, the value must be provided.\n * The value itself uses StructSetInput which handles field-level required/default logic.\n */\ntype InferStructSetInput<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean, THasDefault extends boolean> = \n NeedsValue<StructSetInput<TFields>, TRequired, THasDefault>;\n\n/**\n * Input type for update() - always partial since update only modifies specified fields.\n * For nested structs, allows recursive partial updates.\n */\ntype InferStructUpdateInput<TFields extends Record<string, AnyPrimitive>> = StructUpdateValue<TFields>;\n\n\n/**\n * Maps a schema definition to its state type.\n * { name: StringPrimitive, age: NumberPrimitive } -> { name: string, age: number }\n */\nexport type InferStructState<TFields extends Record<string, AnyPrimitive>> = {\n readonly [K in keyof TFields]: InferState<TFields[K]>;\n};\n\n/**\n * Maps a schema definition to its snapshot type.\n * Each field's snapshot type is inferred from the field primitive.\n */\nexport type InferStructSnapshot<TFields extends Record<string, AnyPrimitive>> = {\n readonly [K in keyof TFields]: InferSnapshot<TFields[K]>;\n};\n\n/**\n * Maps a schema definition to a partial update type.\n * Uses each field's TUpdateInput type, which handles nested updates recursively.\n */\nexport type StructUpdateValue<TFields extends Record<string, AnyPrimitive>> = {\n readonly [K in keyof TFields]?: InferUpdateInput<TFields[K]>;\n};\n\n/**\n * Transforms a primitive type to make it optional (TRequired = false).\n * Preserves the primitive structure while changing the required flag.\n */\nexport type MakeOptional<T extends AnyPrimitive> = T extends Primitive<infer S, infer P, any, infer H, infer SI, infer UI>\n ? Primitive<S, P, false, H, SI, UI>\n : T;\n\n/**\n * Maps each field in a struct to its optional version.\n * All fields become optional (TRequired = false).\n */\nexport type PartialFields<TFields extends Record<string, AnyPrimitive>> = {\n [K in keyof TFields]: MakeOptional<TFields[K]>;\n};\n\n/**\n * Maps a schema definition to its proxy type.\n * Provides nested field access + get()/set()/toSnapshot() methods for the whole struct.\n */\nexport type StructProxy<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean = false, THasDefault extends boolean = false> = {\n readonly [K in keyof TFields]: InferProxy<TFields[K]>;\n} & {\n /** Gets the entire struct value */\n get(): MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;\n /** Sets the entire struct value (only fields that are required without defaults must be provided) */\n set(value: InferStructSetInput<TFields, TRequired, THasDefault>): void;\n /** Updates only the specified fields (partial update, handles nested structs recursively) */\n update(value: InferStructUpdateInput<TFields>): void;\n /** Returns a readonly snapshot of the struct for rendering */\n toSnapshot(): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault>;\n};\n\ninterface StructPrimitiveSchema<TFields extends Record<string, AnyPrimitive>> {\n readonly required: boolean;\n readonly defaultValue: InferStructState<TFields> | undefined;\n readonly fields: TFields;\n readonly validators: readonly Validator<InferStructState<TFields>>[];\n}\n\nexport class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean = false, THasDefault extends boolean = false>\n implements Primitive<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>, TRequired, THasDefault, InferStructSetInput<TFields, TRequired, THasDefault>, InferStructUpdateInput<TFields>>\n{\n readonly _tag = \"StructPrimitive\" as const;\n readonly _State!: InferStructState<TFields>;\n readonly _Proxy!: StructProxy<TFields, TRequired, THasDefault>;\n readonly _TRequired!: TRequired;\n readonly _THasDefault!: THasDefault;\n readonly TSetInput!: InferStructSetInput<TFields, TRequired, THasDefault>;\n readonly TUpdateInput!: InferStructUpdateInput<TFields>;\n\n private readonly _schema: StructPrimitiveSchema<TFields>;\n\n private readonly _opDefinitions = {\n set: OperationDefinition.make({\n kind: \"struct.set\" as const,\n payload: Schema.Unknown,\n target: Schema.Unknown,\n apply: (payload) => payload,\n deduplicable: true,\n }),\n unset: OperationDefinition.make({\n kind: \"struct.unset\" as const,\n payload: Schema.Unknown,\n target: Schema.Unknown,\n apply: (payload) => payload,\n deduplicable: true,\n }),\n };\n\n constructor(schema: StructPrimitiveSchema<TFields>) {\n this._schema = schema;\n }\n\n /** Mark this struct as required */\n required(): StructPrimitive<TFields, true, THasDefault> {\n return new StructPrimitive({\n ...this._schema,\n required: true,\n });\n }\n\n /** Set a default value for this struct */\n default(defaultValue: StructSetInput<TFields>): StructPrimitive<TFields, TRequired, true> {\n // Apply defaults to the provided value\n const merged = applyDefaults(this as AnyPrimitive, defaultValue as Partial<InferStructState<TFields>>) as InferStructState<TFields>;\n return new StructPrimitive({\n ...this._schema,\n defaultValue: merged,\n });\n }\n\n /** Get the fields schema */\n get fields(): TFields {\n return this._schema.fields;\n }\n\n /** Add a custom validation rule (useful for cross-field validation) */\n refine(fn: (value: InferStructState<TFields>) => boolean, message: string): StructPrimitive<TFields, TRequired, THasDefault> {\n return new StructPrimitive({\n ...this._schema,\n validators: [...this._schema.validators, { validate: fn, message }],\n });\n }\n\n /** Extend this struct with additional fields */\n extend<TNewFields extends Record<string, AnyPrimitive>>(\n newFields: TNewFields\n ): StructPrimitive<TFields & TNewFields, TRequired, THasDefault> {\n return new StructPrimitive<TFields & TNewFields, TRequired, THasDefault>({\n required: this._schema.required,\n defaultValue: undefined,\n fields: { ...this._schema.fields, ...newFields } as TFields & TNewFields,\n validators: [],\n });\n }\n\n /** Make all properties of this struct optional (TRequired = false for all fields) */\n partial(): StructPrimitive<PartialFields<TFields>, TRequired, THasDefault> {\n const partialFields: Record<string, AnyPrimitive> = {};\n for (const key in this._schema.fields) {\n const field = this._schema.fields[key]!;\n // Create a new field that is not required (optional)\n partialFields[key] = this._makeFieldOptional(field);\n }\n return new StructPrimitive<PartialFields<TFields>, TRequired, THasDefault>({\n required: this._schema.required,\n defaultValue: undefined,\n fields: partialFields as PartialFields<TFields>,\n validators: [],\n });\n }\n\n private _makeFieldOptional(field: AnyPrimitive): AnyPrimitive {\n // Create a new primitive with required: false\n // We access the _schema property if available, otherwise return as-is\n const fieldAny = field as any;\n if (fieldAny._schema && typeof fieldAny._schema === \"object\") {\n const Constructor = field.constructor as new (schema: any) => AnyPrimitive;\n return new Constructor({\n ...fieldAny._schema,\n required: false,\n });\n }\n return field;\n }\n\n private _isRequiredWithoutDefault(field: AnyPrimitive): boolean {\n const fieldDefault = field._internal.getInitialState();\n return (\n (field as { _schema?: { required?: boolean } })._schema?.required === true &&\n fieldDefault === undefined\n );\n }\n\n readonly _internal: PrimitiveInternal<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>> = {\n createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StructProxy<TFields, TRequired, THasDefault> => {\n const fields = this._schema.fields;\n const defaultValue = this._schema.defaultValue;\n\n // Helper to build a snapshot by calling toSnapshot on each field\n const buildSnapshot = (): InferStructSnapshot<TFields> | undefined => {\n const state = env.getState(operationPath);\n \n // Build snapshot from field proxies (they handle their own defaults)\n const snapshot: Record<string, unknown> = {};\n let hasAnyDefinedField = false;\n \n for (const key in fields) {\n const fieldPrimitive = fields[key]!;\n const fieldPath = operationPath.append(key);\n const fieldProxy = fieldPrimitive._internal.createProxy(env, fieldPath);\n const fieldSnapshot = (fieldProxy as { toSnapshot(): unknown }).toSnapshot();\n snapshot[key] = fieldSnapshot;\n if (fieldSnapshot !== undefined) {\n hasAnyDefinedField = true;\n }\n }\n \n // Return undefined only if there's no state, no struct default, and no field snapshots\n if (state === undefined && defaultValue === undefined && !hasAnyDefinedField) {\n return undefined;\n }\n \n return snapshot as InferStructSnapshot<TFields>;\n };\n\n // Create the base object with get/set/update/toSnapshot methods\n const base = {\n get: (): MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault> => {\n const state = env.getState(operationPath) as InferStructState<TFields> | undefined;\n return (state ?? defaultValue) as MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;\n },\n set: (value: InferStructSetInput<TFields, TRequired, THasDefault>) => {\n // Apply defaults for missing fields\n const merged = applyDefaults(this as AnyPrimitive, value as Partial<InferStructState<TFields>>) as InferStructState<TFields>;\n env.addOperation(\n Operation.fromDefinition(operationPath, this._opDefinitions.set, merged)\n );\n },\n update: (value: InferStructUpdateInput<TFields>) => {\n for (const key in value) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n const fieldValue = value[key as keyof TFields];\n const fieldPrimitive = fields[key as keyof TFields];\n if (!fieldPrimitive) continue; // Skip unknown fields\n\n const fieldPath = operationPath.append(key);\n\n const shouldUnset =\n fieldValue === undefined ||\n (fieldValue === null && !primitiveAllowsNullValue(fieldPrimitive));\n if (shouldUnset) {\n if (this._isRequiredWithoutDefault(fieldPrimitive)) {\n throw new ValidationError(`Field \"${key}\" is required and cannot be null or undefined`);\n }\n env.addOperation(\n Operation.fromDefinition(fieldPath, this._opDefinitions.unset, null)\n );\n continue;\n }\n\n const fieldProxy = fieldPrimitive._internal.createProxy(env, fieldPath);\n\n // Check if this is a nested struct and value is a plain object (partial update)\n if (\n fieldPrimitive._tag === \"StructPrimitive\" &&\n typeof fieldValue === \"object\" &&\n fieldValue !== null &&\n !Array.isArray(fieldValue)\n ) {\n // Recursively update nested struct\n (fieldProxy as { update: (v: unknown) => void }).update(fieldValue);\n } else {\n // Set the field value directly\n (fieldProxy as { set: (v: unknown) => void }).set(fieldValue);\n }\n }\n }\n },\n toSnapshot: (): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault> => {\n const snapshot = buildSnapshot();\n return snapshot as MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault>;\n },\n };\n\n // Use a JavaScript Proxy to intercept field access\n return new globalThis.Proxy(base as StructProxy<TFields, TRequired, THasDefault>, {\n get: (target, prop, _receiver) => {\n // Return base methods (get, set, update, toSnapshot)\n if (prop === \"get\") {\n return target.get;\n }\n if (prop === \"set\") {\n return target.set;\n }\n if (prop === \"update\") {\n return target.update;\n }\n if (prop === \"toSnapshot\") {\n return target.toSnapshot;\n }\n\n // Handle symbol properties (like Symbol.toStringTag)\n if (typeof prop === \"symbol\") {\n return undefined;\n }\n\n // Check if prop is a field in the schema\n if (prop in fields) {\n const fieldPrimitive = fields[prop as keyof TFields]!;\n const fieldPath = operationPath.append(prop as string);\n return fieldPrimitive._internal.createProxy(env, fieldPath);\n }\n\n return undefined;\n },\n has: (_target, prop) => {\n if (prop === \"get\" || prop === \"set\" || prop === \"update\" || prop === \"toSnapshot\") return true;\n if (typeof prop === \"string\" && prop in fields) return true;\n return false;\n },\n });\n },\n\n applyOperation: (\n state: InferStructState<TFields> | undefined,\n operation: Operation.Operation<any, any, any>\n ): InferStructState<TFields> => {\n const path = operation.path;\n const tokens = path.toTokens().filter((t: string) => t !== \"\");\n\n let newState: InferStructState<TFields>;\n\n // If path is empty or root, this is a struct.set operation\n if (tokens.length === 0) {\n if (operation.kind !== \"struct.set\") {\n throw new ValidationError(`StructPrimitive root cannot apply operation of kind: ${operation.kind}`);\n }\n\n const payload = operation.payload;\n if (typeof payload !== \"object\" || payload === null) {\n throw new ValidationError(`StructPrimitive.set requires an object payload`);\n }\n\n newState = payload as InferStructState<TFields>;\n } else {\n // Otherwise, delegate to the appropriate field primitive\n const fieldName = tokens[0] as keyof TFields;\n if (!(fieldName in this._schema.fields)) {\n throw new ValidationError(`Unknown field: ${globalThis.String(fieldName)}`);\n }\n\n const fieldPrimitive = this._schema.fields[fieldName]!;\n // Get the current field state\n const currentState = state ?? ({} as InferStructState<TFields>);\n if (operation.kind === \"struct.unset\" && tokens.length === 1) {\n if (this._isRequiredWithoutDefault(fieldPrimitive)) {\n throw new ValidationError(`Field \"${globalThis.String(fieldName)}\" is required and cannot be removed`);\n }\n const mutableState = { ...currentState } as Record<string, unknown>;\n delete mutableState[globalThis.String(fieldName)];\n newState = mutableState as InferStructState<TFields>;\n } else {\n const remainingPath = path.shift();\n const fieldOperation = {\n ...operation,\n path: remainingPath,\n };\n const currentFieldState = currentState[fieldName] as InferState<typeof fieldPrimitive> | undefined;\n\n // Apply the operation to the field\n const newFieldState = fieldPrimitive._internal.applyOperation(currentFieldState, fieldOperation);\n\n // Build updated state\n newState = {\n ...currentState,\n [fieldName]: newFieldState,\n };\n }\n }\n\n // Run validators on the new state\n runValidators(newState, this._schema.validators);\n\n return newState;\n },\n\n getInitialState: (): InferStructState<TFields> | undefined => {\n if (this._schema.defaultValue !== undefined) {\n return this._schema.defaultValue;\n }\n\n // Build initial state from field defaults\n const fields = this._schema.fields;\n const initialState: Record<string, unknown> = {};\n let hasAnyDefault = false;\n\n for (const key in fields) {\n const fieldDefault = fields[key]!._internal.getInitialState();\n if (fieldDefault !== undefined) {\n initialState[key] = fieldDefault;\n hasAnyDefault = true;\n }\n }\n\n return hasAnyDefault ? (initialState as InferStructState<TFields>) : undefined;\n },\n\n transformOperation: (\n clientOp: Operation.Operation<any, any, any>,\n serverOp: Operation.Operation<any, any, any>\n ): Transform.TransformResult => {\n const clientPath = clientOp.path;\n const serverPath = serverOp.path;\n\n // If paths don't overlap at all, no transformation needed\n if (!OperationPath.pathsOverlap(clientPath, serverPath)) {\n return { type: \"transformed\", operation: clientOp };\n }\n\n const clientTokens = clientPath.toTokens().filter((t: string) => t !== \"\");\n const serverTokens = serverPath.toTokens().filter((t: string) => t !== \"\");\n\n // If both are at root level (struct.set operations)\n if (clientTokens.length === 0 && serverTokens.length === 0) {\n // Client wins (last-write-wins)\n return { type: \"transformed\", operation: clientOp };\n }\n\n // If server set entire struct and client is updating a field\n if (serverTokens.length === 0 && serverOp.kind === \"struct.set\") {\n // Client's field operation proceeds - optimistic update\n // Server will validate/reject if needed\n return { type: \"transformed\", operation: clientOp };\n }\n\n // If client set entire struct and server is updating a field\n if (clientTokens.length === 0 && clientOp.kind === \"struct.set\") {\n // Client's struct.set supersedes server's field update\n return { type: \"transformed\", operation: clientOp };\n }\n\n // Both operations target fields\n if (clientTokens.length > 0 && serverTokens.length > 0) {\n const clientField = clientTokens[0] as keyof TFields;\n const serverField = serverTokens[0] as keyof TFields;\n\n // Different fields - no conflict\n if (clientField !== serverField) {\n return { type: \"transformed\", operation: clientOp };\n }\n\n // Same field - delegate to field primitive\n const fieldPrimitive = this._schema.fields[clientField];\n if (!fieldPrimitive) {\n return { type: \"transformed\", operation: clientOp };\n }\n\n const clientOpForField = {\n ...clientOp,\n path: clientOp.path.shift(),\n };\n const serverOpForField = {\n ...serverOp,\n path: serverOp.path.shift(),\n };\n\n const result = fieldPrimitive._internal.transformOperation(clientOpForField, serverOpForField);\n\n if (result.type === \"transformed\") {\n // Restore the original path\n return {\n type: \"transformed\",\n operation: {\n ...result.operation,\n path: clientOp.path,\n },\n };\n }\n\n return result;\n }\n\n // Default: no transformation needed\n return { type: \"transformed\", operation: clientOp };\n },\n };\n}\n\n/** Creates a new StructPrimitive with the given fields */\nexport const Struct = <TFields extends Record<string, AnyPrimitive>>(\n fields: TFields\n): StructPrimitive<TFields, false, false> =>\n new StructPrimitive({ required: false, defaultValue: undefined, fields, validators: [] });\n"],"mappings":"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"}
@@ -16,6 +16,19 @@ function runValidators(value, validators) {
16
16
  for (const validator of validators) if (!validator.validate(value)) throw new ValidationError(validator.message);
17
17
  }
18
18
  /**
19
+ * Returns true if a primitive can represent null as a meaningful value.
20
+ * This is used to avoid pruning explicit null values for null-capable scalar unions.
21
+ */
22
+ function primitiveAllowsNullValue(primitive) {
23
+ if (primitive._tag === "LiteralPrimitive") return primitive.literal === null;
24
+ if (primitive._tag === "EitherPrimitive") {
25
+ var _schema;
26
+ const variants = (_schema = primitive._schema) === null || _schema === void 0 ? void 0 : _schema.variants;
27
+ return Array.isArray(variants) && variants.some((variant) => primitiveAllowsNullValue(variant));
28
+ }
29
+ return false;
30
+ }
31
+ /**
19
32
  * Checks if an operation is compatible with the given operation definitions.
20
33
  * @param operation - The operation to check.
21
34
  * @param operationDefinitions - The operation definitions to check against.
@@ -41,13 +54,23 @@ function applyDefaults(primitive, value) {
41
54
  var _structPrimitive$_int;
42
55
  const structPrimitive = primitive;
43
56
  const result = require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, (_structPrimitive$_int = structPrimitive._internal.getInitialState()) !== null && _structPrimitive$_int !== void 0 ? _structPrimitive$_int : {}), value);
57
+ const inputObject = typeof value === "object" && value !== null ? value : void 0;
44
58
  for (const key in structPrimitive.fields) {
59
+ var _schema2;
45
60
  const fieldPrimitive = structPrimitive.fields[key];
46
- if (result[key] === void 0) {
47
- const fieldDefault = fieldPrimitive._internal.getInitialState();
61
+ const hasExplicitKey = inputObject !== void 0 && Object.prototype.hasOwnProperty.call(inputObject, key);
62
+ const explicitValue = hasExplicitKey ? inputObject[key] : void 0;
63
+ const fieldDefault = fieldPrimitive._internal.getInitialState();
64
+ const isRequiredWithoutDefault = ((_schema2 = fieldPrimitive._schema) === null || _schema2 === void 0 ? void 0 : _schema2.required) === true && fieldDefault === void 0;
65
+ if (hasExplicitKey && (explicitValue === void 0 || explicitValue === null && !primitiveAllowsNullValue(fieldPrimitive))) {
66
+ if (isRequiredWithoutDefault) throw new ValidationError(`Field "${key}" is required and cannot be null or undefined`);
67
+ delete result[key];
68
+ continue;
69
+ }
70
+ if (!hasExplicitKey && result[key] === void 0) {
48
71
  if (fieldDefault !== void 0) result[key] = fieldDefault;
49
- } else if (typeof result[key] === "object" && result[key] !== null) {
50
- if (fieldPrimitive._tag === "StructPrimitive" || fieldPrimitive._tag === "UnionPrimitive") result[key] = applyDefaults(fieldPrimitive, result[key]);
72
+ } else if (hasExplicitKey && typeof explicitValue === "object" && explicitValue !== null) {
73
+ if (fieldPrimitive._tag === "StructPrimitive" || fieldPrimitive._tag === "UnionPrimitive") result[key] = applyDefaults(fieldPrimitive, explicitValue);
51
74
  }
52
75
  }
53
76
  return result;
@@ -81,4 +104,5 @@ function applyDefaults(primitive, value) {
81
104
  exports.ValidationError = ValidationError;
82
105
  exports.applyDefaults = applyDefaults;
83
106
  exports.isCompatibleOperation = isCompatibleOperation;
107
+ exports.primitiveAllowsNullValue = primitiveAllowsNullValue;
84
108
  exports.runValidators = runValidators;
@@ -131,6 +131,11 @@ declare function runValidators<T>(value: T, validators: readonly {
131
131
  validate: (value: T) => boolean;
132
132
  message: string;
133
133
  }[]): void;
134
+ /**
135
+ * Returns true if a primitive can represent null as a meaningful value.
136
+ * This is used to avoid pruning explicit null values for null-capable scalar unions.
137
+ */
138
+ declare function primitiveAllowsNullValue(primitive: AnyPrimitive): boolean;
134
139
  /**
135
140
  * Checks if an operation is compatible with the given operation definitions.
136
141
  * @param operation - The operation to check.
@@ -152,5 +157,5 @@ declare function isCompatibleOperation(operation: Operation<any, any, any>, oper
152
157
  */
153
158
  declare function applyDefaults<T extends AnyPrimitive>(primitive: T, value: Partial<InferState<T>>): InferState<T>;
154
159
  //#endregion
155
- export { AnyPrimitive, HasDefault, InferProxy, InferSetInput, InferSnapshot, InferState, InferUpdateInput, IsDefined, IsRequired, MaybeUndefined, NeedsValue, Optional, Primitive, PrimitiveInternal, ValidationError, Validator, applyDefaults, isCompatibleOperation, runValidators };
160
+ export { AnyPrimitive, HasDefault, InferProxy, InferSetInput, InferSnapshot, InferState, InferUpdateInput, IsDefined, IsRequired, MaybeUndefined, NeedsValue, Optional, Primitive, PrimitiveInternal, ValidationError, Validator, applyDefaults, isCompatibleOperation, primitiveAllowsNullValue, runValidators };
156
161
  //# sourceMappingURL=shared.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.cts","names":[],"sources":["../../src/primitives/shared.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAmBA;;;;;;;AAMwB,UANP,SAMO,CAAA,MAAA,EAAA,MAAA,EAAA,kBAAA,OAAA,GAAA,KAAA,EAAA,oBAAA,OAAA,GAAA,KAAA,EAAA,YAAA,OAAA,EAAA,eAAA,OAAA,CAAA,CAAA;EACA,SAAA,IAAA,EAAA,MAAA;EACG,SAAA,MAAA,EANN,MAMM;EAAY,SAAA,MAAA,EALlB,MAKkB;EAMpB,SAAA,UAAA,EAVM,SAUW;EAEJ,SAAA,YAAA,EAXL,WAWK;EAAyC,SAAA,SAAA,EAVjD,iBAUiD,CAV/B,MAU+B,EAVvB,MAUuB,CAAA;EAAgC,SAAA,SAAA,EATjF,SASiF;EAEpE,SAAA,YAAA,EAVV,YAUU;;;;;AAqBrB,UAzBG,iBAyBH,CAAA,MAAA,EAAA,MAAA,CAAA,CAAA;EACA;EACP,SAAA,WAAA,EAAA,CAAA,GAAA,EAzBuB,gBAyBvB,EAAA,IAAA,EAzBgE,aAyBhE,EAAA,GAzBgG,MAyBhG;EAAyB;EAMpB,SAAA,cAAY,EAAA,CAAA,KAAG,EA7BQ,MA6BC,GAAA,SAAA,EAAA,SAAA,EA7B8B,SA6B9B,CAAA,GAAA,EAAA,GAAA,EAAA,GAAA,CAAA,EAAA,GA7BqE,MA6BrE;EAKxB;EAKA,SAAA,eAAU,EAAM,GAAU,GArCJ,MAqCI,GAAS,SAAA;EAMnC;;;;;AASZ;;;EAEE,SAAA,sBAAA,CAAA,EAAA,CAAA,KAAA,EAAA,OAAA,EAAA,GA7CsD,MA6CtD;EAAC;AAQH;;;;;;;EAAiK,SAAA,kBAAA,EAAA,CAAA,QAAA,EA3CnJ,SA2CmJ,CAAA,GAAA,EAAA,GAAA,EAAA,GAAA,CAAA,EAAA,QAAA,EA1CnJ,SA0CmJ,CAAA,GAAA,EAAA,GAAA,EAAA,GAAA,CAAA,EAAA,GAzC1J,eAyC0J;AAEjK;AAOA;;;AAAyI,KA5C7H,YAAA,GAAe,SA4C8G,CAAA,GAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA,CAAA;;;;AAAkB,KAvC/I,UAuC+I,CAAA,CAAA,CAAA,GAvC/H,CAuC+H,SAvCrH,SAuCqH,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA,CAAA,GAAA,CAAA,GAAA,KAAA;;AAM3J;AAOA;AAKY,KApDA,UAoDS,CAAA,CAAA,CAAA,GApDO,CAoDP,SApDiB,SAoDQ,CAAA,GAAA,EAAA,KAAA,EAAA,EAAA,GAAA,EAAA,GAAA,CAAA,GAAA,CAAA,GAAA,KAAA;AAM9C;AAOA;AAeA;AASF;AAcgB,KAjGF,aAiGE,CAAA,CAAA,CAAqB,GAhGjC,CAgGiC,SAhGvB,SAgGuB,CAAA,GAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA,EAAA,KAAA,EAAA,EAAA,GAAA,CAAA,GAAA,CAAA,GA/FjC,CA+FiC,SAAA;EAAY,SAAA,EAAA,KAAA,EAAA;CAAyE,GAAA,CAAA,GAAA,KAAA;;;AAqB1H;;AACa,KA9GC,gBA8GD,CAAA,CAAA,CAAA,GA7GT,CA6GS,SA7GC,SA6GD,CAAA,GAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA,EAAA,KAAA,EAAA,CAAA,GAAA,CAAA,GA5GT,CA4GS,SAAA;EACe,YAAA,EAAA,KAAA,EAAA;CAAX,GAAA,CAAA,GAAA,KAAA;;;;;;KArGH,4EAA4E,0BAA0B,4BAA4B,SAAS,KAAK,IAAI;KAEpJ,cAAc;;;;;;KAOd,wEAAwE,yBAAyB,4BAA4B,IAAI,SAAS,KAAK,SAAS;;;;;KAMxJ,mBAAmB,UAAU;;;;;;KAO7B,gBAAgB,UAAU;;;;KAK1B,eAAe,UAAU;;;;;KAMzB,gBAAgB,UAAU;cAOzB,eAAA,SAAwB,KAAA;;;;;;;UAepB;6BACY;;;;;;iBAQf,wBAAwB;oBAA4C;;;;;;;;;iBAcpE,qBAAA,YAAiC,gDAA0D,eAAe;;;;;;;;;;;;;iBAqB1G,wBAAwB,yBAC3B,UACJ,QAAQ,WAAW,MACzB,WAAW"}
1
+ {"version":3,"file":"shared.d.cts","names":[],"sources":["../../src/primitives/shared.ts"],"sourcesContent":[],"mappings":"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"}
@@ -131,6 +131,11 @@ declare function runValidators<T>(value: T, validators: readonly {
131
131
  validate: (value: T) => boolean;
132
132
  message: string;
133
133
  }[]): void;
134
+ /**
135
+ * Returns true if a primitive can represent null as a meaningful value.
136
+ * This is used to avoid pruning explicit null values for null-capable scalar unions.
137
+ */
138
+ declare function primitiveAllowsNullValue(primitive: AnyPrimitive): boolean;
134
139
  /**
135
140
  * Checks if an operation is compatible with the given operation definitions.
136
141
  * @param operation - The operation to check.
@@ -152,5 +157,5 @@ declare function isCompatibleOperation(operation: Operation<any, any, any>, oper
152
157
  */
153
158
  declare function applyDefaults<T extends AnyPrimitive>(primitive: T, value: Partial<InferState<T>>): InferState<T>;
154
159
  //#endregion
155
- export { AnyPrimitive, HasDefault, InferProxy, InferSetInput, InferSnapshot, InferState, InferUpdateInput, IsDefined, IsRequired, MaybeUndefined, NeedsValue, Optional, Primitive, PrimitiveInternal, ValidationError, Validator, applyDefaults, isCompatibleOperation, runValidators };
160
+ export { AnyPrimitive, HasDefault, InferProxy, InferSetInput, InferSnapshot, InferState, InferUpdateInput, IsDefined, IsRequired, MaybeUndefined, NeedsValue, Optional, Primitive, PrimitiveInternal, ValidationError, Validator, applyDefaults, isCompatibleOperation, primitiveAllowsNullValue, runValidators };
156
161
  //# sourceMappingURL=shared.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.mts","names":[],"sources":["../../src/primitives/shared.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.d.mts","names":[],"sources":["../../src/primitives/shared.ts"],"sourcesContent":[],"mappings":"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"}
@@ -16,6 +16,19 @@ function runValidators(value, validators) {
16
16
  for (const validator of validators) if (!validator.validate(value)) throw new ValidationError(validator.message);
17
17
  }
18
18
  /**
19
+ * Returns true if a primitive can represent null as a meaningful value.
20
+ * This is used to avoid pruning explicit null values for null-capable scalar unions.
21
+ */
22
+ function primitiveAllowsNullValue(primitive) {
23
+ if (primitive._tag === "LiteralPrimitive") return primitive.literal === null;
24
+ if (primitive._tag === "EitherPrimitive") {
25
+ var _schema;
26
+ const variants = (_schema = primitive._schema) === null || _schema === void 0 ? void 0 : _schema.variants;
27
+ return Array.isArray(variants) && variants.some((variant) => primitiveAllowsNullValue(variant));
28
+ }
29
+ return false;
30
+ }
31
+ /**
19
32
  * Checks if an operation is compatible with the given operation definitions.
20
33
  * @param operation - The operation to check.
21
34
  * @param operationDefinitions - The operation definitions to check against.
@@ -41,13 +54,23 @@ function applyDefaults(primitive, value) {
41
54
  var _structPrimitive$_int;
42
55
  const structPrimitive = primitive;
43
56
  const result = _objectSpread2(_objectSpread2({}, (_structPrimitive$_int = structPrimitive._internal.getInitialState()) !== null && _structPrimitive$_int !== void 0 ? _structPrimitive$_int : {}), value);
57
+ const inputObject = typeof value === "object" && value !== null ? value : void 0;
44
58
  for (const key in structPrimitive.fields) {
59
+ var _schema2;
45
60
  const fieldPrimitive = structPrimitive.fields[key];
46
- if (result[key] === void 0) {
47
- const fieldDefault = fieldPrimitive._internal.getInitialState();
61
+ const hasExplicitKey = inputObject !== void 0 && Object.prototype.hasOwnProperty.call(inputObject, key);
62
+ const explicitValue = hasExplicitKey ? inputObject[key] : void 0;
63
+ const fieldDefault = fieldPrimitive._internal.getInitialState();
64
+ const isRequiredWithoutDefault = ((_schema2 = fieldPrimitive._schema) === null || _schema2 === void 0 ? void 0 : _schema2.required) === true && fieldDefault === void 0;
65
+ if (hasExplicitKey && (explicitValue === void 0 || explicitValue === null && !primitiveAllowsNullValue(fieldPrimitive))) {
66
+ if (isRequiredWithoutDefault) throw new ValidationError(`Field "${key}" is required and cannot be null or undefined`);
67
+ delete result[key];
68
+ continue;
69
+ }
70
+ if (!hasExplicitKey && result[key] === void 0) {
48
71
  if (fieldDefault !== void 0) result[key] = fieldDefault;
49
- } else if (typeof result[key] === "object" && result[key] !== null) {
50
- if (fieldPrimitive._tag === "StructPrimitive" || fieldPrimitive._tag === "UnionPrimitive") result[key] = applyDefaults(fieldPrimitive, result[key]);
72
+ } else if (hasExplicitKey && typeof explicitValue === "object" && explicitValue !== null) {
73
+ if (fieldPrimitive._tag === "StructPrimitive" || fieldPrimitive._tag === "UnionPrimitive") result[key] = applyDefaults(fieldPrimitive, explicitValue);
51
74
  }
52
75
  }
53
76
  return result;
@@ -78,5 +101,5 @@ function applyDefaults(primitive, value) {
78
101
  }
79
102
 
80
103
  //#endregion
81
- export { ValidationError, applyDefaults, isCompatibleOperation, runValidators };
104
+ export { ValidationError, applyDefaults, isCompatibleOperation, primitiveAllowsNullValue, runValidators };
82
105
  //# sourceMappingURL=shared.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.mjs","names":["result: Record<string, unknown>","matchingVariantKey: string | undefined"],"sources":["../../src/primitives/shared.ts"],"sourcesContent":["import * as Operation from \"../Operation\";\nimport * as OperationDefinition from \"../OperationDefinition\";\nimport * as ProxyEnvironment from \"../ProxyEnvironment\";\nimport * as OperationPath from \"../OperationPath\";\nimport * as Transform from \"../Transform\";\n\n// =============================================================================\n// Primitive Interface & Type Utilities\n// =============================================================================\n\n/**\n * Base interface that all primitives must implement.\n * Provides type inference helpers and internal operations.\n * \n * @typeParam TState - The state type this primitive holds\n * @typeParam TProxy - The proxy type for interacting with this primitive\n * @typeParam TDefined - Whether the value is guaranteed to be defined (via required() or default())\n * @typeParam THasDefault - Whether this primitive has a default value\n */\nexport interface Primitive<TState, TProxy, TRequired extends boolean = false, THasDefault extends boolean = false, TSetInput = unknown, TUpdateInput = unknown> {\n readonly _tag: string;\n readonly _State: TState;\n readonly _Proxy: TProxy;\n readonly _TRequired: TRequired;\n readonly _THasDefault: THasDefault;\n readonly _internal: PrimitiveInternal<TState, TProxy>;\n readonly TSetInput: TSetInput;\n readonly TUpdateInput: TUpdateInput;\n }\n \n /**\n * Internal operations that each primitive must provide.\n */\n export interface PrimitiveInternal<TState, TProxy> {\n /** Creates a proxy for generating operations */\n readonly createProxy: (env: ProxyEnvironment.ProxyEnvironment, path: OperationPath.OperationPath) => TProxy;\n /** Applies an operation to the current state, returning the new state */\n readonly applyOperation: (state: TState | undefined, operation: Operation.Operation<any, any, any>) => TState;\n /** Returns the initial/default state for this primitive */\n readonly getInitialState: () => TState | undefined;\n /**\n * Converts a set input value to state format.\n * For most primitives, this is a simple pass-through with defaults applied.\n * For Tree primitives, this converts nested input to flat TreeState.\n *\n * @param input - The set input value\n * @returns The corresponding state value\n */\n readonly convertSetInputToState?: (input: unknown) => TState;\n /**\n * Transforms a client operation against a server operation.\n * Used for operational transformation (OT) conflict resolution.\n *\n * @param clientOp - The client's operation to transform\n * @param serverOp - The server's operation that has already been applied\n * @returns TransformResult indicating how the client operation should be handled\n */\n readonly transformOperation: (\n clientOp: Operation.Operation<any, any, any>,\n serverOp: Operation.Operation<any, any, any>\n ) => Transform.TransformResult;\n }\n \n /**\n * Any primitive type - used for generic constraints.\n */\n export type AnyPrimitive = Primitive<any, any, any, any>;\n \n /**\n * Infer the state type from a primitive.\n */\n export type InferState<T> = T extends Primitive<infer S, any, any, any> ? S : never;\n \n /**\n * Infer the proxy type from a primitive.\n */\n export type InferProxy<T> = T extends Primitive<any, infer P, any, any> ? P : never;\n\n /**\n * Infer the SetInput type from a primitive.\n * Works with both Primitive interface types and types with a TSetInput property (like TreeNodePrimitive).\n */\n export type InferSetInput<T> = \n T extends Primitive<any, any, any, any, infer S, any> ? S : \n T extends { TSetInput: infer S } ? S : \n never;\n\n /**\n * Infer the UpdateInput type from a primitive.\n * Works with both Primitive interface types and types with a TUpdateInput property (like TreeNodePrimitive).\n */\n export type InferUpdateInput<T> = \n T extends Primitive<any, any, any, any, any, infer U> ? U : \n T extends { TUpdateInput: infer U } ? U : \n never;\n \n /**\n * Helper type to conditionally add undefined based on TRequired and THasDefault.\n * When TRequired is false and THasDefault is false, the value may be undefined.\n * Otherwise, the value is guaranteed to be defined.\n */\n export type MaybeUndefined<T, TRequired extends boolean, THasDefault extends boolean> = TRequired extends false ? THasDefault extends false ? Optional<T> : T : T;\n\n export type Optional<T> = T | undefined;\n\n /**\n * Helper type to conditionally add undefined based on TRequired and THasDefault.\n * When TRequired is true and THasDefault is false, the value must be provided.\n * Otherwise, the value may be undefined.\n */\n export type NeedsValue<T, TRequired extends boolean, THasDefault extends boolean> = TRequired extends true ? THasDefault extends false ? T : Optional<T> : Optional<T>;\n \n /**\n * Infer the snapshot type from a primitive.\n * The snapshot is a readonly, type-safe structure suitable for rendering.\n */\n export type InferSnapshot<T> = T extends Primitive<any, infer P, any, any>\n ? P extends { toSnapshot(): infer S } ? S : never\n : never;\n\n /**\n * Extract THasDefault from a primitive.\n */\n export type HasDefault<T> = T extends Primitive<any, any, any, infer H> ? H : false;\n\n /**\n * Extract TDefined from a primitive.\n */\n export type IsDefined<T> = T extends Primitive<any, any, infer D, any> ? D : false;\n\n /**\n * Infer whether a primitive is required.\n * Alias for IsDefined for clarity.\n */\n export type IsRequired<T> = IsDefined<T>;\n\n\n // =============================================================================\n // Validation Errors\n // =============================================================================\n \n export class ValidationError extends Error {\n readonly _tag = \"ValidationError\";\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n }\n \n // =============================================================================\n // Validation Infrastructure\n // =============================================================================\n \n /**\n * A validator that checks a value and returns whether it's valid.\n */\n export interface Validator<T> {\n readonly validate: (value: T) => boolean;\n readonly message: string;\n }\n \n\n/**\n * Runs all validators against a value, throwing ValidationError if any fail.\n */\nexport function runValidators<T>(value: T, validators: readonly { validate: (value: T) => boolean; message: string }[]): void {\n for (const validator of validators) {\n if (!validator.validate(value)) {\n throw new ValidationError(validator.message);\n }\n }\n}\n\n/**\n * Checks if an operation is compatible with the given operation definitions.\n * @param operation - The operation to check.\n * @param operationDefinitions - The operation definitions to check against.\n * @returns True if the operation is compatible, false otherwise.\n */\nexport function isCompatibleOperation(operation: Operation.Operation<any, any, any>, operationDefinitions: Record<string, OperationDefinition.OperationDefinition<any, any, any>>) {\n const values = Object.values(operationDefinitions);\n return values.some(value => value.kind === operation.kind);\n}\n\n// =============================================================================\n// Default Value Utilities\n// =============================================================================\n\n/**\n * Applies default values to a partial input, recursively handling nested structs and unions.\n * \n * Uses a two-layer approach:\n * 1. First, get the struct's initial state (which includes struct-level defaults)\n * 2. Then, layer the provided values on top\n * 3. Finally, ensure nested structs and unions are recursively processed\n * \n * @param primitive - The primitive definition containing field information\n * @param value - The partial value provided by the user\n * @returns The value with defaults applied for missing fields\n */\nexport function applyDefaults<T extends AnyPrimitive>(\n primitive: T,\n value: Partial<InferState<T>>\n): InferState<T> {\n // Handle StructPrimitive\n if (primitive._tag === \"StructPrimitive\") {\n const structPrimitive = primitive as unknown as { \n fields: Record<string, AnyPrimitive>;\n _internal: { getInitialState: () => Record<string, unknown> | undefined };\n };\n \n // Start with the struct's initial state (struct-level default or field defaults)\n const structInitialState = structPrimitive._internal.getInitialState() ?? {};\n \n // Layer the provided values on top of initial state\n const result: Record<string, unknown> = { ...structInitialState, ...value };\n \n for (const key in structPrimitive.fields) {\n const fieldPrimitive = structPrimitive.fields[key]!;\n \n if (result[key] === undefined) {\n // Field still not provided after merging - try individual field default\n const fieldDefault = fieldPrimitive._internal.getInitialState();\n if (fieldDefault !== undefined) {\n result[key] = fieldDefault;\n }\n } else if (typeof result[key] === \"object\" && result[key] !== null) {\n // Recursively apply defaults to nested structs and unions\n if (fieldPrimitive._tag === \"StructPrimitive\" || fieldPrimitive._tag === \"UnionPrimitive\") {\n result[key] = applyDefaults(fieldPrimitive, result[key] as Partial<InferState<typeof fieldPrimitive>>);\n }\n }\n }\n \n return result as InferState<T>;\n }\n \n // Handle UnionPrimitive\n if (primitive._tag === \"UnionPrimitive\") {\n const unionPrimitive = primitive as unknown as {\n _schema: {\n discriminator: string;\n variants: Record<string, AnyPrimitive>;\n };\n };\n \n // Validate that value is an object\n if (typeof value !== \"object\" || value === null) {\n return value as InferState<T>;\n }\n \n const discriminator = unionPrimitive._schema.discriminator;\n const discriminatorValue = (value as Record<string, unknown>)[discriminator];\n \n // Find the matching variant based on discriminator value\n let matchingVariantKey: string | undefined;\n for (const variantKey in unionPrimitive._schema.variants) {\n const variant = unionPrimitive._schema.variants[variantKey]!;\n // Variants are structs - check if the discriminator field's literal matches\n if (variant._tag === \"StructPrimitive\") {\n const variantStruct = variant as unknown as {\n fields: Record<string, AnyPrimitive>;\n };\n const discriminatorField = variantStruct.fields[discriminator];\n if (discriminatorField && discriminatorField._tag === \"LiteralPrimitive\") {\n const literalPrimitive = discriminatorField as unknown as { literal: unknown };\n if (literalPrimitive.literal === discriminatorValue) {\n matchingVariantKey = variantKey;\n break;\n }\n }\n }\n }\n \n if (!matchingVariantKey) {\n // No matching variant found - return value as-is\n return value as InferState<T>;\n }\n \n // Apply defaults using the matching variant's struct\n const variantPrimitive = unionPrimitive._schema.variants[matchingVariantKey]!;\n return applyDefaults(variantPrimitive, value as Partial<InferState<typeof variantPrimitive>>) as InferState<T>;\n }\n \n // For other primitives, return the value as-is\n return value as InferState<T>;\n}\n\n"],"mappings":";;;;AA6IE,IAAa,kBAAb,cAAqC,MAAM;CAEzC,YAAY,SAAiB;AAC3B,QAAM,QAAQ;wBAFP,QAAO;AAGd,OAAK,OAAO;;;;;;AAoBlB,SAAgB,cAAiB,OAAU,YAAmF;AAC5H,MAAK,MAAM,aAAa,WACtB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,gBAAgB,UAAU,QAAQ;;;;;;;;AAWlD,SAAgB,sBAAsB,WAA+C,sBAA8F;AAEjL,QADe,OAAO,OAAO,qBAAqB,CACpC,MAAK,UAAS,MAAM,SAAS,UAAU,KAAK;;;;;;;;;;;;;;AAmB5D,SAAgB,cACd,WACA,OACe;AAEf,KAAI,UAAU,SAAS,mBAAmB;;EACxC,MAAM,kBAAkB;EASxB,MAAMA,oEAHqB,gBAAgB,UAAU,iBAAiB,yEAAI,EAAE,GAGR;AAEpE,OAAK,MAAM,OAAO,gBAAgB,QAAQ;GACxC,MAAM,iBAAiB,gBAAgB,OAAO;AAE9C,OAAI,OAAO,SAAS,QAAW;IAE7B,MAAM,eAAe,eAAe,UAAU,iBAAiB;AAC/D,QAAI,iBAAiB,OACnB,QAAO,OAAO;cAEP,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,MAE5D;QAAI,eAAe,SAAS,qBAAqB,eAAe,SAAS,iBACvE,QAAO,OAAO,cAAc,gBAAgB,OAAO,KAAmD;;;AAK5G,SAAO;;AAIT,KAAI,UAAU,SAAS,kBAAkB;EACvC,MAAM,iBAAiB;AAQvB,MAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;EAGT,MAAM,gBAAgB,eAAe,QAAQ;EAC7C,MAAM,qBAAsB,MAAkC;EAG9D,IAAIC;AACJ,OAAK,MAAM,cAAc,eAAe,QAAQ,UAAU;GACxD,MAAM,UAAU,eAAe,QAAQ,SAAS;AAEhD,OAAI,QAAQ,SAAS,mBAAmB;IAItC,MAAM,qBAHgB,QAGmB,OAAO;AAChD,QAAI,sBAAsB,mBAAmB,SAAS,oBAEpD;SADyB,mBACJ,YAAY,oBAAoB;AACnD,2BAAqB;AACrB;;;;;AAMR,MAAI,CAAC,mBAEH,QAAO;EAIT,MAAM,mBAAmB,eAAe,QAAQ,SAAS;AACzD,SAAO,cAAc,kBAAkB,MAAsD;;AAI/F,QAAO"}
1
+ {"version":3,"file":"shared.mjs","names":["result: Record<string, unknown>","matchingVariantKey: string | undefined"],"sources":["../../src/primitives/shared.ts"],"sourcesContent":["import * as Operation from \"../Operation\";\nimport * as OperationDefinition from \"../OperationDefinition\";\nimport * as ProxyEnvironment from \"../ProxyEnvironment\";\nimport * as OperationPath from \"../OperationPath\";\nimport * as Transform from \"../Transform\";\n\n// =============================================================================\n// Primitive Interface & Type Utilities\n// =============================================================================\n\n/**\n * Base interface that all primitives must implement.\n * Provides type inference helpers and internal operations.\n * \n * @typeParam TState - The state type this primitive holds\n * @typeParam TProxy - The proxy type for interacting with this primitive\n * @typeParam TDefined - Whether the value is guaranteed to be defined (via required() or default())\n * @typeParam THasDefault - Whether this primitive has a default value\n */\nexport interface Primitive<TState, TProxy, TRequired extends boolean = false, THasDefault extends boolean = false, TSetInput = unknown, TUpdateInput = unknown> {\n readonly _tag: string;\n readonly _State: TState;\n readonly _Proxy: TProxy;\n readonly _TRequired: TRequired;\n readonly _THasDefault: THasDefault;\n readonly _internal: PrimitiveInternal<TState, TProxy>;\n readonly TSetInput: TSetInput;\n readonly TUpdateInput: TUpdateInput;\n }\n \n /**\n * Internal operations that each primitive must provide.\n */\n export interface PrimitiveInternal<TState, TProxy> {\n /** Creates a proxy for generating operations */\n readonly createProxy: (env: ProxyEnvironment.ProxyEnvironment, path: OperationPath.OperationPath) => TProxy;\n /** Applies an operation to the current state, returning the new state */\n readonly applyOperation: (state: TState | undefined, operation: Operation.Operation<any, any, any>) => TState;\n /** Returns the initial/default state for this primitive */\n readonly getInitialState: () => TState | undefined;\n /**\n * Converts a set input value to state format.\n * For most primitives, this is a simple pass-through with defaults applied.\n * For Tree primitives, this converts nested input to flat TreeState.\n *\n * @param input - The set input value\n * @returns The corresponding state value\n */\n readonly convertSetInputToState?: (input: unknown) => TState;\n /**\n * Transforms a client operation against a server operation.\n * Used for operational transformation (OT) conflict resolution.\n *\n * @param clientOp - The client's operation to transform\n * @param serverOp - The server's operation that has already been applied\n * @returns TransformResult indicating how the client operation should be handled\n */\n readonly transformOperation: (\n clientOp: Operation.Operation<any, any, any>,\n serverOp: Operation.Operation<any, any, any>\n ) => Transform.TransformResult;\n }\n \n /**\n * Any primitive type - used for generic constraints.\n */\n export type AnyPrimitive = Primitive<any, any, any, any>;\n \n /**\n * Infer the state type from a primitive.\n */\n export type InferState<T> = T extends Primitive<infer S, any, any, any> ? S : never;\n \n /**\n * Infer the proxy type from a primitive.\n */\n export type InferProxy<T> = T extends Primitive<any, infer P, any, any> ? P : never;\n\n /**\n * Infer the SetInput type from a primitive.\n * Works with both Primitive interface types and types with a TSetInput property (like TreeNodePrimitive).\n */\n export type InferSetInput<T> = \n T extends Primitive<any, any, any, any, infer S, any> ? S : \n T extends { TSetInput: infer S } ? S : \n never;\n\n /**\n * Infer the UpdateInput type from a primitive.\n * Works with both Primitive interface types and types with a TUpdateInput property (like TreeNodePrimitive).\n */\n export type InferUpdateInput<T> = \n T extends Primitive<any, any, any, any, any, infer U> ? U : \n T extends { TUpdateInput: infer U } ? U : \n never;\n \n /**\n * Helper type to conditionally add undefined based on TRequired and THasDefault.\n * When TRequired is false and THasDefault is false, the value may be undefined.\n * Otherwise, the value is guaranteed to be defined.\n */\n export type MaybeUndefined<T, TRequired extends boolean, THasDefault extends boolean> = TRequired extends false ? THasDefault extends false ? Optional<T> : T : T;\n\n export type Optional<T> = T | undefined;\n\n /**\n * Helper type to conditionally add undefined based on TRequired and THasDefault.\n * When TRequired is true and THasDefault is false, the value must be provided.\n * Otherwise, the value may be undefined.\n */\n export type NeedsValue<T, TRequired extends boolean, THasDefault extends boolean> = TRequired extends true ? THasDefault extends false ? T : Optional<T> : Optional<T>;\n \n /**\n * Infer the snapshot type from a primitive.\n * The snapshot is a readonly, type-safe structure suitable for rendering.\n */\n export type InferSnapshot<T> = T extends Primitive<any, infer P, any, any>\n ? P extends { toSnapshot(): infer S } ? S : never\n : never;\n\n /**\n * Extract THasDefault from a primitive.\n */\n export type HasDefault<T> = T extends Primitive<any, any, any, infer H> ? H : false;\n\n /**\n * Extract TDefined from a primitive.\n */\n export type IsDefined<T> = T extends Primitive<any, any, infer D, any> ? D : false;\n\n /**\n * Infer whether a primitive is required.\n * Alias for IsDefined for clarity.\n */\n export type IsRequired<T> = IsDefined<T>;\n\n\n // =============================================================================\n // Validation Errors\n // =============================================================================\n \n export class ValidationError extends Error {\n readonly _tag = \"ValidationError\";\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n }\n \n // =============================================================================\n // Validation Infrastructure\n // =============================================================================\n \n /**\n * A validator that checks a value and returns whether it's valid.\n */\n export interface Validator<T> {\n readonly validate: (value: T) => boolean;\n readonly message: string;\n }\n \n\n/**\n * Runs all validators against a value, throwing ValidationError if any fail.\n */\nexport function runValidators<T>(value: T, validators: readonly { validate: (value: T) => boolean; message: string }[]): void {\n for (const validator of validators) {\n if (!validator.validate(value)) {\n throw new ValidationError(validator.message);\n }\n }\n}\n\n/**\n * Returns true if a primitive can represent null as a meaningful value.\n * This is used to avoid pruning explicit null values for null-capable scalar unions.\n */\nexport function primitiveAllowsNullValue(primitive: AnyPrimitive): boolean {\n if (primitive._tag === \"LiteralPrimitive\") {\n return (primitive as { literal: unknown }).literal === null;\n }\n\n if (primitive._tag === \"EitherPrimitive\") {\n const variants = (primitive as { _schema?: { variants?: readonly AnyPrimitive[] } })._schema?.variants;\n return Array.isArray(variants) && variants.some((variant) => primitiveAllowsNullValue(variant));\n }\n\n return false;\n}\n\n/**\n * Checks if an operation is compatible with the given operation definitions.\n * @param operation - The operation to check.\n * @param operationDefinitions - The operation definitions to check against.\n * @returns True if the operation is compatible, false otherwise.\n */\nexport function isCompatibleOperation(operation: Operation.Operation<any, any, any>, operationDefinitions: Record<string, OperationDefinition.OperationDefinition<any, any, any>>) {\n const values = Object.values(operationDefinitions);\n return values.some(value => value.kind === operation.kind);\n}\n\n// =============================================================================\n// Default Value Utilities\n// =============================================================================\n\n/**\n * Applies default values to a partial input, recursively handling nested structs and unions.\n * \n * Uses a two-layer approach:\n * 1. First, get the struct's initial state (which includes struct-level defaults)\n * 2. Then, layer the provided values on top\n * 3. Finally, ensure nested structs and unions are recursively processed\n * \n * @param primitive - The primitive definition containing field information\n * @param value - The partial value provided by the user\n * @returns The value with defaults applied for missing fields\n */\nexport function applyDefaults<T extends AnyPrimitive>(\n primitive: T,\n value: Partial<InferState<T>>\n): InferState<T> {\n // Handle StructPrimitive\n if (primitive._tag === \"StructPrimitive\") {\n const structPrimitive = primitive as unknown as { \n fields: Record<string, AnyPrimitive>;\n _internal: { getInitialState: () => Record<string, unknown> | undefined };\n };\n \n // Start with the struct's initial state (struct-level default or field defaults)\n const structInitialState = structPrimitive._internal.getInitialState() ?? {};\n \n // Layer the provided values on top of initial state\n const result: Record<string, unknown> = { ...structInitialState, ...value };\n const inputObject =\n typeof value === \"object\" && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n \n for (const key in structPrimitive.fields) {\n const fieldPrimitive = structPrimitive.fields[key]!;\n const hasExplicitKey = inputObject !== undefined && Object.prototype.hasOwnProperty.call(inputObject, key);\n const explicitValue = hasExplicitKey ? inputObject[key] : undefined;\n const fieldDefault = fieldPrimitive._internal.getInitialState();\n const isRequiredWithoutDefault =\n (fieldPrimitive as { _schema?: { required?: boolean } })._schema?.required === true &&\n fieldDefault === undefined;\n\n // Explicit undefined values always prune optional keys.\n // Explicit null values prune optional keys unless null is a valid semantic value for this field.\n // Required fields without defaults reject nullish values.\n const shouldPruneExplicitNullish =\n hasExplicitKey &&\n (\n explicitValue === undefined ||\n (explicitValue === null && !primitiveAllowsNullValue(fieldPrimitive))\n );\n if (shouldPruneExplicitNullish) {\n if (isRequiredWithoutDefault) {\n throw new ValidationError(`Field \"${key}\" is required and cannot be null or undefined`);\n }\n delete result[key];\n continue;\n }\n \n if (!hasExplicitKey && result[key] === undefined) {\n // Field still not provided after merging - try individual field default\n if (fieldDefault !== undefined) {\n result[key] = fieldDefault;\n }\n } else if (\n hasExplicitKey &&\n typeof explicitValue === \"object\" &&\n explicitValue !== null\n ) {\n // Recursively apply defaults to nested structs and unions\n if (fieldPrimitive._tag === \"StructPrimitive\" || fieldPrimitive._tag === \"UnionPrimitive\") {\n result[key] = applyDefaults(fieldPrimitive, explicitValue as Partial<InferState<typeof fieldPrimitive>>);\n }\n }\n }\n \n return result as InferState<T>;\n }\n \n // Handle UnionPrimitive\n if (primitive._tag === \"UnionPrimitive\") {\n const unionPrimitive = primitive as unknown as {\n _schema: {\n discriminator: string;\n variants: Record<string, AnyPrimitive>;\n };\n };\n \n // Validate that value is an object\n if (typeof value !== \"object\" || value === null) {\n return value as InferState<T>;\n }\n \n const discriminator = unionPrimitive._schema.discriminator;\n const discriminatorValue = (value as Record<string, unknown>)[discriminator];\n \n // Find the matching variant based on discriminator value\n let matchingVariantKey: string | undefined;\n for (const variantKey in unionPrimitive._schema.variants) {\n const variant = unionPrimitive._schema.variants[variantKey]!;\n // Variants are structs - check if the discriminator field's literal matches\n if (variant._tag === \"StructPrimitive\") {\n const variantStruct = variant as unknown as {\n fields: Record<string, AnyPrimitive>;\n };\n const discriminatorField = variantStruct.fields[discriminator];\n if (discriminatorField && discriminatorField._tag === \"LiteralPrimitive\") {\n const literalPrimitive = discriminatorField as unknown as { literal: unknown };\n if (literalPrimitive.literal === discriminatorValue) {\n matchingVariantKey = variantKey;\n break;\n }\n }\n }\n }\n \n if (!matchingVariantKey) {\n // No matching variant found - return value as-is\n return value as InferState<T>;\n }\n \n // Apply defaults using the matching variant's struct\n const variantPrimitive = unionPrimitive._schema.variants[matchingVariantKey]!;\n return applyDefaults(variantPrimitive, value as Partial<InferState<typeof variantPrimitive>>) as InferState<T>;\n }\n \n // For other primitives, return the value as-is\n return value as InferState<T>;\n}\n"],"mappings":";;;;AA6IE,IAAa,kBAAb,cAAqC,MAAM;CAEzC,YAAY,SAAiB;AAC3B,QAAM,QAAQ;wBAFP,QAAO;AAGd,OAAK,OAAO;;;;;;AAoBlB,SAAgB,cAAiB,OAAU,YAAmF;AAC5H,MAAK,MAAM,aAAa,WACtB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,gBAAgB,UAAU,QAAQ;;;;;;AASlD,SAAgB,yBAAyB,WAAkC;AACzE,KAAI,UAAU,SAAS,mBACrB,QAAQ,UAAmC,YAAY;AAGzD,KAAI,UAAU,SAAS,mBAAmB;;EACxC,MAAM,sBAAY,UAAmE,2DAAS;AAC9F,SAAO,MAAM,QAAQ,SAAS,IAAI,SAAS,MAAM,YAAY,yBAAyB,QAAQ,CAAC;;AAGjG,QAAO;;;;;;;;AAST,SAAgB,sBAAsB,WAA+C,sBAA8F;AAEjL,QADe,OAAO,OAAO,qBAAqB,CACpC,MAAK,UAAS,MAAM,SAAS,UAAU,KAAK;;;;;;;;;;;;;;AAmB5D,SAAgB,cACd,WACA,OACe;AAEf,KAAI,UAAU,SAAS,mBAAmB;;EACxC,MAAM,kBAAkB;EASxB,MAAMA,oEAHqB,gBAAgB,UAAU,iBAAiB,yEAAI,EAAE,GAGR;EACpE,MAAM,cACJ,OAAO,UAAU,YAAY,UAAU,OAClC,QACD;AAEN,OAAK,MAAM,OAAO,gBAAgB,QAAQ;;GACxC,MAAM,iBAAiB,gBAAgB,OAAO;GAC9C,MAAM,iBAAiB,gBAAgB,UAAa,OAAO,UAAU,eAAe,KAAK,aAAa,IAAI;GAC1G,MAAM,gBAAgB,iBAAiB,YAAY,OAAO;GAC1D,MAAM,eAAe,eAAe,UAAU,iBAAiB;GAC/D,MAAM,wCACH,eAAwD,6DAAS,cAAa,QAC/E,iBAAiB;AAWnB,OALE,mBAEE,kBAAkB,UACjB,kBAAkB,QAAQ,CAAC,yBAAyB,eAAe,GAExC;AAC9B,QAAI,yBACF,OAAM,IAAI,gBAAgB,UAAU,IAAI,+CAA+C;AAEzF,WAAO,OAAO;AACd;;AAGF,OAAI,CAAC,kBAAkB,OAAO,SAAS,QAErC;QAAI,iBAAiB,OACnB,QAAO,OAAO;cAGhB,kBACA,OAAO,kBAAkB,YACzB,kBAAkB,MAGlB;QAAI,eAAe,SAAS,qBAAqB,eAAe,SAAS,iBACvE,QAAO,OAAO,cAAc,gBAAgB,cAA4D;;;AAK9G,SAAO;;AAIT,KAAI,UAAU,SAAS,kBAAkB;EACvC,MAAM,iBAAiB;AAQvB,MAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;EAGT,MAAM,gBAAgB,eAAe,QAAQ;EAC7C,MAAM,qBAAsB,MAAkC;EAG9D,IAAIC;AACJ,OAAK,MAAM,cAAc,eAAe,QAAQ,UAAU;GACxD,MAAM,UAAU,eAAe,QAAQ,SAAS;AAEhD,OAAI,QAAQ,SAAS,mBAAmB;IAItC,MAAM,qBAHgB,QAGmB,OAAO;AAChD,QAAI,sBAAsB,mBAAmB,SAAS,oBAEpD;SADyB,mBACJ,YAAY,oBAAoB;AACnD,2BAAqB;AACrB;;;;;AAMR,MAAI,CAAC,mBAEH,QAAO;EAIT,MAAM,mBAAmB,eAAe,QAAQ,SAAS;AACzD,SAAO,cAAc,kBAAkB,MAAsD;;AAI/F,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic",
3
- "version": "1.0.0-beta.13",
3
+ "version": "1.0.0-beta.15",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,7 +31,7 @@
31
31
  "typescript": "5.8.3",
32
32
  "vite-tsconfig-paths": "^5.1.4",
33
33
  "vitest": "^3.2.4",
34
- "@voidhash/tsconfig": "1.0.0-beta.13"
34
+ "@voidhash/tsconfig": "1.0.0-beta.15"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "effect": "^3.19.12"
@@ -558,6 +558,9 @@ export const make = <
558
558
  ops: tx.ops,
559
559
  pendingCount: _pending.length + 1,
560
560
  isConnected: transport.isConnected(),
561
+ activeDraftCount: _drafts.size,
562
+ activeDraftIds: Array.from(_drafts.keys()),
563
+ callStack: new Error().stack?.split("\n").slice(1, 6).join("\n"),
561
564
  });
562
565
 
563
566
  const pending: PendingTransaction = {
@@ -1002,6 +1005,9 @@ export const make = <
1002
1005
  isConnected: transport.isConnected(),
1003
1006
  isReady: _initState.type === "ready",
1004
1007
  pendingCount: _pending.length,
1008
+ activeDraftCount: _drafts.size,
1009
+ activeDraftIds: Array.from(_drafts.keys()),
1010
+ callStack: new Error().stack?.split("\n").slice(1, 6).join("\n"),
1005
1011
  });
1006
1012
 
1007
1013
  // Allow transactions even when disconnected - they will be queued
@@ -1176,6 +1182,13 @@ export const make = <
1176
1182
  id: draftId,
1177
1183
 
1178
1184
  update: (fn: (root: Primitive.InferProxy<TSchema>) => void): void => {
1185
+ debugLog("draft.update: starting", {
1186
+ draftId,
1187
+ consumed,
1188
+ currentOpsCount: draftState.ops.size,
1189
+ pendingCount: _pending.length,
1190
+ });
1191
+
1179
1192
  if (consumed) {
1180
1193
  throw new InvalidStateError("Draft has already been committed or discarded.");
1181
1194
  }
@@ -1213,7 +1226,12 @@ export const make = <
1213
1226
  }
1214
1227
  }
1215
1228
 
1216
- debugLog("draft.update", { draftId, newOpsCount: tx.ops.length, totalOps: draftState.ops.size });
1229
+ debugLog("draft.update: complete", {
1230
+ draftId,
1231
+ newOpsCount: tx.ops.length,
1232
+ totalOps: draftState.ops.size,
1233
+ note: "Ops stored in draft - NOT sent to server",
1234
+ });
1217
1235
 
1218
1236
  // Recompute optimistic state to reflect draft changes
1219
1237
  recomputeOptimisticState();
@@ -1221,6 +1239,12 @@ export const make = <
1221
1239
  },
1222
1240
 
1223
1241
  commit: (): void => {
1242
+ debugLog("draft.commit: starting", {
1243
+ draftId,
1244
+ consumed,
1245
+ opsCount: draftState.ops.size,
1246
+ });
1247
+
1224
1248
  if (consumed) {
1225
1249
  throw new InvalidStateError("Draft has already been committed or discarded.");
1226
1250
  }
@@ -1229,7 +1253,11 @@ export const make = <
1229
1253
  const ops = Array.from(draftState.ops.values());
1230
1254
  _drafts.delete(draftId);
1231
1255
 
1232
- debugLog("draft.commit", { draftId, opsCount: ops.length });
1256
+ debugLog("draft.commit: submitting", {
1257
+ draftId,
1258
+ opsCount: ops.length,
1259
+ note: ops.length > 0 ? "Will call submitTransaction" : "Empty draft - no transaction",
1260
+ });
1233
1261
 
1234
1262
  if (ops.length > 0) {
1235
1263
  const tx = Transaction.make(ops);
@@ -1243,6 +1271,12 @@ export const make = <
1243
1271
  },
1244
1272
 
1245
1273
  discard: (): void => {
1274
+ debugLog("draft.discard: starting", {
1275
+ draftId,
1276
+ consumed,
1277
+ opsCount: draftState.ops.size,
1278
+ });
1279
+
1246
1280
  if (consumed) {
1247
1281
  throw new InvalidStateError("Draft has already been committed or discarded.");
1248
1282
  }
@@ -1250,7 +1284,10 @@ export const make = <
1250
1284
 
1251
1285
  _drafts.delete(draftId);
1252
1286
 
1253
- debugLog("draft.discard", { draftId });
1287
+ debugLog("draft.discard: complete", {
1288
+ draftId,
1289
+ note: "Draft discarded - no transaction sent to server",
1290
+ });
1254
1291
 
1255
1292
  recomputeOptimisticState();
1256
1293
  notifyDraftChange();
@@ -6,7 +6,7 @@ import * as ProxyEnvironment from "../ProxyEnvironment";
6
6
  import * as Transform from "../Transform";
7
7
  import type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot, NeedsValue, InferUpdateInput, InferSetInput } from "../Primitive";
8
8
  import { ValidationError } from "../Primitive";
9
- import { runValidators, applyDefaults } from "./shared";
9
+ import { runValidators, applyDefaults, primitiveAllowsNullValue } from "./shared";
10
10
 
11
11
  // =============================================================================
12
12
  // Struct Set Input Types
@@ -142,6 +142,13 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequ
142
142
  apply: (payload) => payload,
143
143
  deduplicable: true,
144
144
  }),
145
+ unset: OperationDefinition.make({
146
+ kind: "struct.unset" as const,
147
+ payload: Schema.Unknown,
148
+ target: Schema.Unknown,
149
+ apply: (payload) => payload,
150
+ deduplicable: true,
151
+ }),
145
152
  };
146
153
 
147
154
  constructor(schema: StructPrimitiveSchema<TFields>) {
@@ -221,6 +228,14 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequ
221
228
  return field;
222
229
  }
223
230
 
231
+ private _isRequiredWithoutDefault(field: AnyPrimitive): boolean {
232
+ const fieldDefault = field._internal.getInitialState();
233
+ return (
234
+ (field as { _schema?: { required?: boolean } })._schema?.required === true &&
235
+ fieldDefault === undefined
236
+ );
237
+ }
238
+
224
239
  readonly _internal: PrimitiveInternal<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>> = {
225
240
  createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StructProxy<TFields, TRequired, THasDefault> => {
226
241
  const fields = this._schema.fields;
@@ -270,12 +285,24 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequ
270
285
  for (const key in value) {
271
286
  if (Object.prototype.hasOwnProperty.call(value, key)) {
272
287
  const fieldValue = value[key as keyof TFields];
273
- if (fieldValue === undefined) continue; // Skip undefined values
274
-
275
288
  const fieldPrimitive = fields[key as keyof TFields];
276
289
  if (!fieldPrimitive) continue; // Skip unknown fields
277
290
 
278
291
  const fieldPath = operationPath.append(key);
292
+
293
+ const shouldUnset =
294
+ fieldValue === undefined ||
295
+ (fieldValue === null && !primitiveAllowsNullValue(fieldPrimitive));
296
+ if (shouldUnset) {
297
+ if (this._isRequiredWithoutDefault(fieldPrimitive)) {
298
+ throw new ValidationError(`Field "${key}" is required and cannot be null or undefined`);
299
+ }
300
+ env.addOperation(
301
+ Operation.fromDefinition(fieldPath, this._opDefinitions.unset, null)
302
+ );
303
+ continue;
304
+ }
305
+
279
306
  const fieldProxy = fieldPrimitive._internal.createProxy(env, fieldPath);
280
307
 
281
308
  // Check if this is a nested struct and value is a plain object (partial update)
@@ -368,24 +395,32 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequ
368
395
  }
369
396
 
370
397
  const fieldPrimitive = this._schema.fields[fieldName]!;
371
- const remainingPath = path.shift();
372
- const fieldOperation = {
373
- ...operation,
374
- path: remainingPath,
375
- };
376
-
377
398
  // Get the current field state
378
399
  const currentState = state ?? ({} as InferStructState<TFields>);
379
- const currentFieldState = currentState[fieldName] as InferState<typeof fieldPrimitive> | undefined;
400
+ if (operation.kind === "struct.unset" && tokens.length === 1) {
401
+ if (this._isRequiredWithoutDefault(fieldPrimitive)) {
402
+ throw new ValidationError(`Field "${globalThis.String(fieldName)}" is required and cannot be removed`);
403
+ }
404
+ const mutableState = { ...currentState } as Record<string, unknown>;
405
+ delete mutableState[globalThis.String(fieldName)];
406
+ newState = mutableState as InferStructState<TFields>;
407
+ } else {
408
+ const remainingPath = path.shift();
409
+ const fieldOperation = {
410
+ ...operation,
411
+ path: remainingPath,
412
+ };
413
+ const currentFieldState = currentState[fieldName] as InferState<typeof fieldPrimitive> | undefined;
380
414
 
381
- // Apply the operation to the field
382
- const newFieldState = fieldPrimitive._internal.applyOperation(currentFieldState, fieldOperation);
415
+ // Apply the operation to the field
416
+ const newFieldState = fieldPrimitive._internal.applyOperation(currentFieldState, fieldOperation);
383
417
 
384
- // Build updated state
385
- newState = {
386
- ...currentState,
387
- [fieldName]: newFieldState,
388
- };
418
+ // Build updated state
419
+ newState = {
420
+ ...currentState,
421
+ [fieldName]: newFieldState,
422
+ };
423
+ }
389
424
  }
390
425
 
391
426
  // Run validators on the new state
@@ -501,4 +536,3 @@ export const Struct = <TFields extends Record<string, AnyPrimitive>>(
501
536
  fields: TFields
502
537
  ): StructPrimitive<TFields, false, false> =>
503
538
  new StructPrimitive({ required: false, defaultValue: undefined, fields, validators: [] });
504
-