@atolljs/core 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contract/reef.d.ts +88 -28
- package/dist/contract/sharedMemory.d.ts +2 -0
- package/dist/contract/types.d.ts +2 -0
- package/dist/contract/zod.d.ts +114 -14
- package/dist/devtools.d.ts +223 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +19 -17
- package/dist/index10.js +14 -185
- package/dist/index11.js +310 -47
- package/dist/index12.js +46 -59
- package/dist/index13.js +59 -20
- package/dist/index14.js +24 -60
- package/dist/index15.js +56 -43
- package/dist/index16.js +44 -50
- package/dist/index17.js +52 -38
- package/dist/index18.js +36 -54
- package/dist/index19.js +81 -47
- package/dist/index20.js +48 -51
- package/dist/index21.js +57 -0
- package/dist/index3.js +101 -41
- package/dist/index5.js +156 -35
- package/dist/index6.js +32 -485
- package/dist/index7.js +508 -19
- package/dist/index8.js +19 -33
- package/dist/index9.js +29 -14
- package/dist/pool/workerClient.d.ts +2 -0
- package/dist/pool/workerPool.d.ts +4 -0
- package/package.json +1 -1
- package/src/contract/sharedMemory.ts +19 -3
- package/src/contract/types.ts +2 -0
- package/src/contract/zod.ts +130 -14
- package/src/devtools.ts +490 -0
- package/src/index.ts +16 -0
- package/src/pool/workerClient.ts +3 -0
- package/src/pool/workerPool.ts +56 -7
- package/src/reactive.ts +107 -42
- package/src/worker/workerBootstrap.ts +3 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Prettify, Schema } from './types';
|
|
2
2
|
import { msgpackrCodec } from './msgpackrCodec';
|
|
3
3
|
import { memberToSpec, zodArrayInfo, zodObjectShape, zodStringBytes } from './listSchema';
|
|
4
|
+
import { devtoolsEnabled, emitDevtools } from '../devtools';
|
|
4
5
|
import { fmtBytes, scoped } from '../log';
|
|
5
6
|
import { z, fluent } from './zod';
|
|
6
7
|
|
|
@@ -101,6 +102,8 @@ export interface ConnectorContext {
|
|
|
101
102
|
codec: Codec;
|
|
102
103
|
/** Slot in the shared version counter — bump + notify on granular writes. */
|
|
103
104
|
version?: { view: Int32Array; index: number };
|
|
105
|
+
/** The field's contract path — for devtools write events on granular commits. */
|
|
106
|
+
path?: string;
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
/**
|
|
@@ -670,10 +673,13 @@ function listFactory(d: FieldDescriptor, ctx: ConnectorContext, byteOffset: numb
|
|
|
670
673
|
writeAt: writeAt as ListConnector<any>['writeAt'],
|
|
671
674
|
commit: () => {
|
|
672
675
|
if (ctx.version) {
|
|
673
|
-
Atomics.add(ctx.version.view, ctx.version.index, 1);
|
|
676
|
+
const prev = Atomics.add(ctx.version.view, ctx.version.index, 1);
|
|
674
677
|
// Wake EVERY waitAsync observer — several watchers may share this
|
|
675
678
|
// field's counter; a bounded notify starves all but one.
|
|
676
679
|
Atomics.notify(ctx.version.view, ctx.version.index);
|
|
680
|
+
if (ctx.path && devtoolsEnabled()) {
|
|
681
|
+
emitDevtools({ type: 'memory:write', path: ctx.path, version: prev + 1 });
|
|
682
|
+
}
|
|
677
683
|
}
|
|
678
684
|
},
|
|
679
685
|
read: () => Array.from({ length: count }, (_, i) => readAt(i)),
|
|
@@ -803,7 +809,7 @@ export class SharedMemory<S extends SharedSpec = SharedSpec> {
|
|
|
803
809
|
throw new Error(`Unknown shared memory field kind: ${descriptor.kind}`);
|
|
804
810
|
}
|
|
805
811
|
memLog.debug(`bind "${path}" (${descriptor.kind}, ${fmtBytes(descriptor.byteLength)}) via ${this.plugins?.[descriptor.kind] ? 'plugin' : 'built-in'} factory`);
|
|
806
|
-
const ctx: ConnectorContext = { buffer, codec: this.codec, version: { view: versionView, index } };
|
|
812
|
+
const ctx: ConnectorContext = { buffer, codec: this.codec, version: { view: versionView, index }, path };
|
|
807
813
|
const raw = factory(descriptor, ctx, this.offsets.get(path)!);
|
|
808
814
|
this.connectors.set(path, {
|
|
809
815
|
...raw,
|
|
@@ -812,12 +818,22 @@ export class SharedMemory<S extends SharedSpec = SharedSpec> {
|
|
|
812
818
|
// Bump the version counter and explicitly wake waitAsync observers —
|
|
813
819
|
// V8 only resolves waitAsync waiters on notify, not on value changes.
|
|
814
820
|
// No count bound: every watcher on this field must wake.
|
|
815
|
-
Atomics.add(versionView, index, 1);
|
|
821
|
+
const prev = Atomics.add(versionView, index, 1);
|
|
816
822
|
Atomics.notify(versionView, index);
|
|
823
|
+
if (devtoolsEnabled()) {
|
|
824
|
+
emitDevtools({ type: 'memory:write', path, version: prev + 1 });
|
|
825
|
+
}
|
|
817
826
|
},
|
|
818
827
|
_version: { view: versionView, index },
|
|
819
828
|
});
|
|
820
829
|
});
|
|
830
|
+
if (devtoolsEnabled()) {
|
|
831
|
+
emitDevtools({
|
|
832
|
+
type: 'memory:bind',
|
|
833
|
+
fields: this.entries.map((e, i) => [i, e.path]),
|
|
834
|
+
totalBytes: this.totalBytes,
|
|
835
|
+
});
|
|
836
|
+
}
|
|
821
837
|
this.isBound = true;
|
|
822
838
|
this.boundBuffer = buffer;
|
|
823
839
|
for (const cb of [...this.boundListeners]) cb();
|
package/src/contract/types.ts
CHANGED
|
@@ -65,6 +65,8 @@ export interface WorkerPoolConfig<S extends SharedSpec = SharedSpec, T extends T
|
|
|
65
65
|
persistence?: (memory: SharedMemory<S>) => MemoryPersistence | undefined;
|
|
66
66
|
/** Worker count — a number, or 'auto' (the default) for navigator.hardwareConcurrency ?? 4. */
|
|
67
67
|
poolSize?: number | 'auto';
|
|
68
|
+
/** Display label for devtools/dashboards — falls back to a generated id. */
|
|
69
|
+
name?: string;
|
|
68
70
|
memory?: MemoryConfig;
|
|
69
71
|
/** Max in-flight tasks per worker (default 1); excess tasks queue FIFO. */
|
|
70
72
|
concurrency?: number;
|
package/src/contract/zod.ts
CHANGED
|
@@ -98,6 +98,10 @@ export interface SchemaDef {
|
|
|
98
98
|
format?: string;
|
|
99
99
|
shape?: Record<string, ZodLike>;
|
|
100
100
|
element?: ZodLike;
|
|
101
|
+
/** union members. */
|
|
102
|
+
options?: readonly ZodLike[];
|
|
103
|
+
/** literal value. */
|
|
104
|
+
literal?: unknown;
|
|
101
105
|
cls?: abstract new (...args: any[]) => unknown;
|
|
102
106
|
}
|
|
103
107
|
|
|
@@ -205,9 +209,24 @@ class ZodInstanceof<T> extends ZodType<T> {
|
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
212
|
+
/** Members whose output admits `undefined` become optional keys — mirroring
|
|
213
|
+
* zod's `z.optional()` object-member semantics (message-domain schemas like
|
|
214
|
+
* island prop contracts need this; reef's fixed-width members never produce
|
|
215
|
+
* it, so the split preserves their all-required output). */
|
|
216
|
+
type IsOptional<S, K extends keyof S> =
|
|
217
|
+
S[K] extends { _zod: { output: infer O } }
|
|
218
|
+
? undefined extends O
|
|
219
|
+
? true
|
|
220
|
+
: false
|
|
221
|
+
: false;
|
|
222
|
+
|
|
223
|
+
type Flatten<T> = { [K in keyof T]: T[K] };
|
|
224
|
+
|
|
225
|
+
type ShapeOutput<S extends Record<string, ZodLike>> = Flatten<
|
|
226
|
+
{ [K in keyof S as IsOptional<S, K> extends true ? never : K]: OutputOf<S[K]> } & {
|
|
227
|
+
[K in keyof S as IsOptional<S, K> extends true ? K : never]?: OutputOf<S[K]>;
|
|
228
|
+
}
|
|
229
|
+
>;
|
|
211
230
|
|
|
212
231
|
/** Strips unknown keys like zod's object parse — output is the shape only. */
|
|
213
232
|
class ZodObjectSchema<S extends Record<string, ZodLike>> extends ZodType<ShapeOutput<S>> {
|
|
@@ -242,21 +261,111 @@ class ZodArraySchema<E extends ZodLike> extends ZodType<OutputOf<E>[]> {
|
|
|
242
261
|
|
|
243
262
|
type OutputOf<S> = S extends { _zod: { output: infer O } } ? O : never;
|
|
244
263
|
|
|
245
|
-
/*
|
|
246
|
-
|
|
247
|
-
|
|
264
|
+
/* ── Message-domain kinds ────────────────────────────────────────────────
|
|
265
|
+
* Layout-free members for contracts that ride postMessage instead of the
|
|
266
|
+
* shared buffer — island prop/event schemas (defineIslandContract). The
|
|
267
|
+
* layout compiler doesn't understand these def.types and rejects them with
|
|
268
|
+
* "can't lay out" if they ever appear inside a fixed-width field — that
|
|
269
|
+
* loud failure is intended: they have no byte width.
|
|
270
|
+
*/
|
|
271
|
+
|
|
272
|
+
class ZodOptional<E extends ZodLike> extends ZodType<OutputOf<E> | undefined> {
|
|
273
|
+
declare readonly _zod: {
|
|
274
|
+
output: OutputOf<E> | undefined; input: OutputOf<E> | undefined;
|
|
275
|
+
def: SchemaDef & { type: 'optional'; element: E };
|
|
276
|
+
};
|
|
277
|
+
inner(v: unknown): OutputOf<E> | undefined {
|
|
278
|
+
// The intersected def narrows element to E; read it as the element's
|
|
279
|
+
// output type so the wrapper's output lines up.
|
|
280
|
+
return v === undefined
|
|
281
|
+
? undefined
|
|
282
|
+
: (this._zod.def.element as ZodLike<OutputOf<E>>).parse(v);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
class ZodNullable<E extends ZodLike> extends ZodType<OutputOf<E> | null> {
|
|
286
|
+
declare readonly _zod: {
|
|
287
|
+
output: OutputOf<E> | null; input: OutputOf<E> | null;
|
|
288
|
+
def: SchemaDef & { type: 'nullable'; element: E };
|
|
289
|
+
};
|
|
290
|
+
inner(v: unknown): OutputOf<E> | null {
|
|
291
|
+
return v === null
|
|
292
|
+
? null
|
|
293
|
+
: (this._zod.def.element as ZodLike<OutputOf<E>>).parse(v);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
class ZodLiteral<V extends string | number | boolean> extends ZodType<V> {
|
|
297
|
+
declare readonly _zod: { output: V; input: V; def: SchemaDef & { type: 'literal' } };
|
|
298
|
+
inner(v: unknown): V {
|
|
299
|
+
return v === this._zod.def.literal ? (v as V) : fail(JSON.stringify(this._zod.def.literal));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
class ZodUnion<O extends readonly ZodLike[]> extends ZodType<OutputOf<O[number]>> {
|
|
303
|
+
declare readonly _zod: {
|
|
304
|
+
output: OutputOf<O[number]>; input: OutputOf<O[number]>;
|
|
305
|
+
def: SchemaDef & { type: 'union'; options: O };
|
|
306
|
+
};
|
|
307
|
+
inner(v: unknown): OutputOf<O[number]> {
|
|
308
|
+
for (const option of this._zod.def.options as readonly ZodLike<OutputOf<O[number]>>[]) {
|
|
309
|
+
const r = option.safeParse(v);
|
|
310
|
+
if (r.success) return r.data;
|
|
311
|
+
}
|
|
312
|
+
return fail('a union member');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
class ZodRecord<E extends ZodLike> extends ZodType<Record<string, OutputOf<E>>> {
|
|
316
|
+
declare readonly _zod: {
|
|
317
|
+
output: Record<string, OutputOf<E>>; input: Record<string, OutputOf<E>>;
|
|
318
|
+
def: SchemaDef & { type: 'record'; element: E };
|
|
319
|
+
};
|
|
320
|
+
inner(v: unknown): Record<string, OutputOf<E>> {
|
|
321
|
+
if (typeof v !== 'object' || v === null || Array.isArray(v)) fail('object');
|
|
322
|
+
const el = this._zod.def.element;
|
|
323
|
+
const out: Record<string, unknown> = {};
|
|
324
|
+
for (const [k, value] of Object.entries(v as Record<string, unknown>)) {
|
|
325
|
+
out[k] = el.parse(value);
|
|
326
|
+
}
|
|
327
|
+
return out as Record<string, OutputOf<E>>;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Function-typed members — how a props schema declares a `callbackProp`
|
|
332
|
+
* slot: the wire carries a `{__cb:id}` handle that `unmarshalCallbackProps`
|
|
333
|
+
* restores to a callable BEFORE contract validation runs, so at parse time
|
|
334
|
+
* the member is a real function.
|
|
335
|
+
*/
|
|
336
|
+
class ZodCallback<F extends (...args: any[]) => any = (...args: unknown[]) => unknown>
|
|
337
|
+
extends ZodType<F> {
|
|
338
|
+
declare readonly _zod: { output: F; input: F; def: SchemaDef & { type: 'function' } };
|
|
339
|
+
inner(v: unknown): F {
|
|
340
|
+
return typeof v === 'function' ? (v as F) : fail('function');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
248
343
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
const
|
|
344
|
+
// z factories return FLUENT schemas — `z.string().optional()` chains like
|
|
345
|
+
// classic zod; the mini spellings (`z.optional(z.string())`) stay valid.
|
|
346
|
+
const number = () => fluent(new ZodNumber({ type: 'number' }));
|
|
347
|
+
const string = () => fluent(new ZodString({ type: 'string' }));
|
|
348
|
+
const boolean_ = () => fluent(new ZodBoolean({ type: 'boolean' }));
|
|
349
|
+
const bigint_ = () => fluent(new ZodBigint({ type: 'bigint' }));
|
|
350
|
+
const unknown_ = () => fluent(new ZodUnknown({ type: 'unknown' }));
|
|
254
351
|
const instanceof_ = <T extends abstract new (...args: any[]) => any>(cls: T) =>
|
|
255
|
-
new ZodInstanceof<InstanceType<T>>({ type: 'instanceof', cls });
|
|
352
|
+
fluent(new ZodInstanceof<InstanceType<T>>({ type: 'instanceof', cls }));
|
|
256
353
|
const object_ = <S extends Record<string, ZodLike>>(shape: S) =>
|
|
257
|
-
new ZodObjectSchema<S>({ type: 'object', shape });
|
|
354
|
+
fluent(new ZodObjectSchema<S>({ type: 'object', shape }));
|
|
258
355
|
const array = <E extends ZodLike>(element: E) =>
|
|
259
|
-
new ZodArraySchema<E>({ type: 'array', element });
|
|
356
|
+
fluent(new ZodArraySchema<E>({ type: 'array', element }));
|
|
357
|
+
const optional = <E extends ZodLike>(element: E) =>
|
|
358
|
+
fluent(new ZodOptional<E>({ type: 'optional', element }));
|
|
359
|
+
const nullable = <E extends ZodLike>(element: E) =>
|
|
360
|
+
fluent(new ZodNullable<E>({ type: 'nullable', element }));
|
|
361
|
+
const literal = <V extends string | number | boolean>(value: V) =>
|
|
362
|
+
fluent(new ZodLiteral<V>({ type: 'literal', literal: value }));
|
|
363
|
+
const union = <O extends readonly [ZodLike, ...ZodLike[]]>(options: O) =>
|
|
364
|
+
fluent(new ZodUnion<O>({ type: 'union', options }));
|
|
365
|
+
const record = <E extends ZodLike>(element: E) =>
|
|
366
|
+
fluent(new ZodRecord<E>({ type: 'record', element }));
|
|
367
|
+
const callback = <F extends (...args: any[]) => any = (...args: unknown[]) => unknown>() =>
|
|
368
|
+
fluent(new ZodCallback<F>({ type: 'function' }));
|
|
260
369
|
|
|
261
370
|
const int32 = () => new ZodNumber({ type: 'number', format: 'int32' }).check(intRange('int32', int32min, int32max));
|
|
262
371
|
const uint32 = () => new ZodNumber({ type: 'number', format: 'uint32' }).check(intRange('uint32', 0, 4294967295));
|
|
@@ -288,6 +397,10 @@ export type Fluent<S extends ZodLike> = S & {
|
|
|
288
397
|
max(value: number | bigint): Fluent<S>;
|
|
289
398
|
length(n: number): Fluent<S>;
|
|
290
399
|
int(): Fluent<S>;
|
|
400
|
+
/** Wraps in an optional member — the `label?: string` spelling. */
|
|
401
|
+
optional(): Fluent<ZodOptional<S>>;
|
|
402
|
+
/** Wraps in a nullable member — `T | null`. */
|
|
403
|
+
nullable(): Fluent<ZodNullable<S>>;
|
|
291
404
|
};
|
|
292
405
|
|
|
293
406
|
/** Attaches the `Fluent` methods as non-enumerable own properties. */
|
|
@@ -302,6 +415,8 @@ export function fluent<S extends ZodType>(schema: S): Fluent<S> {
|
|
|
302
415
|
max: (v: number | bigint) => fluent(boundCheck_(boundCheck(schema, 'max', v))),
|
|
303
416
|
length: (n: number) => fluent(boundCheck_(length(n))),
|
|
304
417
|
int: () => fluent(boundCheck_(int())),
|
|
418
|
+
optional: () => fluent(new ZodOptional({ type: 'optional', element: schema })),
|
|
419
|
+
nullable: () => fluent(new ZodNullable({ type: 'nullable', element: schema })),
|
|
305
420
|
};
|
|
306
421
|
for (const [key, fn] of Object.entries(own)) {
|
|
307
422
|
Object.defineProperty(schema, key, { value: fn, enumerable: false, configurable: true });
|
|
@@ -316,6 +431,7 @@ export function fluent<S extends ZodType>(schema: S): Fluent<S> {
|
|
|
316
431
|
export const z = {
|
|
317
432
|
number, string, boolean: boolean_, bigint: bigint_, object: object_, array,
|
|
318
433
|
instanceof: instanceof_, unknown: unknown_,
|
|
434
|
+
optional, nullable, literal, union, record, callback,
|
|
319
435
|
int32, uint32, float32, float64, int64, uint64,
|
|
320
436
|
int, gte, lte, nonnegative, refine, length, maxLength, minLength,
|
|
321
437
|
globalRegistry,
|