@voidhash/mimic 0.0.1-alpha.4 → 0.0.1-alpha.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic",
3
- "version": "0.0.1-alpha.4",
3
+ "version": "0.0.1-alpha.6",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  "typescript": "5.8.3",
20
20
  "vite-tsconfig-paths": "^5.1.4",
21
21
  "vitest": "^3.2.4",
22
- "@voidhash/tsconfig": "0.0.1-alpha.4"
22
+ "@voidhash/tsconfig": "0.0.1-alpha.6"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "effect": "^3.19.12"
@@ -43,7 +43,7 @@ type InitState =
43
43
  /**
44
44
  * Listener for presence changes.
45
45
  */
46
- export interface PresenceListener<TData> {
46
+ export interface PresenceListener<_TData> {
47
47
  /** Called when any presence changes (self or others) */
48
48
  readonly onPresenceChange?: () => void;
49
49
  }
@@ -19,7 +19,7 @@ export class MimicClientError extends Error {
19
19
  * Error thrown when a transaction is rejected by the server.
20
20
  */
21
21
  export class TransactionRejectedError extends MimicClientError {
22
- readonly _tag = "TransactionRejectedError";
22
+ override readonly _tag = "TransactionRejectedError";
23
23
  readonly transaction: Transaction.Transaction;
24
24
  readonly reason: string;
25
25
 
@@ -35,7 +35,7 @@ export class TransactionRejectedError extends MimicClientError {
35
35
  * Error thrown when the transport is not connected.
36
36
  */
37
37
  export class NotConnectedError extends MimicClientError {
38
- readonly _tag = "NotConnectedError";
38
+ override readonly _tag = "NotConnectedError";
39
39
  constructor() {
40
40
  super("Transport is not connected");
41
41
  this.name = "NotConnectedError";
@@ -46,8 +46,8 @@ export class NotConnectedError extends MimicClientError {
46
46
  * Error thrown when connection to the server fails.
47
47
  */
48
48
  export class ConnectionError extends MimicClientError {
49
- readonly _tag = "ConnectionError";
50
- readonly cause?: Error;
49
+ override readonly _tag = "ConnectionError";
50
+ override readonly cause?: Error;
51
51
 
52
52
  constructor(message: string, cause?: Error) {
53
53
  super(message);
@@ -60,7 +60,7 @@ export class ConnectionError extends MimicClientError {
60
60
  * Error thrown when state drift is detected and cannot be recovered.
61
61
  */
62
62
  export class StateDriftError extends MimicClientError {
63
- readonly _tag = "StateDriftError";
63
+ override readonly _tag = "StateDriftError";
64
64
  readonly expectedVersion: number;
65
65
  readonly receivedVersion: number;
66
66
 
@@ -78,7 +78,7 @@ export class StateDriftError extends MimicClientError {
78
78
  * Error thrown when a pending transaction times out waiting for confirmation.
79
79
  */
80
80
  export class TransactionTimeoutError extends MimicClientError {
81
- readonly _tag = "TransactionTimeoutError";
81
+ override readonly _tag = "TransactionTimeoutError";
82
82
  readonly transaction: Transaction.Transaction;
83
83
  readonly timeoutMs: number;
84
84
 
@@ -96,7 +96,7 @@ export class TransactionTimeoutError extends MimicClientError {
96
96
  * Error thrown when rebasing operations fails.
97
97
  */
98
98
  export class RebaseError extends MimicClientError {
99
- readonly _tag = "RebaseError";
99
+ override readonly _tag = "RebaseError";
100
100
  readonly transactionId: string;
101
101
 
102
102
  constructor(transactionId: string, message: string) {
@@ -110,7 +110,7 @@ export class RebaseError extends MimicClientError {
110
110
  * Error thrown when the client document is in an invalid state.
111
111
  */
112
112
  export class InvalidStateError extends MimicClientError {
113
- readonly _tag = "InvalidStateError";
113
+ override readonly _tag = "InvalidStateError";
114
114
  constructor(message: string) {
115
115
  super(message);
116
116
  this.name = "InvalidStateError";
@@ -121,7 +121,7 @@ export class InvalidStateError extends MimicClientError {
121
121
  * Error thrown when WebSocket connection or communication fails.
122
122
  */
123
123
  export class WebSocketError extends MimicClientError {
124
- readonly _tag = "WebSocketError";
124
+ override readonly _tag = "WebSocketError";
125
125
  readonly code?: number;
126
126
  readonly reason?: string;
127
127
 
@@ -137,7 +137,7 @@ export class WebSocketError extends MimicClientError {
137
137
  * Error thrown when authentication fails.
138
138
  */
139
139
  export class AuthenticationError extends MimicClientError {
140
- readonly _tag = "AuthenticationError";
140
+ override readonly _tag = "AuthenticationError";
141
141
  constructor(message: string) {
142
142
  super(message);
143
143
  this.name = "AuthenticationError";
@@ -87,7 +87,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
87
87
  };
88
88
  },
89
89
 
90
- applyOperation: (state: boolean | undefined, operation: Operation.Operation<any, any, any>): boolean => {
90
+ applyOperation: (_state: boolean | undefined, operation: Operation.Operation<any, any, any>): boolean => {
91
91
  if (operation.kind !== "boolean.set") {
92
92
  throw new ValidationError(`BooleanPrimitive cannot apply operation of kind: ${operation.kind}`);
93
93
  }
@@ -88,7 +88,7 @@ export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean =
88
88
  };
89
89
  },
90
90
 
91
- applyOperation: (state: T | undefined, operation: Operation.Operation<any, any, any>): T => {
91
+ applyOperation: (_state: T | undefined, operation: Operation.Operation<any, any, any>): T => {
92
92
  if (operation.kind !== "literal.set") {
93
93
  throw new ValidationError(`LiteralPrimitive cannot apply operation of kind: ${operation.kind}`);
94
94
  }
@@ -128,7 +128,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
128
128
  };
129
129
  },
130
130
 
131
- applyOperation: (state: number | undefined, operation: Operation.Operation<any, any, any>): number => {
131
+ applyOperation: (_state: number | undefined, operation: Operation.Operation<any, any, any>): number => {
132
132
  if (operation.kind !== "number.set") {
133
133
  throw new ValidationError(`NumberPrimitive cannot apply operation of kind: ${operation.kind}`);
134
134
  }
@@ -147,7 +147,7 @@ export class StringPrimitive<TDefined extends boolean = false> implements Primit
147
147
  };
148
148
  },
149
149
 
150
- applyOperation: (state: string | undefined, operation: Operation.Operation<any, any, any>): string => {
150
+ applyOperation: (_state: string | undefined, operation: Operation.Operation<any, any, any>): string => {
151
151
  if (!isCompatibleOperation(operation, this._opDefinitions)) {
152
152
  throw new ValidationError(`StringPrimitive cannot apply operation of kind: ${operation.kind}`);
153
153
  }
@@ -149,7 +149,7 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefi
149
149
 
150
150
  // Use a JavaScript Proxy to intercept field access
151
151
  return new globalThis.Proxy(base as StructProxy<TFields, TDefined>, {
152
- get: (target, prop, receiver) => {
152
+ get: (target, prop, _receiver) => {
153
153
  // Return base methods (get, set, toSnapshot)
154
154
  if (prop === "get") {
155
155
  return target.get;
@@ -175,7 +175,7 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefi
175
175
 
176
176
  return undefined;
177
177
  },
178
- has: (target, prop) => {
178
+ has: (_target, prop) => {
179
179
  if (prop === "get" || prop === "set" || prop === "toSnapshot") return true;
180
180
  if (typeof prop === "string" && prop in fields) return true;
181
181
  return false;
@@ -37,7 +37,7 @@ export interface TypedTreeNodeState<TNode extends AnyTreeNodePrimitive> {
37
37
  /**
38
38
  * The state type for trees - a flat array of nodes
39
39
  */
40
- export type TreeState<TRoot extends AnyTreeNodePrimitive> = readonly TreeNodeState[];
40
+ export type TreeState<_TRoot extends AnyTreeNodePrimitive> = readonly TreeNodeState[];
41
41
 
42
42
  /**
43
43
  * Helper to get children sorted by position
@@ -122,7 +122,7 @@ export interface TypedNodeProxy<TNode extends AnyTreeNodePrimitive> {
122
122
  /**
123
123
  * Node proxy with type narrowing capabilities
124
124
  */
125
- export interface TreeNodeProxyBase<TRoot extends AnyTreeNodePrimitive> {
125
+ export interface TreeNodeProxyBase<_TRoot extends AnyTreeNodePrimitive> {
126
126
  /** The node ID */
127
127
  readonly id: string;
128
128
  /** The node type (string) */
@@ -1,23 +1,64 @@
1
- import { Effect, Schema } from "effect";
2
- import * as OperationDefinition from "../OperationDefinition";
3
- import * as Operation from "../Operation";
4
- import * as OperationPath from "../OperationPath";
5
- import * as ProxyEnvironment from "../ProxyEnvironment";
6
- import * as Transform from "../Transform";
7
- import type { AnyPrimitive, InferState } from "../Primitive";
1
+ import type { InferState } from "../Primitive";
8
2
  import { StructPrimitive } from "./Struct";
9
3
 
4
+ /**
5
+ * Symbol used to identify the Self placeholder
6
+ */
7
+ const TreeNodeSelfSymbol = Symbol.for("TreeNode.Self");
10
8
 
11
9
  /**
12
- * Any TreeNodePrimitive type - used for generic constraints.
10
+ * Branded type for TreeNodeSelf - distinguishable at compile time
11
+ */
12
+ declare const SelfBrand: unique symbol;
13
+ export interface TreeNodeSelfType {
14
+ readonly _tag: "TreeNodeSelf";
15
+ readonly _brand: typeof SelfBrand;
16
+ }
17
+
18
+ /**
19
+ * Special placeholder for self-referential tree nodes.
20
+ * Use this in the children array when a node type can contain itself.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const FolderNode = TreeNode("folder", {
25
+ * data: Struct({ name: String() }),
26
+ * children: [TreeNodeSelf], // Folder can contain other folders
27
+ * });
28
+ * ```
29
+ */
30
+ export const TreeNodeSelf: TreeNodeSelfType = { _tag: "TreeNodeSelf", _symbol: TreeNodeSelfSymbol } as unknown as TreeNodeSelfType;
31
+
32
+ /**
33
+ * Check if a value is the Self placeholder
13
34
  */
14
- export type AnyTreeNodePrimitive = TreeNodePrimitive<string, StructPrimitive<any>, readonly AnyTreeNodePrimitive[] | (() => readonly AnyTreeNodePrimitive[])>;
35
+ const isSelf = (value: unknown): boolean => {
36
+ return typeof value === "object" && value !== null && "_symbol" in value && (value as any)._symbol === TreeNodeSelfSymbol;
37
+ };
15
38
 
16
39
  /**
17
- * Resolves children type - handles both array and lazy thunk
40
+ * Type utility to resolve Self placeholders to the actual node type
41
+ */
42
+ type ResolveSelf<T, TSelf extends AnyTreeNodePrimitive> =
43
+ T extends TreeNodeSelfType ? TSelf : T;
44
+
45
+ /**
46
+ * Type utility to resolve all children in a tuple, replacing Self with the node type
47
+ */
48
+ type ResolveChildrenUnion<TChildren, TSelf extends AnyTreeNodePrimitive> =
49
+ TChildren extends readonly (infer U)[]
50
+ ? ResolveSelf<U, TSelf>
51
+ : never;
52
+
53
+ /**
54
+ * The type for children - either a direct array or a lazy function (for self-referential nodes).
55
+ */
56
+ export type TreeNodeChildrenInput = readonly (AnyTreeNodePrimitive | TreeNodeSelfType)[] | (() => readonly (AnyTreeNodePrimitive | TreeNodeSelfType)[]);
57
+
58
+ /**
59
+ * Any TreeNodePrimitive type - used for generic constraints.
18
60
  */
19
- export type ResolveChildren<TChildren extends readonly AnyTreeNodePrimitive[] | (() => readonly AnyTreeNodePrimitive[])> =
20
- TChildren extends () => readonly AnyTreeNodePrimitive[] ? ReturnType<TChildren> : TChildren;
61
+ export type AnyTreeNodePrimitive = TreeNodePrimitive<string, StructPrimitive<any>, any>;
21
62
 
22
63
  /**
23
64
  * Infer the data state type from a TreeNodePrimitive
@@ -34,18 +75,18 @@ export type InferTreeNodeType<T extends AnyTreeNodePrimitive> =
34
75
  /**
35
76
  * Infer the allowed children from a TreeNodePrimitive
36
77
  */
37
- export type InferTreeNodeChildren<T extends AnyTreeNodePrimitive> =
38
- T extends TreeNodePrimitive<any, any, infer TChildren> ? ResolveChildren<TChildren>[number] : never;
78
+ export type InferTreeNodeChildren<T> =
79
+ T extends TreeNodePrimitive<any, any, infer TChildren> ? TChildren : never;
39
80
 
40
81
  /**
41
82
  * Configuration for a TreeNode primitive
42
83
  */
43
84
  export interface TreeNodeConfig<
44
85
  TData extends StructPrimitive<any>,
45
- TChildren extends readonly AnyTreeNodePrimitive[] | (() => readonly AnyTreeNodePrimitive[])
86
+ TChildren extends readonly (AnyTreeNodePrimitive | TreeNodeSelfType)[]
46
87
  > {
47
88
  readonly data: TData;
48
- readonly children: TChildren;
89
+ readonly children: TChildren | (() => TChildren);
49
90
  }
50
91
 
51
92
  /**
@@ -54,7 +95,7 @@ export interface TreeNodeConfig<
54
95
  export class TreeNodePrimitive<
55
96
  TType extends string,
56
97
  TData extends StructPrimitive<any>,
57
- TChildren extends readonly AnyTreeNodePrimitive[] | (() => readonly AnyTreeNodePrimitive[])
98
+ TChildren extends AnyTreeNodePrimitive = AnyTreeNodePrimitive
58
99
  > {
59
100
  readonly _tag = "TreeNodePrimitive" as const;
60
101
  readonly _Type!: TType;
@@ -63,10 +104,10 @@ export class TreeNodePrimitive<
63
104
 
64
105
  private readonly _type: TType;
65
106
  private readonly _data: TData;
66
- private readonly _children: TChildren;
107
+ private readonly _children: TreeNodeChildrenInput;
67
108
  private _resolvedChildren: readonly AnyTreeNodePrimitive[] | undefined;
68
109
 
69
- constructor(type: TType, config: TreeNodeConfig<TData, TChildren>) {
110
+ constructor(type: TType, config: TreeNodeConfig<TData, readonly (AnyTreeNodePrimitive | TreeNodeSelfType)[]>) {
70
111
  this._type = type;
71
112
  this._data = config.data;
72
113
  this._children = config.children;
@@ -82,14 +123,14 @@ export class TreeNodePrimitive<
82
123
  return this._data;
83
124
  }
84
125
 
85
- /** Get resolved children (resolves lazy thunk if needed) */
126
+ /** Get resolved children (resolves lazy thunk if needed, replaces Self with this node) */
86
127
  get children(): readonly AnyTreeNodePrimitive[] {
87
128
  if (this._resolvedChildren === undefined) {
88
- if (typeof this._children === "function") {
89
- this._resolvedChildren = (this._children as () => readonly AnyTreeNodePrimitive[])();
90
- } else {
91
- this._resolvedChildren = this._children as readonly AnyTreeNodePrimitive[];
92
- }
129
+ const resolved = typeof this._children === "function"
130
+ ? (this._children as () => readonly AnyTreeNodePrimitive[])()
131
+ : this._children;
132
+ // Replace Self placeholders with this node
133
+ this._resolvedChildren = resolved.map(child => isSelf(child) ? this : child) as readonly AnyTreeNodePrimitive[];
93
134
  }
94
135
  return this._resolvedChildren;
95
136
  }
@@ -104,10 +145,10 @@ export class TreeNodePrimitive<
104
145
  export const TreeNode = <
105
146
  TType extends string,
106
147
  TData extends StructPrimitive<any>,
107
- TChildren extends readonly AnyTreeNodePrimitive[] | (() => readonly AnyTreeNodePrimitive[])
148
+ const TChildren extends readonly (AnyTreeNodePrimitive | TreeNodeSelfType)[]
108
149
  >(
109
150
  type: TType,
110
151
  config: TreeNodeConfig<TData, TChildren>
111
- ): TreeNodePrimitive<TType, TData, TChildren> =>
112
- new TreeNodePrimitive(type, config);
152
+ ): TreeNodePrimitive<TType, TData, ResolveChildrenUnion<TChildren, TreeNodePrimitive<TType, TData, any>>> =>
153
+ new TreeNodePrimitive(type, config) as TreeNodePrimitive<TType, TData, ResolveChildrenUnion<TChildren, TreeNodePrimitive<TType, TData, any>>>;
113
154
 
@@ -33,7 +33,7 @@ export type InferUnionSnapshot<TVariants extends UnionVariants> = {
33
33
  /**
34
34
  * Proxy for accessing union variants
35
35
  */
36
- export interface UnionProxy<TVariants extends UnionVariants, TDiscriminator extends string, TDefined extends boolean = false> {
36
+ export interface UnionProxy<TVariants extends UnionVariants, _TDiscriminator extends string, TDefined extends boolean = false> {
37
37
  /** Gets the current union value */
38
38
  get(): MaybeUndefined<InferUnionState<TVariants>, TDefined>;
39
39
 
@@ -19,7 +19,7 @@ export class MimicServerError extends Error {
19
19
  * Error thrown when a transaction fails validation.
20
20
  */
21
21
  export class ValidationError extends MimicServerError {
22
- readonly _tag = "ValidationError";
22
+ override readonly _tag = "ValidationError";
23
23
  readonly transactionId: string;
24
24
 
25
25
  constructor(transactionId: string, message: string) {
@@ -33,7 +33,7 @@ export class ValidationError extends MimicServerError {
33
33
  * Error thrown when an operation is invalid for the current schema.
34
34
  */
35
35
  export class InvalidOperationError extends MimicServerError {
36
- readonly _tag = "InvalidOperationError";
36
+ override readonly _tag = "InvalidOperationError";
37
37
  readonly operationKind: string;
38
38
  readonly path: string;
39
39
 
@@ -49,9 +49,9 @@ export class InvalidOperationError extends MimicServerError {
49
49
  * Error thrown when an operation cannot be applied to the current state.
50
50
  */
51
51
  export class StateValidationError extends MimicServerError {
52
- readonly _tag = "StateValidationError";
52
+ override readonly _tag = "StateValidationError";
53
53
  readonly transactionId: string;
54
- readonly cause?: Error;
54
+ override readonly cause?: Error;
55
55
 
56
56
  constructor(transactionId: string, message: string, cause?: Error) {
57
57
  super(`Transaction ${transactionId} cannot be applied to current state: ${message}`);
@@ -65,7 +65,7 @@ export class StateValidationError extends MimicServerError {
65
65
  * Error thrown when attempting to apply an empty transaction.
66
66
  */
67
67
  export class EmptyTransactionError extends MimicServerError {
68
- readonly _tag = "EmptyTransactionError";
68
+ override readonly _tag = "EmptyTransactionError";
69
69
  readonly transactionId: string;
70
70
 
71
71
  constructor(transactionId: string) {
@@ -79,7 +79,7 @@ export class EmptyTransactionError extends MimicServerError {
79
79
  * Error thrown when a duplicate transaction is submitted.
80
80
  */
81
81
  export class DuplicateTransactionError extends MimicServerError {
82
- readonly _tag = "DuplicateTransactionError";
82
+ override readonly _tag = "DuplicateTransactionError";
83
83
  readonly transactionId: string;
84
84
 
85
85
  constructor(transactionId: string) {