@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 +2 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/schema.d.ts +33 -16
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +153 -20
- package/dist/shared-object.d.ts +6 -0
- package/dist/shared-object.d.ts.map +1 -1
- package/dist/shared-object.js +12 -0
- package/package.json +1 -1
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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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)
|
|
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
|
|
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
|
|
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. {
|
|
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]:
|
|
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
|
|
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.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
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
|
|
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:
|
|
80
|
+
type: ScalarType;
|
|
69
81
|
offset: number;
|
|
70
82
|
}
|
|
71
83
|
interface ArrayFieldLayout {
|
|
72
84
|
kind: "array";
|
|
73
|
-
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
|
|
104
|
-
*
|
|
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 {};
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"AAAA
|
|
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)
|
|
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
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
144
|
-
*
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
}
|
package/dist/shared-object.d.ts
CHANGED
|
@@ -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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"}
|
package/dist/shared-object.js
CHANGED
|
@@ -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
|
}
|