@colyseus/schema 5.0.14 → 5.0.20
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 +11 -5
- package/build/Metadata.d.ts +10 -1
- package/build/annotations.d.ts +6 -5
- package/build/codegen/api.d.ts +2 -0
- package/build/codegen/cli.cjs +322 -31
- package/build/codegen/cli.cjs.map +1 -1
- package/build/codegen/parser.d.ts +6 -1
- package/build/codegen/resolve.d.ts +25 -0
- package/build/codegen/types.d.ts +2 -0
- package/build/decoder/strategy/Callbacks.d.ts +7 -8
- package/build/decoder/strategy/getDecoderStateCallbacks.d.ts +2 -2
- package/build/encoder/ChangeTree.d.ts +40 -12
- package/build/encoder/Encoder.d.ts +1 -1
- package/build/encoder/Root.d.ts +9 -0
- package/build/encoder/StateView.d.ts +38 -1
- package/build/encoder/changeTree/inheritedFlags.d.ts +13 -19
- package/build/encoder/changeTree/liveIteration.d.ts +8 -0
- package/build/encoder/changeTree/parentChain.d.ts +30 -8
- package/build/encoder/streaming.d.ts +1 -1
- package/build/index.cjs +3232 -2830
- package/build/index.cjs.map +1 -1
- package/build/index.js +3228 -2826
- package/build/index.mjs +3232 -2830
- package/build/index.mjs.map +1 -1
- package/build/types/HelperTypes.d.ts +24 -14
- package/build/types/TypeContext.d.ts +0 -17
- package/build/types/builder.d.ts +1 -5
- package/build/types/symbols.d.ts +1 -0
- package/package.json +9 -8
- package/src/Metadata.ts +59 -77
- package/src/Reflection.ts +9 -5
- package/src/annotations.ts +28 -18
- package/src/codegen/api.ts +3 -1
- package/src/codegen/cli.ts +5 -2
- package/src/codegen/parser.ts +69 -31
- package/src/codegen/resolve.ts +322 -0
- package/src/codegen/types.ts +4 -1
- package/src/decoder/DecodeOperation.ts +13 -2
- package/src/decoder/strategy/Callbacks.ts +7 -8
- package/src/decoder/strategy/getDecoderStateCallbacks.ts +2 -2
- package/src/encoder/ChangeTree.ts +76 -25
- package/src/encoder/EncodeOperation.ts +10 -1
- package/src/encoder/Encoder.ts +52 -2
- package/src/encoder/Root.ts +28 -8
- package/src/encoder/StateView.ts +150 -66
- package/src/encoder/changeTree/inheritedFlags.ts +164 -45
- package/src/encoder/changeTree/liveIteration.ts +24 -3
- package/src/encoder/changeTree/parentChain.ts +72 -15
- package/src/encoder/streaming.ts +2 -1
- package/src/types/HelperTypes.ts +43 -34
- package/src/types/TypeContext.ts +5 -52
- package/src/types/builder.ts +14 -10
- package/src/types/custom/ArraySchema.ts +57 -14
- package/src/types/symbols.ts +3 -0
|
@@ -9,10 +9,17 @@ import type { StreamSchema } from "./custom/StreamSchema.js";
|
|
|
9
9
|
import type { FieldBuilder } from "./builder.js";
|
|
10
10
|
export type Constructor<T = {}> = new (...args: any[]) => T;
|
|
11
11
|
type PrimitiveStringToType<T> = T extends "string" ? string : T extends "number" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" ? number : T extends "boolean" ? boolean : T;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* What the decoder callbacks accept as "a collection": the public shape, which
|
|
14
|
+
* a plain array satisfies too — `@type([X]) items: X[]` is a common way to
|
|
15
|
+
* declare a field. {@link Collection} is the runtime contract on top of it.
|
|
16
|
+
*/
|
|
17
|
+
export interface CollectionLike<K = any, V = any, IT = V> {
|
|
13
18
|
[Symbol.iterator](): IterableIterator<IT>;
|
|
14
19
|
forEach(callback: Function): void;
|
|
15
20
|
entries(): IterableIterator<[K, V]>;
|
|
21
|
+
}
|
|
22
|
+
export interface Collection<K = any, V = any, IT = V> extends CollectionLike<K, V, IT> {
|
|
16
23
|
/** See {@link $resyncPrune} — every collection kind must declare its resync-sweep semantics. */
|
|
17
24
|
[$resyncPrune](visited: Set<number | string>, prune: (value: V, identity: number | string) => void, keep: (value: V) => void): void;
|
|
18
25
|
}
|
|
@@ -69,35 +76,39 @@ export type InferValueType<T> = T extends FieldBuilder<infer V> ? V : T extends
|
|
|
69
76
|
} ? StreamSchema<InstanceType<ChildType>> : T extends {
|
|
70
77
|
stream: infer ChildType;
|
|
71
78
|
} ? StreamSchema<ChildType> : T extends Constructor ? InstanceType<T> : T extends Record<string | number, string | number> ? T[keyof T] : T extends PrimitiveType ? T : never;
|
|
79
|
+
type IsOptionalBuilderKey<T, K extends keyof T> = T[K] extends FieldBuilder<unknown, boolean, infer O extends boolean> ? O : false;
|
|
72
80
|
type OptionalBuilderKeys<T> = {
|
|
73
|
-
[K in keyof T]
|
|
81
|
+
[K in keyof T]-?: IsOptionalBuilderKey<T, K> extends true ? K : never;
|
|
82
|
+
}[keyof T];
|
|
83
|
+
type RequiredBuilderKeys<T> = {
|
|
84
|
+
[K in keyof T]-?: IsOptionalBuilderKey<T, K> extends true ? never : K;
|
|
74
85
|
}[keyof T];
|
|
75
|
-
type RequiredBuilderKeys<T> = Exclude<keyof T, OptionalBuilderKeys<T>>;
|
|
76
86
|
export type InferSchemaInstanceType<T> = {
|
|
77
87
|
[K in RequiredBuilderKeys<T>]: T[K] extends FieldBuilder<any> ? InferValueType<T[K]> : T[K] extends (...args: any[]) => any ? (T[K] extends new (...args: any[]) => any ? InferValueType<T[K]> : T[K]) : InferValueType<T[K]>;
|
|
78
88
|
} & {
|
|
79
89
|
[K in OptionalBuilderKeys<T>]?: T[K] extends FieldBuilder<infer V> ? V : never;
|
|
80
90
|
} & Schema;
|
|
81
|
-
|
|
82
|
-
[K in keyof T]: T[K] extends Function ? K : never;
|
|
83
|
-
}[keyof T]>;
|
|
91
|
+
type DataKey<T, K extends keyof T> = K extends keyof Schema ? never : T[K] extends Function ? never : K;
|
|
84
92
|
export type NonFunctionPropNames<T> = {
|
|
85
|
-
[K in keyof T]
|
|
93
|
+
[K in keyof T]-?: DataKey<T, K>;
|
|
86
94
|
}[keyof T];
|
|
87
95
|
export type NonFunctionNonPrimitivePropNames<T> = {
|
|
88
|
-
[K in keyof T]
|
|
96
|
+
[K in keyof T]-?: [DataKey<T, K>] extends [never] ? never : T[K] extends number | string | boolean ? never : K;
|
|
89
97
|
}[keyof T];
|
|
90
98
|
type ToJSONValue<U> = U extends Schema ? ToJSON<U> : PrimitiveStringToType<U>;
|
|
91
99
|
type ToJSONField<X> = X extends MapSchema<infer U> ? Record<string, ToJSONValue<U>> : X extends Map<string, infer U> ? Record<string, ToJSONValue<U>> : X extends ArraySchema<infer U> ? ToJSONValue<U>[] : X extends SetSchema<infer U> ? ToJSONValue<U>[] : X extends CollectionSchema<infer U> ? ToJSONValue<U>[] : X extends Schema ? ToJSON<X> : X;
|
|
100
|
+
type IsOptionalKey<T, K extends keyof T> = undefined extends {} ? ({} extends Pick<T, K> ? true : false) : (undefined extends T[K] ? true : false);
|
|
101
|
+
type ToJSONRequiredKeys<T> = {
|
|
102
|
+
[K in keyof T]-?: [DataKey<T, K>] extends [never] ? never : IsOptionalKey<T, K> extends true ? never : K;
|
|
103
|
+
}[keyof T];
|
|
92
104
|
type ToJSONOptionalKeys<T> = {
|
|
93
|
-
[K in keyof T]-?:
|
|
105
|
+
[K in keyof T]-?: [DataKey<T, K>] extends [never] ? never : IsOptionalKey<T, K> extends true ? K : never;
|
|
94
106
|
}[keyof T];
|
|
95
|
-
type
|
|
96
|
-
export type ToJSON<T> = NonFunctionProps<{
|
|
107
|
+
export type ToJSON<T> = {
|
|
97
108
|
[K in ToJSONRequiredKeys<T>]: ToJSONField<T[K]>;
|
|
98
109
|
} & {
|
|
99
110
|
[K in ToJSONOptionalKeys<T>]?: ToJSONField<Exclude<T[K], undefined>>;
|
|
100
|
-
}
|
|
111
|
+
};
|
|
101
112
|
/**
|
|
102
113
|
* The plain DATA shape of a Schema instance type `T`: its synchronized fields
|
|
103
114
|
* with all `Schema` machinery stripped (`assign`, `clone`, `toJSON`, the
|
|
@@ -116,8 +127,7 @@ export type ToJSON<T> = NonFunctionProps<{
|
|
|
116
127
|
*
|
|
117
128
|
* Unlike {@link ToJSON} (a recursive *serialization* shape), this is a flat
|
|
118
129
|
* structural projection: nested Schema / collection fields keep their instance
|
|
119
|
-
* types
|
|
120
|
-
* `NonFunctionProps` pass leaves behind.
|
|
130
|
+
* types.
|
|
121
131
|
*/
|
|
122
132
|
export type Data<T> = Omit<T, keyof Schema>;
|
|
123
133
|
export type IsNever<T> = [T] extends [never] ? true : false;
|
|
@@ -5,18 +5,6 @@ export declare class TypeContext {
|
|
|
5
5
|
};
|
|
6
6
|
schemas: Map<typeof Schema, number>;
|
|
7
7
|
hasFilters: boolean;
|
|
8
|
-
parentFiltered: {
|
|
9
|
-
[typeIdAndParentIndex: string]: boolean;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* True iff `parentFiltered` has at least one entry. Flipped on by
|
|
13
|
-
* `registerFilteredByParent` and read in `checkInheritedFlags` as a
|
|
14
|
-
* cheap gate to skip the string-keyed `parentFiltered[key]` lookup
|
|
15
|
-
* when no class has registered filter inheritance via ancestry — the
|
|
16
|
-
* common case when @view tags exist only on sibling fields, not
|
|
17
|
-
* along any attachment chain.
|
|
18
|
-
*/
|
|
19
|
-
hasParentFilteredEntries: boolean;
|
|
20
8
|
/**
|
|
21
9
|
* For inheritance support
|
|
22
10
|
* Keeps track of which classes extends which. (parent -> children)
|
|
@@ -31,10 +19,5 @@ export declare class TypeContext {
|
|
|
31
19
|
add(schema: typeof Schema, typeid?: number): boolean;
|
|
32
20
|
getTypeId(klass: typeof Schema): number;
|
|
33
21
|
private discoverTypes;
|
|
34
|
-
/**
|
|
35
|
-
* Keep track of which classes have filters applied.
|
|
36
|
-
* Format: `${typeid}-${parentTypeid}-${parentIndex}`
|
|
37
|
-
*/
|
|
38
|
-
private registerFilteredByParent;
|
|
39
22
|
debug(): string;
|
|
40
23
|
}
|
package/build/types/builder.d.ts
CHANGED
|
@@ -213,26 +213,22 @@ interface PrimitiveFactory<TBase> {
|
|
|
213
213
|
(): FieldBuilder<TBase>;
|
|
214
214
|
<T extends TBase>(): FieldBuilder<T>;
|
|
215
215
|
}
|
|
216
|
-
export type ChildType = RawPrimitiveType | Constructor<Schema
|
|
216
|
+
export type ChildType = RawPrimitiveType | Constructor<Schema>;
|
|
217
217
|
interface ArrayFactory {
|
|
218
218
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<ArraySchema<InstanceType<C>>, true, false>;
|
|
219
219
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<ArraySchema<InferValueType<P>>, true, false>;
|
|
220
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<ArraySchema<V>, true, false>;
|
|
221
220
|
}
|
|
222
221
|
interface MapFactory {
|
|
223
222
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<MapSchema<InstanceType<C>>, true, false>;
|
|
224
223
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<MapSchema<InferValueType<P>>, true, false>;
|
|
225
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<MapSchema<V>, true, false>;
|
|
226
224
|
}
|
|
227
225
|
interface SetFactory {
|
|
228
226
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<SetSchema<InstanceType<C>>, true, false>;
|
|
229
227
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<SetSchema<InferValueType<P>>, true, false>;
|
|
230
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<SetSchema<V>, true, false>;
|
|
231
228
|
}
|
|
232
229
|
interface CollectionFactory {
|
|
233
230
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<CollectionSchema<InstanceType<C>>, true, false>;
|
|
234
231
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<CollectionSchema<InferValueType<P>>, true, false>;
|
|
235
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<CollectionSchema<V>, true, false>;
|
|
236
232
|
}
|
|
237
233
|
interface StreamFactory {
|
|
238
234
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<StreamSchema<InstanceType<C>>, true, false>;
|
package/build/types/symbols.d.ts
CHANGED
|
@@ -80,6 +80,7 @@ export declare const $viewFieldIndexes = "~__viewFieldIndexes";
|
|
|
80
80
|
export declare const $fieldIndexesByViewTag = "$__fieldIndexesByViewTag";
|
|
81
81
|
export declare const $unreliableFieldIndexes = "~__unreliableFieldIndexes";
|
|
82
82
|
export declare const $patchOnlyFieldIndexes = "~__patchOnlyFieldIndexes";
|
|
83
|
+
export declare const $fullSyncSkipIndexes = "~__fullSyncSkipIndexes";
|
|
83
84
|
export declare const $fullStateOnlyFieldIndexes = "~__fullStateOnlyFieldIndexes";
|
|
84
85
|
export declare const $streamFieldIndexes = "~__streamFieldIndexes";
|
|
85
86
|
export declare const $streamPriorities = "~__streamPriorities";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/schema",
|
|
3
|
-
"version": "5.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.0.20",
|
|
4
|
+
"description": "Automatic state replication for multiplayer games. Mutate plain objects, and every client gets a live, typed mirror through delta encoding.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"schema-codegen": "bin/schema-codegen",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"bench:gate": "node bench/run.mjs --assert --samples 3 --filter gate",
|
|
15
15
|
"profile:cpu": "node bench/profile.mjs --cpu",
|
|
16
16
|
"profile:heap": "node bench/profile.mjs --heap",
|
|
17
|
-
"build": "tsc -p tsconfig.build.json && rollup -c rollup.config.mjs",
|
|
18
|
-
"watch": "tsc -p tsconfig.build.json -w",
|
|
19
|
-
"typecheck:build": "tsc -p tsconfig.build.json --noEmit",
|
|
17
|
+
"build": "node node_modules/typescript/bin/tsc -p tsconfig.build.json && rollup -c rollup.config.mjs",
|
|
18
|
+
"watch": "node node_modules/typescript/bin/tsc -p tsconfig.build.json -w",
|
|
19
|
+
"typecheck:build": "node node_modules/typescript/bin/tsc -p tsconfig.build.json --noEmit",
|
|
20
20
|
"test": "npm run test:types && tsx --tsconfig tsconfig.test.json ./node_modules/mocha/bin/mocha.js \"test/*.test.ts\" \"test/**/*.test.ts\"",
|
|
21
|
-
"test:types": "tsc -p tsconfig.build.json &&
|
|
21
|
+
"test:types": "node node_modules/typescript/bin/tsc -p tsconfig.build.json && node test/types/run.mjs typescript typescript-7",
|
|
22
22
|
"test:exports": "node test/verify-exports.mjs",
|
|
23
|
-
"typecheck": "tsc -p tsconfig.test.json",
|
|
23
|
+
"typecheck": "node node_modules/typescript/bin/tsc -p tsconfig.test.json",
|
|
24
24
|
"coverage": "c8 npm run test",
|
|
25
25
|
"generate-test-1": "bin/schema-codegen test-external/PrimitiveTypes.ts --namespace SchemaTest.PrimitiveTypes --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/PrimitiveTypes",
|
|
26
26
|
"generate-test-2": "bin/schema-codegen test-external/ChildSchemaTypes.ts --namespace SchemaTest.ChildSchemaTypes --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/ChildSchemaTypes",
|
|
@@ -94,7 +94,8 @@
|
|
|
94
94
|
"source-map-support": "^0.5.13",
|
|
95
95
|
"tslib": "^2.1.0",
|
|
96
96
|
"tsx": "^4.21.0",
|
|
97
|
-
"typescript": "^5.9.3"
|
|
97
|
+
"typescript": "^5.9.3",
|
|
98
|
+
"typescript-7": "npm:typescript@^7.0.2"
|
|
98
99
|
},
|
|
99
100
|
"peerDependencies": {
|
|
100
101
|
"typescript": ">=5.0.0"
|
package/src/Metadata.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefinitionType, getPropertyDescriptor } from "./annotations.js";
|
|
2
2
|
import { Schema } from "./Schema.js";
|
|
3
3
|
import { getType, registeredTypes, TypeDefinition } from "./types/registry.js";
|
|
4
|
-
import { $decoder, $descriptors, $encoder, $encoders, $fieldIndexesByViewTag, $numFields, $refTypeFieldIndexes, $fullStateOnlyFieldIndexes, $streamFieldIndexes, $streamPriorities, $track, $patchOnlyFieldIndexes, $unreliableFieldIndexes, $viewFieldIndexes } from "./types/symbols.js";
|
|
4
|
+
import { $decoder, $descriptors, $encoder, $encoders, $fieldIndexesByViewTag, $numFields, $refTypeFieldIndexes, $fullStateOnlyFieldIndexes, $fullSyncSkipIndexes, $streamFieldIndexes, $streamPriorities, $track, $patchOnlyFieldIndexes, $unreliableFieldIndexes, $viewFieldIndexes } from "./types/symbols.js";
|
|
5
5
|
import { ARRAY_STREAM_NOT_SUPPORTED } from "./encoder/streaming.js";
|
|
6
6
|
import { encode } from "./encoding/encode.js";
|
|
7
7
|
import { TypeContext } from "./types/TypeContext.js";
|
|
@@ -37,6 +37,8 @@ export type Metadata =
|
|
|
37
37
|
{ [$refTypeFieldIndexes]: number[]; } & // all field indexes containing Ref types (Schema, ArraySchema, MapSchema, etc)
|
|
38
38
|
{ [$unreliableFieldIndexes]: number[]; } & // all field indexes tagged with @unreliable
|
|
39
39
|
{ [$patchOnlyFieldIndexes]: number[]; } & // all field indexes tagged with @patchOnly (not persisted to snapshots)
|
|
40
|
+
{ [$fullSyncSkipIndexes]: number[]; } & // @patchOnly ∪ @deprecated() — never read during full sync
|
|
41
|
+
|
|
40
42
|
{ [$fullStateOnlyFieldIndexes]: number[]; } & // all field indexes tagged @fullStateOnly / .fullStateOnly() (not tracked after assignment)
|
|
41
43
|
{ [$streamFieldIndexes]: number[]; } & // all field indexes holding a t.stream(...) collection
|
|
42
44
|
{ [$streamPriorities]: { [field: number]: (view: any, element: any) => number }; } & // per-stream-field priority callback declared at schema definition time
|
|
@@ -113,6 +115,30 @@ function isTSEnum(_enum: any) {
|
|
|
113
115
|
return false;
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
// Copied parent → subclass on Metadata.initialize, so each class owns its list.
|
|
119
|
+
const INHERITED_ARRAY_KEYS = [
|
|
120
|
+
$refTypeFieldIndexes,
|
|
121
|
+
$unreliableFieldIndexes,
|
|
122
|
+
$patchOnlyFieldIndexes,
|
|
123
|
+
$fullSyncSkipIndexes,
|
|
124
|
+
$fullStateOnlyFieldIndexes,
|
|
125
|
+
$streamFieldIndexes,
|
|
126
|
+
$encoders,
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
/** Append to a non-enumerable metadata index list, creating it on first use. */
|
|
130
|
+
function pushIndexList(metadata: any, key: string, index: number) {
|
|
131
|
+
if (!metadata[key]) {
|
|
132
|
+
Object.defineProperty(metadata, key, {
|
|
133
|
+
value: [],
|
|
134
|
+
enumerable: false,
|
|
135
|
+
configurable: true,
|
|
136
|
+
writable: true,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
metadata[key].push(index);
|
|
140
|
+
}
|
|
141
|
+
|
|
116
142
|
export const Metadata = {
|
|
117
143
|
|
|
118
144
|
addField(metadata: any, index: number, name: string, type: DefinitionType, descriptor?: PropertyDescriptor) {
|
|
@@ -302,15 +328,26 @@ export const Metadata = {
|
|
|
302
328
|
}
|
|
303
329
|
metadata[index].patchOnly = true;
|
|
304
330
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
331
|
+
pushIndexList(metadata, $patchOnlyFieldIndexes, index);
|
|
332
|
+
pushIndexList(metadata, $fullSyncSkipIndexes, index); // not persisted to snapshots
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* `@deprecated()` bookkeeping: the field keeps its wire index (so peers
|
|
337
|
+
* that still carry it stay compatible) but is excluded from full sync —
|
|
338
|
+
* its accessor may throw — and hidden from `for..in` consumers.
|
|
339
|
+
*/
|
|
340
|
+
setDeprecated(metadata: Metadata, fieldName: string) {
|
|
341
|
+
const index = metadata[fieldName];
|
|
342
|
+
metadata[index].deprecated = true;
|
|
343
|
+
|
|
344
|
+
pushIndexList(metadata, $fullSyncSkipIndexes, index);
|
|
345
|
+
|
|
346
|
+
Object.defineProperty(metadata, index, {
|
|
347
|
+
value: metadata[index],
|
|
348
|
+
enumerable: false,
|
|
349
|
+
configurable: true
|
|
350
|
+
});
|
|
314
351
|
},
|
|
315
352
|
|
|
316
353
|
setFullStateOnly(metadata: Metadata, fieldName: string) {
|
|
@@ -324,15 +361,7 @@ export const Metadata = {
|
|
|
324
361
|
}
|
|
325
362
|
metadata[index].fullStateOnly = true;
|
|
326
363
|
|
|
327
|
-
|
|
328
|
-
Object.defineProperty(metadata, $fullStateOnlyFieldIndexes, {
|
|
329
|
-
value: [],
|
|
330
|
-
enumerable: false,
|
|
331
|
-
configurable: true,
|
|
332
|
-
writable: true,
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
metadata[$fullStateOnlyFieldIndexes].push(index);
|
|
364
|
+
pushIndexList(metadata, $fullStateOnlyFieldIndexes, index);
|
|
336
365
|
},
|
|
337
366
|
|
|
338
367
|
setStream(metadata: Metadata, fieldName: string) {
|
|
@@ -510,54 +539,17 @@ export const Metadata = {
|
|
|
510
539
|
});
|
|
511
540
|
}
|
|
512
541
|
|
|
513
|
-
//
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
if (parentMetadata[$unreliableFieldIndexes] !== undefined) {
|
|
525
|
-
Object.defineProperty(metadata, $unreliableFieldIndexes, {
|
|
526
|
-
value: [...parentMetadata[$unreliableFieldIndexes]],
|
|
527
|
-
enumerable: false,
|
|
528
|
-
configurable: true,
|
|
529
|
-
writable: true,
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// $patchOnlyFieldIndexes
|
|
534
|
-
if (parentMetadata[$patchOnlyFieldIndexes] !== undefined) {
|
|
535
|
-
Object.defineProperty(metadata, $patchOnlyFieldIndexes, {
|
|
536
|
-
value: [...parentMetadata[$patchOnlyFieldIndexes]],
|
|
537
|
-
enumerable: false,
|
|
538
|
-
configurable: true,
|
|
539
|
-
writable: true,
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// $fullStateOnlyFieldIndexes
|
|
544
|
-
if (parentMetadata[$fullStateOnlyFieldIndexes] !== undefined) {
|
|
545
|
-
Object.defineProperty(metadata, $fullStateOnlyFieldIndexes, {
|
|
546
|
-
value: [...parentMetadata[$fullStateOnlyFieldIndexes]],
|
|
547
|
-
enumerable: false,
|
|
548
|
-
configurable: true,
|
|
549
|
-
writable: true,
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// $streamFieldIndexes
|
|
554
|
-
if (parentMetadata[$streamFieldIndexes] !== undefined) {
|
|
555
|
-
Object.defineProperty(metadata, $streamFieldIndexes, {
|
|
556
|
-
value: [...parentMetadata[$streamFieldIndexes]],
|
|
557
|
-
enumerable: false,
|
|
558
|
-
configurable: true,
|
|
559
|
-
writable: true,
|
|
560
|
-
});
|
|
542
|
+
// per-class arrays the subclass extends independently
|
|
543
|
+
for (const key of INHERITED_ARRAY_KEYS) {
|
|
544
|
+
const list = parentMetadata[key] as unknown as any[] | undefined;
|
|
545
|
+
if (list !== undefined) {
|
|
546
|
+
Object.defineProperty(metadata, key, {
|
|
547
|
+
value: [...list],
|
|
548
|
+
enumerable: false,
|
|
549
|
+
configurable: true,
|
|
550
|
+
writable: true,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
561
553
|
}
|
|
562
554
|
|
|
563
555
|
// $descriptors
|
|
@@ -567,16 +559,6 @@ export const Metadata = {
|
|
|
567
559
|
configurable: true,
|
|
568
560
|
writable: true,
|
|
569
561
|
});
|
|
570
|
-
|
|
571
|
-
// $encoders
|
|
572
|
-
if (parentMetadata[$encoders] !== undefined) {
|
|
573
|
-
Object.defineProperty(metadata, $encoders, {
|
|
574
|
-
value: [...parentMetadata[$encoders]],
|
|
575
|
-
enumerable: false,
|
|
576
|
-
configurable: true,
|
|
577
|
-
writable: true,
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
562
|
}
|
|
581
563
|
}
|
|
582
564
|
|
package/src/Reflection.ts
CHANGED
|
@@ -149,9 +149,15 @@ Reflection.encode = function (encoder: Encoder, it: Iterator = { offset: 0 }) {
|
|
|
149
149
|
// if metadata is the same reference as the parent class - it means the class has no own metadata
|
|
150
150
|
//
|
|
151
151
|
if (metadata !== inheritFrom[Symbol.metadata]) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
// Walk by index rather than `for…in`: `@deprecated()` makes its
|
|
153
|
+
// metadata slot non-enumerable, and dropping it from the payload
|
|
154
|
+
// shifts every later field down one wire index on the peer.
|
|
155
|
+
const numFields = (metadata[$numFields] ?? -1) as number;
|
|
156
|
+
for (let index = 0; index <= numFields; index++) {
|
|
157
|
+
const field = metadata[index];
|
|
158
|
+
if (field === undefined) { continue; }
|
|
159
|
+
|
|
160
|
+
const fieldName = field.name;
|
|
155
161
|
|
|
156
162
|
// skip fields from parent classes
|
|
157
163
|
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
@@ -163,8 +169,6 @@ Reflection.encode = function (encoder: Encoder, it: Iterator = { offset: 0 }) {
|
|
|
163
169
|
|
|
164
170
|
let fieldType: string;
|
|
165
171
|
|
|
166
|
-
const field = metadata[index];
|
|
167
|
-
|
|
168
172
|
if (typeof (field.type) === "string") {
|
|
169
173
|
fieldType = field.type;
|
|
170
174
|
|
package/src/annotations.ts
CHANGED
|
@@ -477,12 +477,27 @@ function makeCollectionSetter(
|
|
|
477
477
|
|
|
478
478
|
if (value !== undefined && value !== null) {
|
|
479
479
|
// automatic Array → ArraySchema / Map → MapSchema conversion.
|
|
480
|
+
// `$childType` goes on before populating — push()/set() gate
|
|
481
|
+
// their `assertInstanceType` on it.
|
|
480
482
|
if (isArrayKlass && !(value instanceof ArraySchema)) {
|
|
481
|
-
|
|
483
|
+
const array: any = new ArraySchema();
|
|
484
|
+
array[$childType] = type;
|
|
485
|
+
array.push(...value);
|
|
486
|
+
value = array;
|
|
487
|
+
|
|
482
488
|
} else if (isMapKlass && !(value instanceof MapSchema)) {
|
|
483
|
-
|
|
489
|
+
const map: any = new MapSchema();
|
|
490
|
+
map[$childType] = type;
|
|
491
|
+
if (value instanceof Map) {
|
|
492
|
+
value.forEach((v, k) => map.set(k, v));
|
|
493
|
+
} else {
|
|
494
|
+
for (const k in value) { map.set(k, value[k]); }
|
|
495
|
+
}
|
|
496
|
+
value = map;
|
|
497
|
+
|
|
498
|
+
} else {
|
|
499
|
+
value[$childType] = type;
|
|
484
500
|
}
|
|
485
|
-
value[$childType] = type;
|
|
486
501
|
|
|
487
502
|
const changeTree = this[$changes];
|
|
488
503
|
const ctor = this.constructor as typeof Schema;
|
|
@@ -569,9 +584,7 @@ export function getPropertyDescriptor(
|
|
|
569
584
|
export function deprecated(throws: boolean = true): PropertyDecorator {
|
|
570
585
|
return function (klass: typeof Schema, field: string) {
|
|
571
586
|
const metadata = Metadata.initialize(klass.constructor as typeof Schema);
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
metadata[fieldIndex].deprecated = true;
|
|
587
|
+
Metadata.setDeprecated(metadata, field);
|
|
575
588
|
|
|
576
589
|
if (throws) {
|
|
577
590
|
metadata[$descriptors] ??= {};
|
|
@@ -584,13 +597,6 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
|
|
|
584
597
|
// Override accessor on the prototype so deprecated throws at access.
|
|
585
598
|
Object.defineProperty(klass, field, metadata[$descriptors][field]);
|
|
586
599
|
}
|
|
587
|
-
|
|
588
|
-
// flag metadata[field] as non-enumerable
|
|
589
|
-
Object.defineProperty(metadata, fieldIndex, {
|
|
590
|
-
value: metadata[fieldIndex],
|
|
591
|
-
enumerable: false,
|
|
592
|
-
configurable: true
|
|
593
|
-
});
|
|
594
600
|
}
|
|
595
601
|
}
|
|
596
602
|
|
|
@@ -662,9 +668,13 @@ type HasExplicitInit<T> = T extends { initialize: (...args: infer P) => void }
|
|
|
662
668
|
*/
|
|
663
669
|
export type FieldsAndMethods = Record<string, FieldBuilder<any, boolean, boolean> | (new (...args: any[]) => Schema) | Function>;
|
|
664
670
|
|
|
671
|
+
// One spelling for every site that names the instance — identical
|
|
672
|
+
// instantiations compare by identity, not structurally.
|
|
673
|
+
type SchemaInstance<T, P extends typeof Schema> = InferSchemaInstanceType<T> & InstanceType<P>;
|
|
674
|
+
|
|
665
675
|
export interface SchemaWithExtends<T, P extends typeof Schema> {
|
|
666
676
|
extend: <T2 extends FieldsAndMethods = FieldsAndMethods>(
|
|
667
|
-
fields: T2 & ThisType<
|
|
677
|
+
fields: T2 & ThisType<SchemaInstance<T & T2, P>>,
|
|
668
678
|
name?: string,
|
|
669
679
|
) => SchemaWithExtendsConstructor<T & T2, ExtractInitProps<T & T2>, P>;
|
|
670
680
|
}
|
|
@@ -686,7 +696,7 @@ export interface SchemaWithExtendsConstructor<
|
|
|
686
696
|
InitProps,
|
|
687
697
|
P extends typeof Schema
|
|
688
698
|
> extends SchemaWithExtends<T, P> {
|
|
689
|
-
'~type':
|
|
699
|
+
'~type': SchemaInstance<T, P>;
|
|
690
700
|
// Constructor signature:
|
|
691
701
|
// - InitProps = never (zero-arg initialize): no args.
|
|
692
702
|
// - InitProps is a tuple (multi-arg initialize): spread it.
|
|
@@ -702,8 +712,8 @@ export interface SchemaWithExtendsConstructor<
|
|
|
702
712
|
: HasExplicitInit<T> extends true ? [InitProps]
|
|
703
713
|
: IsInitPropsRequired<T> extends true ? ([] | [InitProps])
|
|
704
714
|
: [InitProps?]
|
|
705
|
-
):
|
|
706
|
-
prototype:
|
|
715
|
+
): SchemaInstance<T, P>;
|
|
716
|
+
prototype: SchemaInstance<T, P> & {
|
|
707
717
|
initialize(...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : [InitProps]): void;
|
|
708
718
|
};
|
|
709
719
|
}
|
|
@@ -749,7 +759,7 @@ export function schema<
|
|
|
749
759
|
T extends FieldsAndMethods,
|
|
750
760
|
P extends typeof Schema = typeof Schema
|
|
751
761
|
>(
|
|
752
|
-
fieldsAndMethods: T & ThisType<
|
|
762
|
+
fieldsAndMethods: T & ThisType<SchemaInstance<T, P>>,
|
|
753
763
|
name?: string,
|
|
754
764
|
inherits: P = Schema as P,
|
|
755
765
|
): SchemaWithExtendsConstructor<T, ExtractInitProps<T>, P> {
|
package/src/codegen/api.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface GenerateOptions {
|
|
|
24
24
|
decorator?: string;
|
|
25
25
|
namespace?: string;
|
|
26
26
|
bundle?: boolean;
|
|
27
|
+
/** Overrides the nearest-tsconfig lookup used to resolve import path aliases. */
|
|
28
|
+
tsconfig?: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export function generate(targetId: string, options: GenerateOptions) {
|
|
@@ -53,7 +55,7 @@ export function generate(targetId: string, options: GenerateOptions) {
|
|
|
53
55
|
return acc;
|
|
54
56
|
}, [])
|
|
55
57
|
|
|
56
|
-
const structures = parseFiles(options.files, options.decorator);
|
|
58
|
+
const structures = parseFiles(options.files, options.decorator, undefined, { tsconfig: options.tsconfig });
|
|
57
59
|
|
|
58
60
|
// Post-process classes before generating
|
|
59
61
|
structures.classes.forEach(klass => klass.postProcessing());
|
package/src/codegen/cli.ts
CHANGED
|
@@ -21,7 +21,9 @@ ${Object.
|
|
|
21
21
|
|
|
22
22
|
Optional:
|
|
23
23
|
--namespace: generate namespace on output code
|
|
24
|
-
--decorator: custom name for @type decorator to scan for
|
|
24
|
+
--decorator: custom name for @type decorator to scan for
|
|
25
|
+
--tsconfig: tsconfig.json to resolve import path aliases with
|
|
26
|
+
(default: nearest tsconfig.json/jsconfig.json above each source file)`);
|
|
25
27
|
process.exit(exitCode);
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -49,7 +51,8 @@ try {
|
|
|
49
51
|
decorator: args.decorator,
|
|
50
52
|
output: args.output,
|
|
51
53
|
namespace: args.namespace,
|
|
52
|
-
bundle: args.bundle
|
|
54
|
+
bundle: args.bundle,
|
|
55
|
+
tsconfig: args.tsconfig,
|
|
53
56
|
});
|
|
54
57
|
|
|
55
58
|
} catch (e) {
|