@kargjonas/sabus 0.1.2 → 0.2.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.
package/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  Minimal SharedArrayBuffer worker bus for low-overhead cross-worker data sharing.
4
4
 
5
- > [!NOTE]
6
5
  > Check the `examples/` directory. Each example has a focused `README` with context and usage notes.
7
6
 
8
7
  ## Install
@@ -10,13 +9,7 @@ Minimal SharedArrayBuffer worker bus for low-overhead cross-worker data sharing.
10
9
  Install from command line:
11
10
 
12
11
  ```bash
13
- npm install @kargjonas/sabus@0.1.0
14
- ```
15
-
16
- Install via `package.json`:
17
-
18
- ```bash
19
- "@kargjonas/sabus": "0.1.0"
12
+ npm install @kargjonas/sabus
20
13
  ```
21
14
 
22
15
  ## Quick start
@@ -65,6 +58,7 @@ For worker-side usage and more complete patterns, check `examples/`.
65
58
  - `SharedRuntime` to coordinate shared objects across host and workers.
66
59
  - `SharedObject` with FIFO write lock and atomic latest-read behavior.
67
60
  - `TypedSharedObject` for schema-based typed reads and writes.
61
+ - Bulk array writes (`TypedArray#set`) for all schema array fields, including `Type.Rgba8`.
68
62
  - `schema` helpers (`Type`, `computeLayout`, `readSnapshot`, `writeFields`).
69
63
 
70
64
  ## Requirements
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { default as SharedRuntime } from "./shared-runtime.js";
2
- export { SharedObject, TypedSharedObject, type SharedObjectConfig, type SharedObjectDescriptor, type SharedObjectReadSnapshot, type SharedObjectWriteCallback, type SharedObjectWriteContext, } from "./shared-object.js";
2
+ export { SharedObject, TypedSharedObject, type SharedObjectConfig, type SharedObjectDescriptor, type SharedObjectReadSnapshot, type SharedObjectWriteCallback, type SharedObjectWriteContext, type TypedSharedObjectWriteCallback, type TypedSharedObjectWriteContext, } from "./shared-object.js";
3
3
  export { Type, computeLayout, readSnapshot, writeFields, type Layout, type SchemaDefinition, type SchemaValues, type SchemaWriteValues, } from "./schema.js";
4
4
  export { createRuntimePeer, detectCurrentWorkerPeer, type RuntimePeer, } from "./runtime-peer.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,GAC9B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,IAAI,EACJ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,GACnC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,IAAI,EACJ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC"}
package/dist/schema.d.ts CHANGED
@@ -2,7 +2,9 @@
2
2
  * Schema-driven type system for SharedObjects.
3
3
  *
4
4
  * A schema is a plain object mapping field names to types. Each field is either
5
- * a scalar (Type.Float32) or a fixed-length array ([Type.Float32, 3]).
5
+ * a scalar numeric value (Type.Float32), a fixed-length numeric array
6
+ * ([Type.Float32, 3]), a fixed-length RGBA8 pixel buffer ([Type.Rgba8, pixels]),
7
+ * or a fixed-length UTF-8 string ([Type.Utf8, 64]).
6
8
  *
7
9
  * From a schema definition, this module derives:
8
10
  * - A memory layout (byte offsets and total size), computed once at creation
@@ -17,8 +19,12 @@ export declare enum Type {
17
19
  Int32 = 5,
18
20
  Uint32 = 6,
19
21
  Float32 = 7,
20
- Float64 = 8
22
+ Float64 = 8,
23
+ Utf8 = 9,
24
+ Rgba8 = 10
21
25
  }
26
+ type ScalarType = Exclude<Type, Type.Utf8 | Type.Rgba8>;
27
+ type ArrayType = ScalarType | Type.Rgba8;
22
28
  /**
23
29
  * Maps each Type enum value to its corresponding TypedArray class.
24
30
  * Used as an indexed lookup by TypedArrayFor<T> to resolve types at compile time.
@@ -32,20 +38,26 @@ interface TypedArrayMap {
32
38
  [Type.Uint32]: Uint32Array;
33
39
  [Type.Float32]: Float32Array;
34
40
  [Type.Float64]: Float64Array;
41
+ [Type.Rgba8]: Uint8Array;
35
42
  }
36
43
  /**
37
- * Resolves a Type enum value to its TypedArray type via indexed access into
44
+ * Resolves an array element Type to its TypedArray type via indexed access into
38
45
  * TypedArrayMap. e.g. TypedArrayFor<Type.Float32> = Float32Array
39
46
  */
40
- type TypedArrayFor<T extends Type> = TypedArrayMap[T];
47
+ type TypedArrayFor<T extends ArrayType> = TypedArrayMap[T];
41
48
  /**
42
49
  * A schema definition is a plain object mapping field names to field types.
43
50
  * Must be declared `as const` to preserve literal types for type inference.
44
51
  * Uses an interface to allow recursive (nested) schemas without circular alias errors.
45
- * e.g. { health: Type.Int32, position: [Type.Float32, 3], transform: { x: Type.Float32 } }
52
+ * e.g. {
53
+ * health: Type.Int32,
54
+ * position: [Type.Float32, 3],
55
+ * title: [Type.Utf8, 64],
56
+ * feed: [Type.Rgba8, 320 * 180]
57
+ * }
46
58
  */
47
59
  export interface SchemaDefinition {
48
- readonly [key: string]: Type | readonly [Type, number] | SchemaDefinition;
60
+ readonly [key: string]: ScalarType | readonly [Type, number] | SchemaDefinition;
49
61
  }
50
62
  /**
51
63
  * Derives the JS read types from a schema. Scalar fields become `number`,
@@ -53,33 +65,38 @@ export interface SchemaDefinition {
53
65
  * and nested schema fields become a nested SchemaValues object.
54
66
  */
55
67
  export type SchemaValues<S extends SchemaDefinition> = {
56
- [K in keyof S]: S[K] extends readonly [infer T extends Type, number] ? TypedArrayFor<T> : S[K] extends SchemaDefinition ? SchemaValues<S[K]> : number;
68
+ [K in keyof S]: S[K] extends readonly [Type.Utf8, number] ? string : S[K] extends readonly [infer T extends ArrayType, number] ? TypedArrayFor<T> : S[K] extends SchemaDefinition ? SchemaValues<S[K]> : number;
57
69
  };
58
70
  /**
59
- * Derives the JS write types from a schema. Same as SchemaValues, except
60
- * array fields also accept ArrayLike<number> for ergonomics (e.g. plain [1, 2, 3]),
61
- * and nested schema fields accept partial values at every level.
71
+ * Derives the JS write types from a schema. Array fields require concrete
72
+ * typed arrays to ensure writes can use one bulk copy operation.
73
+ * Nested schema fields accept partial values at every level.
62
74
  */
63
75
  export type SchemaWriteValues<S extends SchemaDefinition> = {
64
- [K in keyof S]: S[K] extends readonly [infer T extends Type, number] ? TypedArrayFor<T> | ArrayLike<number> : S[K] extends SchemaDefinition ? Partial<SchemaWriteValues<S[K]>> : number;
76
+ [K in keyof S]: S[K] extends readonly [Type.Utf8, number] ? string : S[K] extends readonly [infer T extends ArrayType, number] ? TypedArrayFor<T> : S[K] extends SchemaDefinition ? Partial<SchemaWriteValues<S[K]>> : number;
65
77
  };
66
78
  interface ScalarFieldLayout {
67
79
  kind: "scalar";
68
- type: Type;
80
+ type: ScalarType;
69
81
  offset: number;
70
82
  }
71
83
  interface ArrayFieldLayout {
72
84
  kind: "array";
73
- type: Type;
85
+ type: ArrayType;
74
86
  offset: number;
75
87
  count: number;
76
88
  }
89
+ interface Utf8FieldLayout {
90
+ kind: "utf8";
91
+ offset: number;
92
+ byteLength: number;
93
+ }
77
94
  interface NestedFieldLayout {
78
95
  kind: "nested";
79
96
  offset: number;
80
97
  layout: Layout<SchemaDefinition>;
81
98
  }
82
- type FieldLayout = ScalarFieldLayout | ArrayFieldLayout | NestedFieldLayout;
99
+ type FieldLayout = ScalarFieldLayout | ArrayFieldLayout | Utf8FieldLayout | NestedFieldLayout;
83
100
  export interface Layout<S extends SchemaDefinition> {
84
101
  fields: {
85
102
  [K in keyof S]: FieldLayout;
@@ -100,8 +117,8 @@ export declare function computeLayout<S extends SchemaDefinition>(schema: S): La
100
117
  export declare function readSnapshot<S extends SchemaDefinition>(layout: Layout<S>, dataView: DataView, baseOffset?: number): SchemaValues<S>;
101
118
  /**
102
119
  * Writes a partial set of fields into a DataView according to the layout.
103
- * Scalar fields use a single DataView setter call. Array fields iterate
104
- * and write each element individually. Nested fields recurse.
120
+ * Scalar fields use a single DataView setter call. Array fields use one
121
+ * typed-array bulk copy. Nested fields recurse.
105
122
  */
106
123
  export declare function writeFields<S extends SchemaDefinition>(layout: Layout<S>, dataView: DataView, values: Partial<SchemaWriteValues<S>>, baseOffset?: number): void;
107
124
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"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"}
package/dist/schema.js CHANGED
@@ -2,7 +2,9 @@
2
2
  * Schema-driven type system for SharedObjects.
3
3
  *
4
4
  * A schema is a plain object mapping field names to types. Each field is either
5
- * a scalar (Type.Float32) or a fixed-length array ([Type.Float32, 3]).
5
+ * a scalar numeric value (Type.Float32), a fixed-length numeric array
6
+ * ([Type.Float32, 3]), a fixed-length RGBA8 pixel buffer ([Type.Rgba8, pixels]),
7
+ * or a fixed-length UTF-8 string ([Type.Utf8, 64]).
6
8
  *
7
9
  * From a schema definition, this module derives:
8
10
  * - A memory layout (byte offsets and total size), computed once at creation
@@ -19,8 +21,12 @@ export var Type;
19
21
  Type[Type["Uint32"] = 6] = "Uint32";
20
22
  Type[Type["Float32"] = 7] = "Float32";
21
23
  Type[Type["Float64"] = 8] = "Float64";
24
+ Type[Type["Utf8"] = 9] = "Utf8";
25
+ Type[Type["Rgba8"] = 10] = "Rgba8";
22
26
  })(Type || (Type = {}));
23
- const TYPE_SIZE = {
27
+ const UTF8_ENCODER = new TextEncoder();
28
+ const UTF8_DECODER = new TextDecoder();
29
+ const SCALAR_TYPE_SIZE = {
24
30
  [Type.Int8]: 1,
25
31
  [Type.Uint8]: 1,
26
32
  [Type.Int16]: 2,
@@ -30,6 +36,28 @@ const TYPE_SIZE = {
30
36
  [Type.Float32]: 4,
31
37
  [Type.Float64]: 8,
32
38
  };
39
+ const ARRAY_ELEMENT_SIZE = {
40
+ [Type.Int8]: 1,
41
+ [Type.Uint8]: 1,
42
+ [Type.Int16]: 2,
43
+ [Type.Uint16]: 2,
44
+ [Type.Int32]: 4,
45
+ [Type.Uint32]: 4,
46
+ [Type.Float32]: 4,
47
+ [Type.Float64]: 8,
48
+ [Type.Rgba8]: 4,
49
+ };
50
+ const ARRAY_VIEW_LENGTH_MULTIPLIER = {
51
+ [Type.Int8]: 1,
52
+ [Type.Uint8]: 1,
53
+ [Type.Int16]: 1,
54
+ [Type.Uint16]: 1,
55
+ [Type.Int32]: 1,
56
+ [Type.Uint32]: 1,
57
+ [Type.Float32]: 1,
58
+ [Type.Float64]: 1,
59
+ [Type.Rgba8]: 4,
60
+ };
33
61
  const TYPE_GETTER = {
34
62
  [Type.Int8]: "getInt8",
35
63
  [Type.Uint8]: "getUint8",
@@ -59,9 +87,50 @@ const TYPE_ARRAY_CTOR = {
59
87
  [Type.Uint32]: Uint32Array,
60
88
  [Type.Float32]: Float32Array,
61
89
  [Type.Float64]: Float64Array,
90
+ [Type.Rgba8]: Uint8Array,
62
91
  };
63
92
  function isNestedSchema(raw) {
64
- return typeof raw === "object" && !Array.isArray(raw);
93
+ return typeof raw === "object" && raw !== null && !Array.isArray(raw);
94
+ }
95
+ function isScalarType(type) {
96
+ switch (type) {
97
+ case Type.Int8:
98
+ case Type.Uint8:
99
+ case Type.Int16:
100
+ case Type.Uint16:
101
+ case Type.Int32:
102
+ case Type.Uint32:
103
+ case Type.Float32:
104
+ case Type.Float64:
105
+ return true;
106
+ default:
107
+ return false;
108
+ }
109
+ }
110
+ function isArrayType(type) {
111
+ return isScalarType(type) || type === Type.Rgba8;
112
+ }
113
+ function typeName(type) {
114
+ return Type[type] ?? String(type);
115
+ }
116
+ function valueName(value) {
117
+ if (value === null)
118
+ return "null";
119
+ if (value === undefined)
120
+ return "undefined";
121
+ if (ArrayBuffer.isView(value))
122
+ return value.constructor.name;
123
+ if (typeof value === "object" && "constructor" in value) {
124
+ const name = value.constructor?.name;
125
+ if (name)
126
+ return name;
127
+ }
128
+ return typeof value;
129
+ }
130
+ function assertPositiveInteger(value, label) {
131
+ if (!Number.isInteger(value) || value <= 0) {
132
+ throw new Error(`${label} must be a positive integer, got ${value}`);
133
+ }
65
134
  }
66
135
  /**
67
136
  * Returns the maximum element alignment required by any field in a schema.
@@ -72,8 +141,21 @@ function maxAlignment(schema) {
72
141
  if (isNestedSchema(raw))
73
142
  align = Math.max(align, maxAlignment(raw));
74
143
  else {
75
- const type = typeof raw === "number" ? raw : raw[0];
76
- align = Math.max(align, TYPE_SIZE[type]);
144
+ if (typeof raw === "number") {
145
+ if (!isScalarType(raw)) {
146
+ throw new Error(`Invalid scalar type ${typeName(raw)} in schema`);
147
+ }
148
+ align = Math.max(align, SCALAR_TYPE_SIZE[raw]);
149
+ }
150
+ else {
151
+ const type = raw[0];
152
+ if (type === Type.Utf8)
153
+ continue;
154
+ if (!isArrayType(type)) {
155
+ throw new Error(`Invalid array type ${typeName(type)} in schema`);
156
+ }
157
+ align = Math.max(align, ARRAY_ELEMENT_SIZE[type]);
158
+ }
77
159
  }
78
160
  }
79
161
  return align;
@@ -96,15 +178,32 @@ export function computeLayout(schema) {
96
178
  offset += nested.byteLength;
97
179
  }
98
180
  else {
99
- const type = typeof raw === "number" ? raw : raw[0];
100
- const count = typeof raw === "number" ? 1 : raw[1];
101
- const elemSize = TYPE_SIZE[type];
102
- offset = Math.ceil(offset / elemSize) * elemSize;
103
- if (count === 1)
104
- fields[key] = { kind: "scalar", type, offset };
105
- else
181
+ if (typeof raw === "number") {
182
+ if (!isScalarType(raw)) {
183
+ throw new Error(`Field "${key}" uses non-scalar type ${typeName(raw)} as scalar`);
184
+ }
185
+ const elemSize = SCALAR_TYPE_SIZE[raw];
186
+ offset = Math.ceil(offset / elemSize) * elemSize;
187
+ fields[key] = { kind: "scalar", type: raw, offset };
188
+ offset += elemSize;
189
+ }
190
+ else {
191
+ const type = raw[0];
192
+ const count = raw[1];
193
+ assertPositiveInteger(count, `Field "${key}" length`);
194
+ if (type === Type.Utf8) {
195
+ fields[key] = { kind: "utf8", offset, byteLength: count };
196
+ offset += count;
197
+ continue;
198
+ }
199
+ if (!isArrayType(type)) {
200
+ throw new Error(`Field "${key}" uses unsupported array type ${typeName(type)}`);
201
+ }
202
+ const elemSize = ARRAY_ELEMENT_SIZE[type];
203
+ offset = Math.ceil(offset / elemSize) * elemSize;
106
204
  fields[key] = { kind: "array", type, offset, count };
107
- offset += elemSize * count;
205
+ offset += elemSize * count;
206
+ }
108
207
  }
109
208
  }
110
209
  return { fields, byteLength: offset };
@@ -127,7 +226,15 @@ export function readSnapshot(layout, dataView, baseOffset = 0) {
127
226
  }
128
227
  case "array": {
129
228
  const Ctor = TYPE_ARRAY_CTOR[field.type];
130
- out[key] = new Ctor(dataView.buffer, dataView.byteOffset + abs, field.count);
229
+ const length = field.count * ARRAY_VIEW_LENGTH_MULTIPLIER[field.type];
230
+ out[key] = new Ctor(dataView.buffer, dataView.byteOffset + abs, length);
231
+ break;
232
+ }
233
+ case "utf8": {
234
+ const bytes = new Uint8Array(dataView.buffer, dataView.byteOffset + abs, field.byteLength);
235
+ const zeroIndex = bytes.indexOf(0);
236
+ const end = zeroIndex >= 0 ? zeroIndex : bytes.length;
237
+ out[key] = UTF8_DECODER.decode(bytes.subarray(0, end));
131
238
  break;
132
239
  }
133
240
  case "nested": {
@@ -140,30 +247,56 @@ export function readSnapshot(layout, dataView, baseOffset = 0) {
140
247
  }
141
248
  /**
142
249
  * Writes a partial set of fields into a DataView according to the layout.
143
- * Scalar fields use a single DataView setter call. Array fields iterate
144
- * and write each element individually. Nested fields recurse.
250
+ * Scalar fields use a single DataView setter call. Array fields use one
251
+ * typed-array bulk copy. Nested fields recurse.
145
252
  */
146
253
  export function writeFields(layout, dataView, values, baseOffset = 0) {
147
254
  for (const key of Object.keys(values)) {
148
255
  const field = layout.fields[key];
149
256
  const val = values[key];
257
+ if (val === undefined)
258
+ continue;
150
259
  const abs = baseOffset + field.offset;
151
260
  switch (field.kind) {
152
261
  case "scalar": {
262
+ if (typeof val !== "number") {
263
+ throw new Error(`Expected a number for scalar field "${key}", got ${typeof val}`);
264
+ }
153
265
  const setter = TYPE_SETTER[field.type];
154
266
  dataView[setter].call(dataView, abs, val, true);
155
267
  break;
156
268
  }
157
269
  case "array": {
270
+ const Ctor = TYPE_ARRAY_CTOR[field.type];
158
271
  const src = val;
159
- const setter = TYPE_SETTER[field.type];
160
- const elemSize = TYPE_SIZE[field.type];
161
- for (let i = 0; i < field.count; i++) {
162
- dataView[setter].call(dataView, abs + i * elemSize, src[i], true);
272
+ if (!(src instanceof Ctor)) {
273
+ throw new Error(`Expected ${Ctor.name} for array field "${key}", got ${valueName(src)}`);
274
+ }
275
+ const length = field.count * ARRAY_VIEW_LENGTH_MULTIPLIER[field.type];
276
+ if (src.length !== length) {
277
+ throw new Error(`Array field "${key}" length mismatch: expected ${length}, got ${src.length}`);
163
278
  }
279
+ const dst = new Ctor(dataView.buffer, dataView.byteOffset + abs, length);
280
+ dst.set(src);
281
+ break;
282
+ }
283
+ case "utf8": {
284
+ if (typeof val !== "string") {
285
+ throw new Error(`Expected a string for utf8 field "${key}", got ${typeof val}`);
286
+ }
287
+ const encoded = UTF8_ENCODER.encode(val);
288
+ if (encoded.length > field.byteLength) {
289
+ throw new Error(`Value for utf8 field "${key}" exceeds byteLength ${field.byteLength}: ${encoded.length} bytes`);
290
+ }
291
+ const bytes = new Uint8Array(dataView.buffer, dataView.byteOffset + abs, field.byteLength);
292
+ bytes.fill(0);
293
+ bytes.set(encoded);
164
294
  break;
165
295
  }
166
296
  case "nested": {
297
+ if (typeof val !== "object" || val === null || Array.isArray(val)) {
298
+ throw new Error(`Expected object value for nested field "${key}"`);
299
+ }
167
300
  writeFields(field.layout, dataView, val, abs);
168
301
  break;
169
302
  }
@@ -23,6 +23,11 @@ export interface SharedObjectWriteContext {
23
23
  }
24
24
  export type SharedObjectReadSnapshot = SharedObjectWriteContext;
25
25
  export type SharedObjectWriteCallback<TReturn = void> = (ctx: SharedObjectWriteContext) => TReturn | Promise<TReturn>;
26
+ export interface TypedSharedObjectWriteContext<S extends SchemaDefinition> extends SharedObjectWriteContext {
27
+ view: SchemaValues<S>;
28
+ set(values: Partial<SchemaWriteValues<S>>): void;
29
+ }
30
+ export type TypedSharedObjectWriteCallback<S extends SchemaDefinition, TReturn = void> = (ctx: TypedSharedObjectWriteContext<S>) => TReturn | Promise<TReturn>;
26
31
  export declare class SharedObject {
27
32
  readonly id: string;
28
33
  readonly byteLength: number;
@@ -46,6 +51,7 @@ export declare class TypedSharedObject<S extends SchemaDefinition> {
46
51
  readonly inner: SharedObject;
47
52
  readonly layout: Layout<S>;
48
53
  constructor(inner: SharedObject, schema: S);
54
+ requestWrite<TReturn>(cb: TypedSharedObjectWriteCallback<S, TReturn>): Promise<TReturn>;
49
55
  write(values: Partial<SchemaWriteValues<S>>): Promise<void>;
50
56
  read(): (SchemaValues<S> & {
51
57
  seq: number;
@@ -1 +1 @@
1
- {"version":3,"file":"shared-object.d.ts","sourceRoot":"","sources":["../shared-object.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAarB,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,CAAC;AAChE,MAAM,MAAM,yBAAyB,CAAC,OAAO,GAAG,IAAI,IAAI,CACtD,GAAG,EAAE,wBAAwB,KAC1B,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAkBhC,qBAAa,YAAY;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;gBAErC,UAAU,EAAE,sBAAsB;IAU9C,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,YAAY;IAuBnE,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,sBAAsB,GAAG,YAAY;IAIvE,UAAU,IAAI,sBAAsB;IAU9B,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,yBAAyB,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;YAavE,aAAa;IAiB3B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAM3C,UAAU,IAAI,wBAAwB,GAAG,IAAI;YAe/B,WAAW;IAiBzB,OAAO,CAAC,gBAAgB;CAUzB;AAED,qBAAa,iBAAiB,CAAC,CAAC,SAAS,gBAAgB;IACvD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;gBAEf,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAKpC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAMlD,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;CAG5C"}
1
+ {"version":3,"file":"shared-object.d.ts","sourceRoot":"","sources":["../shared-object.ts"],"names":[],"mappings":"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"}
@@ -144,6 +144,18 @@ export class TypedSharedObject {
144
144
  this.inner = inner;
145
145
  this.layout = computeLayout(schema);
146
146
  }
147
+ async requestWrite(cb) {
148
+ return this.inner.requestWrite((ctx) => {
149
+ const set = (values) => {
150
+ writeFields(this.layout, ctx.dataView, values);
151
+ };
152
+ return cb({
153
+ ...ctx,
154
+ view: readSnapshot(this.layout, ctx.dataView),
155
+ set,
156
+ });
157
+ });
158
+ }
147
159
  async write(values) {
148
160
  await this.inner.requestWrite(({ dataView }) => writeFields(this.layout, dataView, values));
149
161
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kargjonas/sabus",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },