@atolljs/core 0.1.0 → 0.1.1
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/README.md +159 -59
- package/dist/contract/listSchema.d.ts +47 -0
- package/dist/contract/msgpackCodec.d.ts +7 -0
- package/dist/contract/msgpackrCodec.d.ts +2 -0
- package/dist/contract/mz.d.ts +43 -0
- package/dist/contract/sharedMemory.d.ts +350 -0
- package/dist/contract/types.d.ts +131 -0
- package/dist/contract/zod.d.ts +53 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +18 -18
- package/dist/index10.js +14 -178
- package/dist/index11.js +188 -47
- package/dist/index12.js +45 -59
- package/dist/index13.js +59 -20
- package/dist/index14.js +24 -60
- package/dist/index15.js +56 -43
- package/dist/index16.js +45 -41
- package/dist/index17.js +42 -38
- package/dist/index18.js +36 -55
- package/dist/index19.js +52 -47
- package/dist/index20.js +48 -51
- package/dist/index21.js +59 -0
- package/dist/index3.js +24 -117
- package/dist/index4.js +116 -34
- package/dist/index5.js +32 -473
- package/dist/index6.js +489 -19
- package/dist/index7.js +19 -6
- package/dist/index8.js +6 -33
- package/dist/index9.js +29 -14
- package/dist/log.d.ts +35 -0
- package/dist/observable.d.ts +30 -0
- package/dist/pool/errors.d.ts +16 -0
- package/dist/pool/memory.d.ts +19 -0
- package/dist/pool/workerClient.d.ts +80 -0
- package/dist/pool/workerPool.d.ts +115 -0
- package/dist/reactive.d.ts +46 -0
- package/dist/service.d.ts +181 -0
- package/dist/shared/sharedWorkerClient.d.ts +35 -0
- package/dist/shared/sharedWorkerHost.d.ts +21 -0
- package/dist/task.d.ts +40 -0
- package/dist/worker/defineWorker.d.ts +63 -0
- package/dist/worker/registry.d.ts +8 -0
- package/dist/worker/workerBootstrap.d.ts +1 -0
- package/package.json +14 -2
- package/src/contract/listSchema.ts +13 -8
- package/src/contract/mz.ts +1 -1
- package/src/contract/sharedMemory.ts +28 -1
- package/src/contract/types.ts +21 -0
- package/src/contract/zod.ts +44 -0
- package/src/index.ts +2 -1
- package/src/pool/workerPool.ts +27 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import type { Prettify, Schema } from './types';
|
|
2
|
+
import { z } from './zod';
|
|
3
|
+
/**
|
|
4
|
+
* Declares the datatype of a single field in the shared memory contract.
|
|
5
|
+
* `byteLength` is the fixed region reserved for the field; structured types
|
|
6
|
+
* (string, object, array) carry a 4-byte length header inside that region.
|
|
7
|
+
*/
|
|
8
|
+
export interface FieldDescriptor<T = unknown> {
|
|
9
|
+
readonly kind: string;
|
|
10
|
+
readonly byteLength: number;
|
|
11
|
+
readonly schema?: Schema<T>;
|
|
12
|
+
readonly __fieldType?: T;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A group of fields under one intent — any plain-object member of the spec
|
|
16
|
+
* that isn't a descriptor. Group names are the user's vocabulary:
|
|
17
|
+
* `lists`, `state`, `signals`, `telemetry` — one level deep.
|
|
18
|
+
*/
|
|
19
|
+
export type FieldGroup = Record<string, FieldDescriptor>;
|
|
20
|
+
export type SharedSpec = Record<string, FieldDescriptor | FieldGroup>;
|
|
21
|
+
/** Spec keys that hold a descriptor directly (not an intent group). */
|
|
22
|
+
export type FlatKey<S extends SharedSpec> = {
|
|
23
|
+
[K in keyof S]: S[K] extends FieldDescriptor ? K : never;
|
|
24
|
+
}[keyof S];
|
|
25
|
+
/**
|
|
26
|
+
* Every legal field path of a spec — flat fields give their key (`metrics`),
|
|
27
|
+
* grouped fields give `group.field` (`signals.seedProgress`).
|
|
28
|
+
*/
|
|
29
|
+
export type SpecPath<S extends SharedSpec> = {
|
|
30
|
+
[K in keyof S & string]: S[K] extends FieldGroup ? `${K}.${keyof S[K] & string}` : K;
|
|
31
|
+
}[keyof S & string];
|
|
32
|
+
/**
|
|
33
|
+
* Serializer for structured (object/array) fields. Both threads must use the
|
|
34
|
+
* same codec — it comes from the shared contract definition, so it does.
|
|
35
|
+
*/
|
|
36
|
+
export interface Codec {
|
|
37
|
+
encode(value: unknown): Uint8Array;
|
|
38
|
+
decode(data: Uint8Array): unknown;
|
|
39
|
+
}
|
|
40
|
+
export declare const jsonCodec: Codec;
|
|
41
|
+
/**
|
|
42
|
+
* Context handed to connector factories at bind time.
|
|
43
|
+
*/
|
|
44
|
+
export interface ConnectorContext {
|
|
45
|
+
buffer: SharedArrayBuffer;
|
|
46
|
+
codec: Codec;
|
|
47
|
+
/** Slot in the shared version counter — bump + notify on granular writes. */
|
|
48
|
+
version?: {
|
|
49
|
+
view: Int32Array;
|
|
50
|
+
index: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A connector factory builds a connector for one field kind over a region of
|
|
55
|
+
* the shared buffer. Factories are the plugin unit of the SDK: register new
|
|
56
|
+
* kinds globally via `registerConnectorFactory` or override per contract via
|
|
57
|
+
* `SharedMemoryOptions.plugins`.
|
|
58
|
+
*/
|
|
59
|
+
export type ConnectorFactory = (descriptor: FieldDescriptor, ctx: ConnectorContext, byteOffset: number) => Connector<any>;
|
|
60
|
+
/** Registers (or overrides) the connector factory for a field kind, SDK-wide. */
|
|
61
|
+
export declare function registerConnectorFactory(kind: string, factory: ConnectorFactory): void;
|
|
62
|
+
export interface SharedMemoryOptions {
|
|
63
|
+
codec?: Codec;
|
|
64
|
+
plugins?: Record<string, ConnectorFactory>;
|
|
65
|
+
}
|
|
66
|
+
/** The element type a field descriptor holds — `InferField<typeof memory.spec.x>`. */
|
|
67
|
+
export type InferField<D> = D extends FieldDescriptor<infer T> ? T : unknown;
|
|
68
|
+
/**
|
|
69
|
+
* A bound connector reads and writes a single field of the shared memory
|
|
70
|
+
* contract. Both threads hold an identical connector for each field.
|
|
71
|
+
*/
|
|
72
|
+
export interface Connector<T> {
|
|
73
|
+
readonly byteOffset: number;
|
|
74
|
+
readonly byteLength: number;
|
|
75
|
+
read(): T;
|
|
76
|
+
write(value: T): void;
|
|
77
|
+
/** Internal: slot in the shared version counter block, bumped on every write. */
|
|
78
|
+
readonly _version?: {
|
|
79
|
+
view: Int32Array;
|
|
80
|
+
index: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export type ScalarKind = 'i8' | 'u8' | 'i16' | 'u16' | 'i32' | 'u32' | 'f32' | 'f64' | 'i64' | 'u64';
|
|
84
|
+
/** A fixed-layout member spec: a scalar kind, `{ string: n }` for an inline UTF-8 field of n bytes, or `{ bool: true }` for a flag byte. */
|
|
85
|
+
export type ListFieldSpec = ScalarKind | {
|
|
86
|
+
string: number;
|
|
87
|
+
} | {
|
|
88
|
+
bool: true;
|
|
89
|
+
};
|
|
90
|
+
export type ListSpec = Record<string, ListFieldSpec>;
|
|
91
|
+
/**
|
|
92
|
+
* A fixed-width member inside a record or element position — a binary spec
|
|
93
|
+
* token (`'u32'`, `{ string: n }`, `{ bool: true }`) or a zod schema that
|
|
94
|
+
* expresses the same thing (`mz.u32()`, `mz.int(0, 3)`, `mz.string(n)`).
|
|
95
|
+
* Schemas are compiled to tokens for layout and kept as the field's
|
|
96
|
+
* validation schema — one declaration.
|
|
97
|
+
*/
|
|
98
|
+
export type ListMember = ListFieldSpec | z.ZodTypeAny;
|
|
99
|
+
export type ListFields = Record<string, ListMember>;
|
|
100
|
+
/** The plain-object shape of one record in a list field — `z.output` of the declared record schema. */
|
|
101
|
+
export type ListRecord<S extends z.ZodObject> = z.output<S>;
|
|
102
|
+
export interface ListDescriptor<S extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> extends Omit<FieldDescriptor<ListRecord<S>[]>, 'schema'> {
|
|
103
|
+
readonly kind: 'list';
|
|
104
|
+
/**
|
|
105
|
+
* The declared record schema — an `mz.object` of fixed-width members. It
|
|
106
|
+
* IS the layout source AND the record validator (`schemas.lists.x` is this
|
|
107
|
+
* same object). Note it parses one record, not the whole array.
|
|
108
|
+
*/
|
|
109
|
+
readonly schema: S;
|
|
110
|
+
/** The compiled binary spec per member — always scalar tokens/`{string:n}`/`{bool}`. */
|
|
111
|
+
readonly layout: Record<keyof z.output<S> & string, ListFieldSpec>;
|
|
112
|
+
readonly offsets: Record<keyof z.output<S> & string, number>;
|
|
113
|
+
readonly recordSize: number;
|
|
114
|
+
readonly count: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A connector over a fixed-layout record array. `readAt`/`writeAt` access a
|
|
118
|
+
* single record directly via DataView — no encode/decode pass, ever.
|
|
119
|
+
* `readAt(index, out)` reuses `out` for allocation-free scans.
|
|
120
|
+
*/
|
|
121
|
+
export interface ListConnector<R> extends Connector<R[]> {
|
|
122
|
+
readonly recordCount: number;
|
|
123
|
+
readonly recordSize: number;
|
|
124
|
+
readAt(index: number): R;
|
|
125
|
+
/** Read into `out`; pass `fields` to read a subset (skips e.g. inline strings). */
|
|
126
|
+
readAt(index: number, out: Partial<R>, fields?: (keyof R)[]): R;
|
|
127
|
+
writeAt(index: number, record: R): void;
|
|
128
|
+
/** Write only the listed fields — partial updates leave other fields untouched. */
|
|
129
|
+
writeAt(index: number, record: Partial<R>, fields: (keyof R)[]): void;
|
|
130
|
+
/**
|
|
131
|
+
* Bump the field version counter and wake remote observers. `writeAt` is
|
|
132
|
+
* intentionally pure memory access — call commit() after a batch of writes
|
|
133
|
+
* to propagate them (bursts coalesce to latest anyway).
|
|
134
|
+
*/
|
|
135
|
+
commit(): void;
|
|
136
|
+
}
|
|
137
|
+
type AccessMember<V> = V extends FieldDescriptor ? V extends ListDescriptor<infer S> ? ListConnector<ListRecord<S>> : Connector<InferField<V>> : V extends FieldGroup ? Prettify<SharedAccess<V>> : never;
|
|
138
|
+
export type SharedAccess<S extends SharedSpec> = {
|
|
139
|
+
[K in keyof S]: AccessMember<S[K]>;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* The connector a `SpecPath` resolves to — `PathConnector<S, 'state.metrics'>`
|
|
143
|
+
* digs through the intent group to the member's connector.
|
|
144
|
+
*/
|
|
145
|
+
export type PathConnector<S extends SharedSpec, P> = P extends `${infer G}.${infer F}` ? S[G] extends FieldGroup ? F extends keyof S[G] ? AccessMember<S[G][F]> : never : never : P extends keyof S ? AccessMember<S[P]> : never;
|
|
146
|
+
/**
|
|
147
|
+
* A `field.object({ schema })` whose zod members compiled to an inline fixed
|
|
148
|
+
* layout — an 8-byte header (`written` flag + reserved) plus one record.
|
|
149
|
+
* `maxBytes` never appears: the byte width IS the schema.
|
|
150
|
+
*/
|
|
151
|
+
export interface FixedObjectDescriptor<T = unknown> extends FieldDescriptor<T> {
|
|
152
|
+
readonly kind: 'object';
|
|
153
|
+
readonly schema: Schema<T>;
|
|
154
|
+
/** The compiled binary spec per member — always scalar tokens/`{string:n}`/`{bool}`. */
|
|
155
|
+
readonly layout: Record<string, ListFieldSpec>;
|
|
156
|
+
readonly offsets: Record<string, number>;
|
|
157
|
+
readonly recordSize: number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A `field.array({ schema })` whose bounded zod array compiled to an inline
|
|
161
|
+
* layout — an 8-byte header (`written` flag + element count) plus `capacity`
|
|
162
|
+
* fixed-stride elements.
|
|
163
|
+
*/
|
|
164
|
+
export interface FixedArrayDescriptor<T = unknown> extends FieldDescriptor<T[]> {
|
|
165
|
+
readonly kind: 'array';
|
|
166
|
+
readonly schema: Schema<T[]>;
|
|
167
|
+
/** The compiled element spec — scalar token, `{string:n}`, or `{bool}`. */
|
|
168
|
+
readonly element: ListFieldSpec;
|
|
169
|
+
/** Bytes per element, aligned. */
|
|
170
|
+
readonly elementStride: number;
|
|
171
|
+
readonly capacity: number;
|
|
172
|
+
}
|
|
173
|
+
interface ObjectFieldFactory {
|
|
174
|
+
/**
|
|
175
|
+
* Inline fixed layout — the schema is a zod object of fixed-width members
|
|
176
|
+
* (`mz` helpers, `z.uint32()`, bounded ints, `mz.string(n)`, `z.boolean()`);
|
|
177
|
+
* `byteLength` derives from the member widths. Compile fails loudly for
|
|
178
|
+
* members with no fixed width (unbounded strings, nested objects).
|
|
179
|
+
*/
|
|
180
|
+
<S extends z.ZodObject<z.ZodRawShape>>(options: {
|
|
181
|
+
schema: S;
|
|
182
|
+
}): FixedObjectDescriptor<z.output<S>>;
|
|
183
|
+
/** Codec-encoded blob — explicit byte budget for arbitrary payloads. */
|
|
184
|
+
<T = unknown>(options: {
|
|
185
|
+
maxBytes: number;
|
|
186
|
+
schema?: Schema<T>;
|
|
187
|
+
}): FieldDescriptor<T>;
|
|
188
|
+
/** Schema with no derivable width — rejected at construction. */
|
|
189
|
+
<T = unknown>(options: {
|
|
190
|
+
schema: Schema<T>;
|
|
191
|
+
}): FieldDescriptor<T>;
|
|
192
|
+
}
|
|
193
|
+
interface ArrayFieldFactory {
|
|
194
|
+
/**
|
|
195
|
+
* Inline fixed layout — the schema is a bounded zod array
|
|
196
|
+
* (`mz.array(mz.u32()).max(n)`, `.length(n)`); `byteLength` derives from
|
|
197
|
+
* the element width × the bound. Elements must be fixed-width members
|
|
198
|
+
* (scalars, `mz.string(n)`, booleans) — use `field.list` for record arrays.
|
|
199
|
+
*/
|
|
200
|
+
<S extends z.ZodArray>(options: {
|
|
201
|
+
schema: S;
|
|
202
|
+
}): FixedArrayDescriptor<S extends z.ZodArray<infer E> ? z.output<E> : unknown>;
|
|
203
|
+
/** Codec-encoded blob — explicit byte budget for arbitrary payloads. */
|
|
204
|
+
<T = unknown>(options: {
|
|
205
|
+
maxBytes: number;
|
|
206
|
+
schema?: Schema<T[]>;
|
|
207
|
+
}): FieldDescriptor<T[]>;
|
|
208
|
+
/** Schema with no derivable width — rejected at construction. */
|
|
209
|
+
<T = unknown>(options: {
|
|
210
|
+
schema: Schema<T[]>;
|
|
211
|
+
}): FieldDescriptor<T[]>;
|
|
212
|
+
}
|
|
213
|
+
interface StringFieldFactory {
|
|
214
|
+
/** Explicit byte budget — 4-byte length header + up to `maxBytes` of UTF-8. */
|
|
215
|
+
(options: {
|
|
216
|
+
maxBytes: number;
|
|
217
|
+
schema?: Schema<string>;
|
|
218
|
+
}): FieldDescriptor<string>;
|
|
219
|
+
/** Budget derived from the schema — `mz.string(n)` / `z.string().meta({ bytes: n })`. */
|
|
220
|
+
(options: {
|
|
221
|
+
schema: Schema<string>;
|
|
222
|
+
}): FieldDescriptor<string>;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Field datatype helpers for `defineSharedMemory`.
|
|
226
|
+
*/
|
|
227
|
+
export declare const field: {
|
|
228
|
+
number: () => FieldDescriptor<number>;
|
|
229
|
+
boolean: () => FieldDescriptor<boolean>;
|
|
230
|
+
string: StringFieldFactory;
|
|
231
|
+
object: ObjectFieldFactory;
|
|
232
|
+
array: ArrayFieldFactory;
|
|
233
|
+
int32Array: ({ length }: {
|
|
234
|
+
length: number;
|
|
235
|
+
}) => FieldDescriptor<Int32Array>;
|
|
236
|
+
float64Array: ({ length }: {
|
|
237
|
+
length: number;
|
|
238
|
+
}) => FieldDescriptor<Float64Array>;
|
|
239
|
+
bigInt64Array: ({ length }: {
|
|
240
|
+
length: number;
|
|
241
|
+
}) => FieldDescriptor<BigInt64Array>;
|
|
242
|
+
uint8Array: ({ length }: {
|
|
243
|
+
length: number;
|
|
244
|
+
}) => FieldDescriptor<Uint8Array>;
|
|
245
|
+
/**
|
|
246
|
+
* A fixed-layout array of `count` records. `schema` is an `mz.object` of
|
|
247
|
+
* fixed-width members (`mz.u32()`, `mz.int(0, 3)`, `mz.string(n)`,
|
|
248
|
+
* `mz.boolean()`) — the same schema `field.object` takes, repeated `count`
|
|
249
|
+
* times. Every record occupies `recordSize` bytes at a computable offset:
|
|
250
|
+
* reads and writes are direct memory access, no serialization.
|
|
251
|
+
*/
|
|
252
|
+
list: <S extends z.ZodObject<z.ZodRawShape>>({ schema, count }: {
|
|
253
|
+
schema: S;
|
|
254
|
+
count: number;
|
|
255
|
+
}) => ListDescriptor<S>;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* The zod schema for a field's logical unit: the declared `schema` for
|
|
259
|
+
* object/array/list fields (for lists, the record schema), and a derived
|
|
260
|
+
* scalar/typed-array schema for everything else.
|
|
261
|
+
*/
|
|
262
|
+
export type FieldSchema<D> = 'schema' extends keyof D ? NonNullable<D['schema']> : z.ZodType<InferField<D>>;
|
|
263
|
+
/**
|
|
264
|
+
* A zod schema per field — `memory.schemas.<field>` re-exports what the spec
|
|
265
|
+
* declared inline. Intent groups mirror their spec shape:
|
|
266
|
+
* `memory.schemas.lists.incidents` is the record schema.
|
|
267
|
+
*/
|
|
268
|
+
type SchemaMember<V> = V extends FieldDescriptor ? FieldSchema<V> : V extends FieldGroup ? Prettify<SpecSchemas<V>> : never;
|
|
269
|
+
export type SpecSchemas<S extends SharedSpec> = {
|
|
270
|
+
[K in keyof S]: SchemaMember<S[K]>;
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* A shared memory contract: a fixed, deterministic layout of typed fields over
|
|
274
|
+
* a SharedArrayBuffer. Both threads import the same contract object; the pool
|
|
275
|
+
* binds it on the main thread and the worker bootstrap binds it inside workers,
|
|
276
|
+
* guaranteeing identical memory usage on both sides.
|
|
277
|
+
*/
|
|
278
|
+
export declare class SharedMemory<S extends SharedSpec = SharedSpec> {
|
|
279
|
+
readonly totalBytes: number;
|
|
280
|
+
/** The field descriptors the contract was defined with — the inline spec. */
|
|
281
|
+
readonly spec: S;
|
|
282
|
+
/**
|
|
283
|
+
* One zod schema per field, derived from the spec: declared schemas on
|
|
284
|
+
* object/array fields, the record schema on list fields, and derived
|
|
285
|
+
* scalar schemas (z.number(), z.string(), …) elsewhere.
|
|
286
|
+
*/
|
|
287
|
+
readonly schemas: SpecSchemas<S>;
|
|
288
|
+
private readonly codec;
|
|
289
|
+
private readonly plugins?;
|
|
290
|
+
private readonly entries;
|
|
291
|
+
private readonly offsets;
|
|
292
|
+
private readonly versionOffset;
|
|
293
|
+
private readonly connectors;
|
|
294
|
+
private readonly boundListeners;
|
|
295
|
+
private isBound;
|
|
296
|
+
private boundBuffer?;
|
|
297
|
+
constructor(spec: S, options?: SharedMemoryOptions);
|
|
298
|
+
/** Binds this contract's connectors to the shared buffer. Called by the SDK on each thread. */
|
|
299
|
+
bind(buffer: SharedArrayBuffer): void;
|
|
300
|
+
/** True once the contract has been bound to a buffer on this thread. */
|
|
301
|
+
get bound(): boolean;
|
|
302
|
+
/**
|
|
303
|
+
* The buffer this contract is bound to on this thread — the raw backing
|
|
304
|
+
* store persistence adapters and snapshot tooling read/write through.
|
|
305
|
+
* Throws while unbound.
|
|
306
|
+
*/
|
|
307
|
+
get buffer(): SharedArrayBuffer;
|
|
308
|
+
/**
|
|
309
|
+
* Every field's address in the layout — `{ path, byteOffset, byteLength }`.
|
|
310
|
+
* Persistence adapters use this to read/write field regions directly and
|
|
311
|
+
* to pair each region with its `connector(path)._version` slot.
|
|
312
|
+
*/
|
|
313
|
+
fields(): {
|
|
314
|
+
path: string;
|
|
315
|
+
byteOffset: number;
|
|
316
|
+
byteLength: number;
|
|
317
|
+
}[];
|
|
318
|
+
/**
|
|
319
|
+
* Fires when the contract is bound on this thread — immediately if already
|
|
320
|
+
* bound. Returns an unsubscribe function.
|
|
321
|
+
*/
|
|
322
|
+
onBound(cb: () => void): () => void;
|
|
323
|
+
/** Typed lookup for flat (ungrouped) field keys. */
|
|
324
|
+
connector<K extends FlatKey<S>>(key: K): SharedAccess<S>[K];
|
|
325
|
+
/**
|
|
326
|
+
* Looks up a bound connector by its field path — `incidents` for flat
|
|
327
|
+
* fields, `lists.incidents` for grouped ones.
|
|
328
|
+
*/
|
|
329
|
+
connector(path: string): Connector<any>;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Defines a shared memory contract. Spec values are field descriptors or
|
|
333
|
+
* intent groups (one level of nesting — the group's name is the intent):
|
|
334
|
+
*
|
|
335
|
+
* defineSharedMemory({
|
|
336
|
+
* lists: { incidents: field.list({ schema, count }) },
|
|
337
|
+
* state: { metrics: field.object({ maxBytes, schema }) },
|
|
338
|
+
* signals: { progress: field.number() },
|
|
339
|
+
* });
|
|
340
|
+
*
|
|
341
|
+
* The returned object exposes a typed connector per field, nested the same
|
|
342
|
+
* way: `memory.lists.incidents.readAt(0)`, `memory.signals.progress.write(…)`.
|
|
343
|
+
* Flat members (`memory.counter`) keep working for ungrouped fields.
|
|
344
|
+
*/
|
|
345
|
+
export declare function defineSharedMemory<S extends SharedSpec>(spec: S, options?: SharedMemoryOptions): SharedMemory<S> & Prettify<SharedAccess<S>>;
|
|
346
|
+
/** Binds every defined shared memory contract to the buffer. Used by the worker bootstrap. */
|
|
347
|
+
export declare function bindSharedMemories(buffer: SharedArrayBuffer): void;
|
|
348
|
+
/** How many shared-memory contracts this module graph has defined. */
|
|
349
|
+
export declare function getDefinedSharedMemoryCount(): number;
|
|
350
|
+
export {};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { SharedAccess, SharedMemory, SharedSpec } from './sharedMemory';
|
|
2
|
+
export type ViewType = 'Int32' | 'Float64' | 'BigInt64' | 'Uint8';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal validation contract — compatible with zod schemas (`z.object(...)`)
|
|
5
|
+
* and any validator exposing `parse`.
|
|
6
|
+
*/
|
|
7
|
+
export interface Schema<T> {
|
|
8
|
+
parse(value: unknown): T;
|
|
9
|
+
}
|
|
10
|
+
export type Logger = (...args: unknown[]) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Flattens a computed object type into its concrete shape so IDE hovers show
|
|
13
|
+
* `{ id: number; site: string }` instead of the mapped-type machinery that
|
|
14
|
+
* produced it. Applied to synthetic types (e.g. list records); user-named
|
|
15
|
+
* types keep their names since the name is the better display.
|
|
16
|
+
*/
|
|
17
|
+
export type Prettify<T> = {
|
|
18
|
+
[K in keyof T]: T[K];
|
|
19
|
+
} & {};
|
|
20
|
+
export interface MemoryConfig {
|
|
21
|
+
initialPages?: number;
|
|
22
|
+
maximumPages?: number;
|
|
23
|
+
growthFactor?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Runtime handle for a memory-persistence adapter — e.g. the Redis adapter
|
|
27
|
+
* in '@atolljs/node/redis'. The adapter mirrors the bound contract's field
|
|
28
|
+
* regions to external storage; the buffer stays the synchronous source of
|
|
29
|
+
* truth.
|
|
30
|
+
*/
|
|
31
|
+
export interface MemoryPersistence {
|
|
32
|
+
/** Resolves when initial state has been restored into the local buffer. */
|
|
33
|
+
readonly ready?: Promise<unknown>;
|
|
34
|
+
/** Push dirty fields now — most adapters also flush on an interval. */
|
|
35
|
+
flush?(): Promise<unknown>;
|
|
36
|
+
/** Final flush + release external resources. Invoked by pool.terminate(). */
|
|
37
|
+
stop(): void | Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface WorkerPoolConfig<S extends SharedSpec = SharedSpec, T extends TaskMap = TaskMap> {
|
|
40
|
+
/** Worker entry as a URL — e.g. `new URL('./worker.ts', import.meta.url)`. */
|
|
41
|
+
workerUrl?: URL;
|
|
42
|
+
/**
|
|
43
|
+
* Bundler-detectable worker factory. Most bundlers only emit a worker chunk
|
|
44
|
+
* when the entry appears inline, so prefer this form:
|
|
45
|
+
* `createWorker: () => new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' })`.
|
|
46
|
+
* One of `workerUrl` / `createWorker` is required.
|
|
47
|
+
*/
|
|
48
|
+
createWorker?: () => Worker;
|
|
49
|
+
/**
|
|
50
|
+
* The contract the pool binds and hands to each worker via INIT_MEMORY.
|
|
51
|
+
* Omit for message-only workers — the SharedArrayBuffer/isolation check is
|
|
52
|
+
* skipped and workers receive a plain `{ type: 'INIT' }` handshake.
|
|
53
|
+
*/
|
|
54
|
+
sharedMemory?: SharedMemory<S> & Prettify<SharedAccess<S>>;
|
|
55
|
+
/**
|
|
56
|
+
* Persistence adapter for `sharedMemory` — invoked with the contract right
|
|
57
|
+
* after the pool binds it, and its `stop()` runs on `terminate()`.
|
|
58
|
+
* `redisMemoryAdapter(client)` from '@atolljs/node/redis' produces one.
|
|
59
|
+
*/
|
|
60
|
+
persistence?: (memory: SharedMemory<S>) => MemoryPersistence | undefined;
|
|
61
|
+
/** Worker count — a number, or 'auto' (the default) for navigator.hardwareConcurrency ?? 4. */
|
|
62
|
+
poolSize?: number | 'auto';
|
|
63
|
+
memory?: MemoryConfig;
|
|
64
|
+
/** Max in-flight tasks per worker (default 1); excess tasks queue FIFO. */
|
|
65
|
+
concurrency?: number;
|
|
66
|
+
/** Max queued tasks (default Infinity); a full queue rejects with PoolQueueFullError. */
|
|
67
|
+
maxQueue?: number;
|
|
68
|
+
/** Default true — a worker 'error' rejects its in-flight tasks and spawns a replacement. */
|
|
69
|
+
respawn?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Default per-task timeout in ms (per-call `options.timeout` overrides).
|
|
72
|
+
* The timer starts at enqueue, so it bounds queue-wait + execution together.
|
|
73
|
+
*/
|
|
74
|
+
taskTimeout?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Named task contracts. Keys become first-class pool methods:
|
|
77
|
+
* `tasks: { queryIncidents: QueryIncidents }` → `pool.queryIncidents(q)`.
|
|
78
|
+
*/
|
|
79
|
+
tasks?: T;
|
|
80
|
+
}
|
|
81
|
+
export interface SharedWorkerConfig<S extends SharedSpec = SharedSpec, T extends TaskMap = TaskMap> {
|
|
82
|
+
/** SharedWorker entry as a URL — e.g. `new URL('./worker.ts', import.meta.url)`. */
|
|
83
|
+
workerUrl?: URL;
|
|
84
|
+
/**
|
|
85
|
+
* Bundler-detectable factory — `createWorker: () => new SharedWorker(new URL('./worker.ts', import.meta.url), { type: 'module' })`.
|
|
86
|
+
* One of `workerUrl` / `createWorker` / `port` is required.
|
|
87
|
+
*/
|
|
88
|
+
createWorker?: () => SharedWorker;
|
|
89
|
+
/** A pre-opened MessagePort — tests, or hosts that forward a port another way. */
|
|
90
|
+
port?: MessagePort;
|
|
91
|
+
sharedMemory: SharedMemory<S> & Prettify<SharedAccess<S>>;
|
|
92
|
+
/**
|
|
93
|
+
* Buffer sizing hint sent on connect — the first connected client wins;
|
|
94
|
+
* the shared worker allocates the buffer once and shares it with everyone.
|
|
95
|
+
*/
|
|
96
|
+
memory?: MemoryConfig;
|
|
97
|
+
/**
|
|
98
|
+
* Named task contracts. Keys become first-class client methods:
|
|
99
|
+
* `tasks: { queryIncidents: QueryIncidents }` → `client.queryIncidents(q)`.
|
|
100
|
+
*/
|
|
101
|
+
tasks?: T;
|
|
102
|
+
/** Connect handshake timeout in ms (default 10000). */
|
|
103
|
+
connectTimeoutMs?: number;
|
|
104
|
+
}
|
|
105
|
+
export interface TaskContract<Args extends any[] = any[], Result = any> {
|
|
106
|
+
taskId: string;
|
|
107
|
+
dataType?: ViewType;
|
|
108
|
+
argsSchema?: Schema<Args>;
|
|
109
|
+
resultSchema?: Schema<Result>;
|
|
110
|
+
}
|
|
111
|
+
/** A map of named task contracts — keys become first-class WorkerPool methods. */
|
|
112
|
+
export type TaskMap = Record<string, TaskContract<any[], any>>;
|
|
113
|
+
/**
|
|
114
|
+
* The method surface a WorkerPool exposes for a task map: each contract
|
|
115
|
+
* becomes a function with its declared args returning `Promise<Result>`.
|
|
116
|
+
*/
|
|
117
|
+
export type PoolTasks<T extends TaskMap> = {
|
|
118
|
+
[K in keyof T]: T[K] extends TaskContract<infer Args, infer Result> ? (...args: Args) => Promise<Result> : never;
|
|
119
|
+
};
|
|
120
|
+
export interface TaskMessage {
|
|
121
|
+
taskId: string;
|
|
122
|
+
args?: any[];
|
|
123
|
+
memoryOffset?: number;
|
|
124
|
+
byteLength?: number;
|
|
125
|
+
}
|
|
126
|
+
export interface TaskResult {
|
|
127
|
+
taskId: string;
|
|
128
|
+
success: boolean;
|
|
129
|
+
result?: any;
|
|
130
|
+
error?: string;
|
|
131
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK's vendored zod surface — everything the contract layer needs,
|
|
3
|
+
* narrowed.
|
|
4
|
+
*
|
|
5
|
+
* `import { z } from 'zod'` bundles the ENTIRE classic namespace in consumer
|
|
6
|
+
* builds (~440KB minified): the `z` object can't be property-shaken, so it
|
|
7
|
+
* drags in every locale, the JSON-schema processors, and the compile
|
|
8
|
+
* pipeline. Named imports tree-shake (~93KB for the full set below).
|
|
9
|
+
*
|
|
10
|
+
* `instanceof` guards use the `$Zod*` base classes from `zod/v4/core` —
|
|
11
|
+
* classic AND mini schemas both extend them, so user-supplied schemas match
|
|
12
|
+
* either way. Constructors stay classic (`zod` named imports) because `mz`
|
|
13
|
+
* output and `memory.schemas` are documented as ordinary zod — `.meta()`,
|
|
14
|
+
* `.refine()`, `.parse()` must all work and compose with the consumer's own
|
|
15
|
+
* `z.object(...)`.
|
|
16
|
+
*/
|
|
17
|
+
import { array, bigint, boolean, instanceof as instanceof_, int32, int64, float32, float64, number, object, string, uint32, uint64, unknown as unknown_ } from 'zod';
|
|
18
|
+
import { $ZodArray, $ZodObject, $ZodString, $ZodType } from 'zod/v4/core';
|
|
19
|
+
import type * as zod from 'zod';
|
|
20
|
+
export declare const z: {
|
|
21
|
+
number: typeof number;
|
|
22
|
+
string: typeof string;
|
|
23
|
+
boolean: typeof boolean;
|
|
24
|
+
bigint: typeof bigint;
|
|
25
|
+
object: typeof object;
|
|
26
|
+
array: typeof array;
|
|
27
|
+
instanceof: typeof instanceof_;
|
|
28
|
+
unknown: typeof unknown_;
|
|
29
|
+
int32: typeof int32;
|
|
30
|
+
uint32: typeof uint32;
|
|
31
|
+
float32: typeof float32;
|
|
32
|
+
float64: typeof float64;
|
|
33
|
+
int64: typeof int64;
|
|
34
|
+
uint64: typeof uint64;
|
|
35
|
+
/** Core classes — instanceof-matching for user schemas (classic or mini). */
|
|
36
|
+
ZodType: zod.core.$constructor<$ZodType<unknown, unknown, zod.core.$ZodTypeInternals<unknown, unknown>>, zod.core.$ZodTypeDef>;
|
|
37
|
+
ZodObject: zod.core.$constructor<$ZodObject<Readonly<Readonly<{
|
|
38
|
+
[k: string]: $ZodType<unknown, unknown, zod.core.$ZodTypeInternals<unknown, unknown>>;
|
|
39
|
+
}>>, zod.core.$ZodObjectConfig>, zod.core.$ZodObjectDef<Readonly<Readonly<{
|
|
40
|
+
[k: string]: $ZodType<unknown, unknown, zod.core.$ZodTypeInternals<unknown, unknown>>;
|
|
41
|
+
}>>>>;
|
|
42
|
+
ZodArray: zod.core.$constructor<$ZodArray<$ZodType<unknown, unknown, zod.core.$ZodTypeInternals<unknown, unknown>>>, zod.core.$ZodArrayDef<$ZodType<unknown, unknown, zod.core.$ZodTypeInternals<unknown, unknown>>>>;
|
|
43
|
+
ZodString: zod.core.$constructor<$ZodString<unknown>, zod.core.$ZodStringDef>;
|
|
44
|
+
};
|
|
45
|
+
export declare namespace z {
|
|
46
|
+
type ZodTypeAny = zod.ZodTypeAny;
|
|
47
|
+
type ZodType<Output = unknown, Input = unknown> = zod.ZodType<Output, Input>;
|
|
48
|
+
type ZodString = zod.ZodString;
|
|
49
|
+
type ZodArray<T extends zod.ZodTypeAny = zod.ZodTypeAny> = zod.ZodArray<T>;
|
|
50
|
+
type ZodObject<S extends zod.ZodRawShape = zod.ZodRawShape> = zod.ZodObject<S>;
|
|
51
|
+
type ZodRawShape = zod.ZodRawShape;
|
|
52
|
+
type output<T extends zod.ZodTypeAny> = zod.output<T>;
|
|
53
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { bindSharedMemories, defineSharedMemory, field, getDefinedSharedMemoryCount, jsonCodec, registerConnectorFactory } from './contract/sharedMemory';
|
|
2
|
+
export { mz } from './contract/mz';
|
|
3
|
+
export type { Codec, Connector, ConnectorContext, ConnectorFactory, FieldDescriptor, FieldGroup, FieldSchema, FixedArrayDescriptor, FixedObjectDescriptor, FlatKey, InferField, PathConnector, SpecPath, SpecSchemas, SharedAccess, SharedMemory, SharedMemoryOptions, SharedSpec, ScalarKind, ListConnector, ListDescriptor, ListFieldSpec, ListFields, ListMember, ListRecord, ListSpec, } from './contract/sharedMemory';
|
|
4
|
+
export { msgpackCodec } from './contract/msgpackCodec';
|
|
5
|
+
export { msgpackrCodec } from './contract/msgpackrCodec';
|
|
6
|
+
export { listSchema } from './contract/listSchema';
|
|
7
|
+
export type { ListZodShape } from './contract/listSchema';
|
|
8
|
+
export type { MemoryConfig, PoolTasks, Prettify, Schema, Logger, TaskContract, TaskMap, TaskMessage, TaskResult, MemoryPersistence, SharedWorkerConfig, ViewType, WorkerPoolConfig, } from './contract/types';
|
|
9
|
+
export { MemoryManager } from './pool/memory';
|
|
10
|
+
export { WorkerPool } from './pool/workerPool';
|
|
11
|
+
export type { PoolStats, RunOptions } from './pool/workerPool';
|
|
12
|
+
export { PoolQueueFullError, TaskAbortedError, TaskTimeoutError, WorkerCrashedError, } from './pool/errors';
|
|
13
|
+
export { connectWorker, workerClient } from './pool/workerClient';
|
|
14
|
+
export type { ClientMethods, ConnectWorkerConfig, WorkerClient, WorkerMethods, } from './pool/workerClient';
|
|
15
|
+
export { connectSharedWorker } from './shared/sharedWorkerClient';
|
|
16
|
+
export type { SharedWorkerClient } from './shared/sharedWorkerClient';
|
|
17
|
+
export { sharedWorkerHost } from './shared/sharedWorkerHost';
|
|
18
|
+
export { createClient, defineService, implementService, rpc, serviceMethod } from './service';
|
|
19
|
+
export type { RpcMethodDef, RpcMethodInput, RpcMethodInputMap, RpcMethodMap, ServiceClient, ServiceContract, ServiceHandlers, ServiceImpl, ServiceMethod, TaskRunner, } from './service';
|
|
20
|
+
export { TaskRegistry } from './worker/registry';
|
|
21
|
+
export { defineWorker, RESERVED_CLIENT_KEYS } from './worker/defineWorker';
|
|
22
|
+
export type { DefineWorkerConfig, WorkerDefinition, WorkerMethod, WorkerMethodMap, WorkerServiceMap, } from './worker/defineWorker';
|
|
23
|
+
export { reactive, shallowEqual, watch } from './reactive';
|
|
24
|
+
export type { EqualityFn, ReactiveConnector, SliceOptions } from './reactive';
|
|
25
|
+
export { observe } from './observable';
|
|
26
|
+
export type { ObservableValue } from './observable';
|
|
27
|
+
export { defineTask, isAsyncTask, toTask } from './task';
|
|
28
|
+
export type { AsyncTask, TaskSnapshot } from './task';
|
|
29
|
+
export { getLogLevel, log, scoped, setLogLevel, setLogSink } from './log';
|
|
30
|
+
export type { LogEntry, LogLevel, LogSink } from './log';
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { msgpackrCodec as e } from "./index2.js";
|
|
2
|
-
import { listSchema as t } from "./
|
|
3
|
-
import { getLogLevel as n, log as r, scoped as i, setLogLevel as a, setLogSink as o } from "./
|
|
4
|
-
import {
|
|
5
|
-
import { mz as
|
|
6
|
-
import { msgpackCodec as
|
|
7
|
-
import { MemoryManager as
|
|
8
|
-
import { PoolQueueFullError as
|
|
9
|
-
import { WorkerPool as
|
|
10
|
-
import { connectWorker as
|
|
11
|
-
import { connectSharedWorker as
|
|
12
|
-
import { TaskRegistry as
|
|
13
|
-
import { sharedWorkerHost as
|
|
14
|
-
import { createClient as
|
|
15
|
-
import { RESERVED_CLIENT_KEYS as
|
|
16
|
-
import { reactive as
|
|
17
|
-
import { observe as
|
|
18
|
-
import { defineTask as
|
|
19
|
-
export {
|
|
2
|
+
import { listSchema as t } from "./index4.js";
|
|
3
|
+
import { getLogLevel as n, log as r, scoped as i, setLogLevel as a, setLogSink as o } from "./index5.js";
|
|
4
|
+
import { bindSharedMemories as s, defineSharedMemory as c, field as l, getDefinedSharedMemoryCount as u, jsonCodec as d, registerConnectorFactory as f } from "./index6.js";
|
|
5
|
+
import { mz as p } from "./index7.js";
|
|
6
|
+
import { msgpackCodec as m } from "./index8.js";
|
|
7
|
+
import { MemoryManager as h } from "./index9.js";
|
|
8
|
+
import { PoolQueueFullError as g, TaskAbortedError as _, TaskTimeoutError as v, WorkerCrashedError as y } from "./index10.js";
|
|
9
|
+
import { WorkerPool as b } from "./index11.js";
|
|
10
|
+
import { connectWorker as x, workerClient as S } from "./index12.js";
|
|
11
|
+
import { connectSharedWorker as C } from "./index13.js";
|
|
12
|
+
import { TaskRegistry as w } from "./index14.js";
|
|
13
|
+
import { sharedWorkerHost as T } from "./index15.js";
|
|
14
|
+
import { createClient as E, defineService as D, implementService as O, rpc as k, serviceMethod as A } from "./index16.js";
|
|
15
|
+
import { RESERVED_CLIENT_KEYS as j, defineWorker as M } from "./index18.js";
|
|
16
|
+
import { reactive as N, shallowEqual as P, watch as F } from "./index19.js";
|
|
17
|
+
import { observe as I } from "./index20.js";
|
|
18
|
+
import { defineTask as L, isAsyncTask as R, toTask as z } from "./index21.js";
|
|
19
|
+
export { h as MemoryManager, g as PoolQueueFullError, j as RESERVED_CLIENT_KEYS, _ as TaskAbortedError, w as TaskRegistry, v as TaskTimeoutError, y as WorkerCrashedError, b as WorkerPool, s as bindSharedMemories, C as connectSharedWorker, x as connectWorker, E as createClient, D as defineService, c as defineSharedMemory, L as defineTask, M as defineWorker, l as field, u as getDefinedSharedMemoryCount, n as getLogLevel, O as implementService, R as isAsyncTask, d as jsonCodec, t as listSchema, r as log, m as msgpackCodec, e as msgpackrCodec, p as mz, I as observe, N as reactive, f as registerConnectorFactory, k as rpc, i as scoped, A as serviceMethod, a as setLogLevel, o as setLogSink, P as shallowEqual, T as sharedWorkerHost, z as toTask, F as watch, S as workerClient };
|