coaction 2.1.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,446 @@
1
+ import { Transport } from "data-transport";
2
+ import { Draft, Patches } from "mutative";
3
+ import { computed, effect, effectScope, endBatch, isComputed, isEffect, isEffectScope, isSignal, signal, startBatch, trigger } from "alien-signals";
4
+
5
+ //#region packages/core/src/jsonTypes.d.ts
6
+ type JsonPrimitive = null | boolean | number | string;
7
+ type JsonValue = JsonPrimitive | JsonValue[] | {
8
+ [key: string]: JsonValue;
9
+ };
10
+ //#endregion
11
+ //#region packages/core/src/interface.d.ts
12
+ /**
13
+ * Generic object shape used by stores and slices.
14
+ */
15
+ type ISlices<T = any> = Record<PropertyKey, T>;
16
+ /**
17
+ * Recursive partial object accepted by {@link Store.setState} when merging a
18
+ * plain object payload into the current state tree.
19
+ */
20
+ type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };
21
+ /**
22
+ * Subscription callback invoked after the store publishes a state change.
23
+ */
24
+ type Listener = () => void;
25
+ /**
26
+ * Patch pair exposed to middleware compatibility hooks.
27
+ */
28
+ interface PatchTransform {
29
+ patches: Patches;
30
+ inversePatches: Patches;
31
+ }
32
+ /**
33
+ * Trace envelope emitted before and after a store method executes.
34
+ */
35
+ interface StoreTraceEvent {
36
+ /**
37
+ * The id of the method.
38
+ */
39
+ id: string;
40
+ /**
41
+ * The method name.
42
+ */
43
+ method: string;
44
+ /**
45
+ * The slice key.
46
+ */
47
+ sliceKey?: PropertyKey;
48
+ /**
49
+ * The parameters of the method.
50
+ */
51
+ parameters?: any[];
52
+ /**
53
+ * The result of the method.
54
+ */
55
+ result?: any;
56
+ }
57
+ /**
58
+ * Runtime store contract returned by {@link create} before framework-specific
59
+ * wrappers add selectors or reactivity helpers.
60
+ *
61
+ * @remarks
62
+ * `getState()` returns methods and getters alongside plain data. Methods
63
+ * extracted from the returned object keep the correct `this` binding when they
64
+ * are later invoked.
65
+ */
66
+ interface Store<T extends ISlices = ISlices> {
67
+ /**
68
+ * The name of the store.
69
+ */
70
+ name: string;
71
+ /**
72
+ * Mutate the current state.
73
+ *
74
+ * @remarks
75
+ * Pass a deep-partial object to merge fields, or pass an updater to edit a
76
+ * Mutative draft. Passing `null` is a no-op. Client-side shared stores intentionally reject direct
77
+ * `setState()` calls; trigger a store method instead.
78
+ */
79
+ setState: (
80
+ /**
81
+ * The next partial state, or an updater that mutates a draft.
82
+ */
83
+
84
+ next: DeepPartial<T> | ((draft: Draft<T>) => any) | null,
85
+ /**
86
+ * Low-level updater hook used by transports and middleware integrations.
87
+ */
88
+
89
+ updater?: (next: DeepPartial<T> | ((draft: Draft<T>) => any) | null) => [] | [T, Patches, Patches]) => void;
90
+ /**
91
+ * Read the current state object.
92
+ *
93
+ * @remarks
94
+ * The returned object includes methods and getters. Methods destructured from
95
+ * this object continue to execute against the latest store state.
96
+ */
97
+ getState: () => T;
98
+ /**
99
+ * Subscribe to state changes.
100
+ *
101
+ * @returns A function that removes the listener.
102
+ */
103
+ subscribe: (listener: Listener) => () => void;
104
+ /**
105
+ * Tear down the store.
106
+ *
107
+ * @remarks
108
+ * `destroy()` is idempotent. It clears subscriptions and disposes any
109
+ * attached transport.
110
+ */
111
+ destroy: () => void;
112
+ /**
113
+ * Indicates whether the store is local, the main shared store, or a client
114
+ * mirror of a shared store.
115
+ */
116
+ share?: 'main' | 'client' | false;
117
+ /**
118
+ * Transport used to synchronize a shared store between processes or threads.
119
+ */
120
+ transport?: Transport;
121
+ /**
122
+ * Whether `createState` was interpreted as a slices object.
123
+ */
124
+ isSliceStore: boolean;
125
+ /**
126
+ * Apply patches to the current state.
127
+ *
128
+ * @remarks
129
+ * This is a low-level hook used by transports and middleware. Application
130
+ * code should generally prefer store methods or `setState()`. Client-side
131
+ * shared-store mirrors reject direct `apply()` calls.
132
+ */
133
+ apply: (state?: T, patches?: Patches) => void;
134
+ /**
135
+ * Return the current state without methods or getters.
136
+ *
137
+ * @remarks
138
+ * Useful for serialization, inspection, or tests that only care about raw
139
+ * data.
140
+ */
141
+ getPureState: () => T;
142
+ /**
143
+ * Return the state produced during initialization before later mutations.
144
+ */
145
+ getInitialState: () => T;
146
+ /**
147
+ * @deprecated Middleware compatibility hook. Prefer typing middleware stores
148
+ * with `MiddlewareStore`.
149
+ */
150
+ patch?: (option: PatchTransform) => PatchTransform;
151
+ /**
152
+ * @deprecated Middleware compatibility hook. Prefer typing middleware stores
153
+ * with `MiddlewareStore`.
154
+ */
155
+ trace?: (options: StoreTraceEvent) => void;
156
+ }
157
+ /**
158
+ * Semantic alias for middleware-facing stores.
159
+ *
160
+ * @remarks
161
+ * Middleware implementations should type their `store` parameter as
162
+ * `MiddlewareStore` instead of relying on deprecated `patch` or `trace` hooks.
163
+ */
164
+ interface MiddlewareStore<T extends ISlices = ISlices> extends Store<T> {}
165
+ type TransportPolicyRequest = {
166
+ action: readonly string[];
167
+ args: readonly JsonValue[];
168
+ type: 'execute';
169
+ } | {
170
+ type: 'fullSync';
171
+ };
172
+ type TransportPolicy = {
173
+ /** Further restrict action paths declared by the authoritative store. */allowedActions?: readonly (readonly string[])[]; /** Authorize a decoded JSON request before serving it. */
174
+ authorize?: (request: TransportPolicyRequest) => boolean | Promise<boolean>;
175
+ /**
176
+ * Map a caught execute error to an explicitly client-visible message.
177
+ * Returning `undefined` keeps the generic redacted message.
178
+ */
179
+ mapError?: (error: unknown, request: TransportPolicyRequest) => string | undefined | Promise<string | undefined>;
180
+ };
181
+ /**
182
+ * Helper passed into {@link Slice} and {@link Slices} factories.
183
+ *
184
+ * @remarks
185
+ * Call it with no arguments to read the current store state. Call it with a
186
+ * dependency selector pair to define a computed value.
187
+ */
188
+ interface Getter<T extends ISlices> {
189
+ <P extends any[], R>(getDeps: (store: T) => readonly [...P] | [...P], selector: (...args: P) => R): R;
190
+ (): T;
191
+ }
192
+ /**
193
+ * Factory for a single store object.
194
+ *
195
+ * @remarks
196
+ * Return a plain object containing state, getters, and methods. Methods and
197
+ * getters may use `this` to access the live store state.
198
+ */
199
+ type Slice<T extends ISlices> = (
200
+ /**
201
+ * The setState is used to update the state.
202
+ */
203
+
204
+ set: Store<T>['setState'],
205
+ /**
206
+ * The getState is used to get the state.
207
+ */
208
+
209
+ get: Getter<T>,
210
+ /**
211
+ * The store is used to store the state.
212
+ */
213
+
214
+ store: Store<T>) => T;
215
+ /**
216
+ * Factory for a named slice inside a slices store.
217
+ *
218
+ * @remarks
219
+ * The returned object becomes the value stored under the slice key. When an
220
+ * object input only contains functions, prefer explicit `sliceMode` to avoid
221
+ * ambiguity between slices and a plain method-only store.
222
+ */
223
+ type Slices<T extends ISlices, K extends keyof T> = (
224
+ /**
225
+ * The setState is used to update the state.
226
+ */
227
+
228
+ set: Store<T>['setState'],
229
+ /**
230
+ * The getState is used to get the state.
231
+ */
232
+
233
+ get: Getter<T>,
234
+ /**
235
+ * The store is used to store the state.
236
+ */
237
+
238
+ store: Store<T>) => T[K];
239
+ /**
240
+ * Store enhancer invoked during store creation.
241
+ *
242
+ * @remarks
243
+ * Middleware may mutate the received store in place or return a replacement
244
+ * store object, but it must preserve the {@link Store} contract.
245
+ */
246
+ type Middleware<T extends CreateState> = (store: MiddlewareStore<T>) => MiddlewareStore<T>;
247
+ /**
248
+ * Derived state object produced by mapping slice factories to their return
249
+ * types.
250
+ */
251
+ type SliceState<T extends Record<PropertyKey, Slice<any>>> = { [K in keyof T]: ReturnType<T[K]> };
252
+ /**
253
+ * Options for creating a local store or the main side of a shared store.
254
+ */
255
+ type StoreOptions<T extends CreateState> = {
256
+ /**
257
+ * The name of the store.
258
+ */
259
+ name?: string;
260
+ /**
261
+ * @deprecated Internal worker-mode override retained for compatibility.
262
+ * Prefer passing `transport` or letting the runtime infer the environment.
263
+ */
264
+ workerType?: 'SharedWorkerInternal' | 'WebWorkerInternal';
265
+ /**
266
+ * Inject a pre-built transport for advanced shared-store setups.
267
+ */
268
+ transport?: Transport; /** Restrict requests accepted by a shared-main store. */
269
+ transportPolicy?: TransportPolicy;
270
+ /**
271
+ * Middleware chain applied before the initial state is finalized.
272
+ */
273
+ middlewares?: Middleware<T>[];
274
+ /**
275
+ * Enable patch generation.
276
+ *
277
+ * @remarks
278
+ * Required for async client stores and useful for middleware or mutable
279
+ * integrations that depend on patch streams.
280
+ */
281
+ enablePatches?: boolean;
282
+ /**
283
+ * Control how `createState` should be interpreted.
284
+ *
285
+ * @remarks
286
+ * - auto: infer from createState shape. Object maps whose values are all
287
+ * functions are ambiguous, so prefer setting `sliceMode` explicitly.
288
+ * - slices: force slices mode.
289
+ * - single: force single-store mode.
290
+ */
291
+ sliceMode?: 'auto' | 'slices' | 'single';
292
+ };
293
+ /**
294
+ * Options for creating a client mirror of a shared store.
295
+ *
296
+ * @remarks
297
+ * Methods on the returned store become promise-returning methods because
298
+ * execution happens on the main/shared store.
299
+ */
300
+ type ClientStoreOptions<T extends CreateState> = {
301
+ /**
302
+ * The name of the shared store to connect to.
303
+ */
304
+ name?: string;
305
+ /**
306
+ * Middleware chain applied to the client-side store wrapper.
307
+ */
308
+ middlewares?: Middleware<T>[];
309
+ /**
310
+ * Control how `createState` should be interpreted.
311
+ *
312
+ * @remarks
313
+ * - auto: infer from createState shape. Object maps whose values are all
314
+ * functions are ambiguous, so prefer setting `sliceMode` explicitly.
315
+ * - slices: force slices mode.
316
+ * - single: force single-store mode.
317
+ */
318
+ sliceMode?: 'auto' | 'slices' | 'single';
319
+ } & ClientTransportOptions;
320
+ /**
321
+ * Transport-related options for client/shared-store mirrors.
322
+ */
323
+ interface ClientTransportOptions {
324
+ /**
325
+ * @deprecated Internal worker-mode override retained for compatibility.
326
+ * Prefer passing `clientTransport` or `worker`.
327
+ */
328
+ workerType?: 'WebWorkerClient' | 'SharedWorkerClient';
329
+ /**
330
+ * How long the client should wait for sequence catch-up before falling back
331
+ * to `fullSync`.
332
+ *
333
+ * Increase this when worker-side execution can complete before the matching
334
+ * incremental `update` message arrives under heavy load.
335
+ *
336
+ * @default 1500
337
+ */
338
+ executeSyncTimeoutMs?: number;
339
+ /**
340
+ * Inject a pre-built client transport.
341
+ */
342
+ clientTransport?: Transport<any>;
343
+ /**
344
+ * Build a client transport from a Worker or SharedWorker instance.
345
+ */
346
+ worker?: SharedWorker | Worker;
347
+ }
348
+ /**
349
+ * Transform store methods into promise-returning methods for client stores.
350
+ */
351
+ type Asyncify<T extends object, D extends true | false> = { [K in keyof T]: T[K] extends ((...args: any[]) => any) ? (...args: Parameters<T[K]>) => Promise<Awaited<ReturnType<T[K]>>> : D extends false ? T[K] : { [P in keyof T[K]]: T[K][P] extends ((...args: any[]) => any) ? (...args: Parameters<T[K][P]>) => Promise<Awaited<ReturnType<T[K][P]>>> : T[K][P] } };
352
+ /**
353
+ * Store shape returned by {@link create} when acting as a client of a shared
354
+ * store.
355
+ *
356
+ * @remarks
357
+ * Methods return promises because they execute on the main/shared store.
358
+ */
359
+ type StoreWithAsyncFunction<T extends object, D extends true | false = false> = Store<Asyncify<T, D>> & (() => Asyncify<T, D>);
360
+ /**
361
+ * Callable store returned by {@link create} in local or main/shared mode.
362
+ */
363
+ type StoreReturn<T extends object> = Store<T> & ((...args: any[]) => T);
364
+ /**
365
+ * Accepted `create()` input shape.
366
+ *
367
+ * @remarks
368
+ * This can be either a single store factory/object or a map of slice
369
+ * factories.
370
+ */
371
+ type CreateState = ISlices | Record<PropertyKey, Slice<any>>;
372
+ type SingleStoreOptions<T extends CreateState> = StoreOptions<T> & {
373
+ sliceMode: 'single';
374
+ };
375
+ type SingleClientStoreOptions<T extends CreateState> = ClientStoreOptions<T> & {
376
+ sliceMode: 'single';
377
+ };
378
+ /**
379
+ * Overload set for {@link create}.
380
+ *
381
+ * @remarks
382
+ * - `Slice` + `StoreOptions` returns a synchronous local or main/shared store.
383
+ * - slice map + `StoreOptions` returns a synchronous slices store.
384
+ * - `Slice` + `ClientStoreOptions` returns an async client store.
385
+ * - slice map + `ClientStoreOptions` returns an async client slices store.
386
+ *
387
+ * For object inputs whose enumerable values are all functions, prefer explicit
388
+ * `sliceMode` to avoid ambiguous inference.
389
+ */
390
+ type Creator = {
391
+ <T extends ISlices>(createState: T, options: SingleStoreOptions<T>): StoreReturn<T>;
392
+ <T extends Record<PropertyKey, Slice<any>>>(createState: T, options?: StoreOptions<T>): StoreReturn<SliceState<T>>;
393
+ <T extends ISlices>(createState: Slice<T> | T, options?: StoreOptions<T>): StoreReturn<T>;
394
+ <T extends ISlices>(createState: T, options: SingleClientStoreOptions<T>): StoreWithAsyncFunction<T>;
395
+ <T extends Record<PropertyKey, Slice<any>>>(createState: T, options?: ClientStoreOptions<T>): StoreWithAsyncFunction<SliceState<T>, true>;
396
+ <T extends ISlices>(createState: Slice<T> | T, options?: ClientStoreOptions<T>): StoreWithAsyncFunction<T>;
397
+ };
398
+ //#endregion
399
+ //#region packages/core/src/create.d.ts
400
+ /**
401
+ * Create a local store, the main side of a shared store, or a client mirror of
402
+ * a shared store.
403
+ *
404
+ * @remarks
405
+ * Prefer the static `coaction/local` entry when transport support is not
406
+ * required. It excludes the JSON protocol and reconnect runtime from the
407
+ * consumer dependency graph.
408
+ */
409
+ declare const create: Creator;
410
+ //#endregion
411
+ //#region packages/core/src/getRawStateClientAction.d.ts
412
+ /**
413
+ * The authority changed while a remote action was in flight, so its side-effect
414
+ * outcome cannot be determined safely from the current client mirror.
415
+ */
416
+ declare class ActionAuthorityChangedError extends Error {
417
+ readonly code = "COACTION_ACTION_AUTHORITY_CHANGED";
418
+ readonly outcome = "unknown";
419
+ constructor(action: string);
420
+ }
421
+ //#endregion
422
+ //#region packages/core/src/lifecycle.d.ts
423
+ declare const onStoreReady: <T extends CreateState>(store: Store<T>, callback: () => void) => () => void;
424
+ //#endregion
425
+ //#region packages/core/src/utils.d.ts
426
+ declare class UnsafePatchPathError extends Error {
427
+ name: string;
428
+ }
429
+ declare class StateSchemaError extends Error {
430
+ name: string;
431
+ }
432
+ declare const isStateSchemaError: (error: unknown) => error is StateSchemaError;
433
+ declare const assertSafePatches: <T extends {
434
+ path: unknown;
435
+ }>(patches: T[] | undefined, source?: string) => void;
436
+ declare const sanitizePatches: <T extends {
437
+ path: unknown;
438
+ value?: unknown;
439
+ }>(patches: T[] | undefined, options?: {
440
+ source?: string;
441
+ warnOnDropped?: boolean;
442
+ }) => T[] | undefined;
443
+ declare const sanitizeReplacementState: <T>(source: T, seen?: WeakMap<object, unknown>) => T;
444
+ declare const sanitizeInitialStateValue: <T>(source: T, seen?: WeakMap<object, unknown>) => T;
445
+ //#endregion
446
+ export { ActionAuthorityChangedError, type StoreWithAsyncFunction as AsyncStore, type Asyncify, type ClientStoreOptions, type ISlices, type JsonPrimitive, type JsonValue, type Middleware, type MiddlewareStore, type PatchTransform, type Slice, type SliceState, type Slices, StateSchemaError, type Store, type StoreOptions, type StoreTraceEvent, type TransportPolicy, type TransportPolicyRequest, UnsafePatchPathError, assertSafePatches, computed, create, effect, effectScope, endBatch, isComputed, isEffect, isEffectScope, isSignal, isStateSchemaError, onStoreReady, sanitizeInitialStateValue, sanitizePatches, sanitizeReplacementState, signal, startBatch, trigger };