@spine-event-engine/core 2.0.0-snapshot.2 → 2.0.0-snapshot.21
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 +61 -8
- package/REFERENCE.md +31 -3
- package/dist/codegen/index.d.ts +10 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +20 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/entity/entity-column.d.ts +158 -0
- package/dist/entity/entity-column.d.ts.map +1 -0
- package/dist/entity/entity-column.js +303 -0
- package/dist/entity/entity-column.js.map +1 -0
- package/dist/index.d.ts +84 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +331 -7
- package/dist/index.js.map +1 -1
- package/dist/internal/subscription-lifecycle.d.ts +0 -1
- package/dist/internal/subscription-lifecycle.d.ts.map +1 -1
- package/dist/internal/subscription-lifecycle.js +0 -1
- package/dist/internal/subscription-lifecycle.js.map +1 -1
- package/dist/query/entity-field-classification.d.ts +20 -0
- package/dist/query/entity-field-classification.d.ts.map +1 -0
- package/dist/query/entity-field-classification.js +62 -0
- package/dist/query/entity-field-classification.js.map +1 -0
- package/dist/query/entity-query.d.ts +405 -0
- package/dist/query/entity-query.d.ts.map +1 -0
- package/dist/query/entity-query.js +841 -0
- package/dist/query/entity-query.js.map +1 -0
- package/dist/query/generated-entity-query.d.ts +197 -0
- package/dist/query/generated-entity-query.d.ts.map +1 -0
- package/dist/query/generated-entity-query.js +202 -0
- package/dist/query/generated-entity-query.js.map +1 -0
- package/dist/spi/entity-query-plan.d.ts +7 -0
- package/dist/spi/entity-query-plan.d.ts.map +1 -0
- package/dist/spi/entity-query-plan.js +15 -0
- package/dist/spi/entity-query-plan.js.map +1 -0
- package/dist/spi/subscription-lifecycle.d.ts +5 -0
- package/dist/spi/subscription-lifecycle.d.ts.map +1 -0
- package/dist/spi/subscription-lifecycle.js +18 -0
- package/dist/spi/subscription-lifecycle.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -7
package/README.md
CHANGED
|
@@ -5,13 +5,37 @@ Spine TS application. Use it when application code needs to validate a message,
|
|
|
5
5
|
pack it into `google.protobuf.Any`, create a command or event envelope, or look
|
|
6
6
|
up a generated message schema by its Spine type URL.
|
|
7
7
|
|
|
8
|
+
This is an experimental snapshot package. Use Node 24 or newer and generated
|
|
9
|
+
Spine message schemas.
|
|
10
|
+
|
|
11
|
+
## Install and validate one message
|
|
12
|
+
|
|
13
|
+
Install the package with the generated model schemas your application uses:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm add @spine-event-engine/core@snapshot @spine-event-engine/proto@snapshot @bufbuild/protobuf
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then validate and pack one generated message before adding routing or server assembly:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { create } from "@bufbuild/protobuf";
|
|
23
|
+
import { AnyMessages, Validate } from "@spine-event-engine/core";
|
|
24
|
+
import { UserIdSchema } from "@spine-event-engine/proto";
|
|
25
|
+
|
|
26
|
+
const userId = create(UserIdSchema, { value: "ava" });
|
|
27
|
+
Validate.check(UserIdSchema, userId);
|
|
28
|
+
const packed = AnyMessages.pack(UserIdSchema, userId);
|
|
29
|
+
console.log(AnyMessages.unpack(packed, UserIdSchema)?.value);
|
|
30
|
+
```
|
|
31
|
+
|
|
8
32
|
## Message-interface tokens
|
|
9
33
|
|
|
10
34
|
A generated interface export has one name in two TypeScript namespaces: use it
|
|
11
35
|
as a type for message shape and as a value token in a repository `.route(...)`
|
|
12
36
|
call. The To-Do `TaskEvent` token groups task events; its authored
|
|
13
37
|
`TaskAssignmentEvent` counterpart groups assignment events. Start with the
|
|
14
|
-
[To-Do walkthrough](
|
|
38
|
+
[To-Do walkthrough](https://github.com/SpineEventEngine/spine-ts/blob/master/examples/todo/USER_GUIDE.md) for the complete path.
|
|
15
39
|
|
|
16
40
|
For the detailed contract and integration notes, see
|
|
17
41
|
[REFERENCE documentation for agents](REFERENCE.md).
|
|
@@ -30,9 +54,8 @@ pnpm typecheck:build
|
|
|
30
54
|
```
|
|
31
55
|
|
|
32
56
|
Run this workspace-wide TypeScript build from the repository root. For an
|
|
33
|
-
experimental npm consumer, install
|
|
34
|
-
|
|
35
|
-
`@spine-event-engine/core@snapshot` tag.
|
|
57
|
+
experimental npm consumer, install `@spine-event-engine/core@snapshot`.
|
|
58
|
+
The snapshot tag can change before a stable release.
|
|
36
59
|
|
|
37
60
|
## ✅ Validate a message
|
|
38
61
|
|
|
@@ -115,6 +138,35 @@ that generated `BoardId`. An application can register another reversible
|
|
|
115
138
|
mapping in `StringifierRegistry`. If compact Proto JSON encounters an `Any`,
|
|
116
139
|
also call `setTypeRegistry()` with the application's generated `TypeRegistry`.
|
|
117
140
|
|
|
141
|
+
## Query Projection state
|
|
142
|
+
|
|
143
|
+
Normal `spine-proto generate` emits a `_query.ts` companion for eligible Aggregate, Projection,
|
|
144
|
+
and Process Manager states. For example, Todo can import its generated `TaskListQuery` and build
|
|
145
|
+
a context-free query:
|
|
146
|
+
|
|
147
|
+
<!-- docs-snippet-path: examples/todo/src/index.ts -->
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { TaskListQuery } from "../generated/spine/examples/todo/task_list_query.js";
|
|
151
|
+
|
|
152
|
+
// Find nonempty lists, starting with the most open tasks.
|
|
153
|
+
const query = TaskListQuery.create()
|
|
154
|
+
.openTaskCount()
|
|
155
|
+
.isAtLeast(1)
|
|
156
|
+
.orderBy("openTaskCount", "desc")
|
|
157
|
+
.limit(10)
|
|
158
|
+
.build();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The companion registers its descriptor-backed columns when imported. Successive comparisons use
|
|
162
|
+
AND; `either(...)` accepts synchronous condition-only callbacks for OR branches. Apply IDs,
|
|
163
|
+
ordering, limits, and `build()` to the outer query. `byId(...)` uses the first state field.
|
|
164
|
+
`limit()` requires `orderBy()` and accepts a positive integer.
|
|
165
|
+
Ordered columns also expose `isGreaterThan`, `isAtLeast`, `isLessThan`, and `isAtMost`.
|
|
166
|
+
Every build captures a detached value;
|
|
167
|
+
the client or Process Manager supplies actor and tenant context when it executes the query.
|
|
168
|
+
`EntityQuery` remains available for callers using the existing schema-and-columns builder.
|
|
169
|
+
|
|
118
170
|
## 🚫 Throw a generated domain rejection
|
|
119
171
|
|
|
120
172
|
The model generator creates typed rejection factories for top-level messages in
|
|
@@ -122,8 +174,9 @@ an application's `*rejections.proto` files. Application code imports that
|
|
|
122
174
|
generated companion and throws its factory result. This example is from a
|
|
123
175
|
source file in the Todo model package's `src` directory.
|
|
124
176
|
|
|
177
|
+
<!-- docs-snippet-path: examples/todo/src/index.ts -->
|
|
178
|
+
|
|
125
179
|
```ts
|
|
126
|
-
// docs-snippet-path: examples/todo/src/index.ts
|
|
127
180
|
import { create } from "@bufbuild/protobuf";
|
|
128
181
|
import { TaskIdSchema } from "../generated/spine/examples/todo/task_id_pb.js";
|
|
129
182
|
import { TaskAlreadyDone } from "../generated/spine/examples/todo/task_rejections.js";
|
|
@@ -145,7 +198,7 @@ by hand.
|
|
|
145
198
|
|
|
146
199
|
## 🔗 Learn more
|
|
147
200
|
|
|
148
|
-
- [Protobuf package](
|
|
149
|
-
- [Model-generation tools](
|
|
150
|
-
- [Server](
|
|
201
|
+
- [Protobuf package](https://github.com/SpineEventEngine/spine-ts/blob/master/packages/proto/README.md)
|
|
202
|
+
- [Model-generation tools](https://github.com/SpineEventEngine/spine-ts/blob/master/packages/proto-tools/README.md)
|
|
203
|
+
- [Server](https://github.com/SpineEventEngine/spine-ts/blob/master/packages/server/README.md)
|
|
151
204
|
- [Reference for coding agents](REFERENCE.md)
|
package/REFERENCE.md
CHANGED
|
@@ -18,6 +18,30 @@ Import from `@spine-event-engine/core`. The package exports `Validate`,
|
|
|
18
18
|
`StringifierRegistry`, the `Stringifier` contract, and their exported input,
|
|
19
19
|
result, and metadata types.
|
|
20
20
|
|
|
21
|
+
Descriptor-backed `EntityColumn` and `EntityQuery` behavior is canonical in
|
|
22
|
+
core. The generated `GeneratedEntityColumns` helper is intentionally excluded
|
|
23
|
+
from the root: generated model code imports it from
|
|
24
|
+
`@spine-event-engine/core/codegen`. `@spine-event-engine/client-node` retains
|
|
25
|
+
its compatibility exports and its `/codegen` forwarding entry point.
|
|
26
|
+
|
|
27
|
+
Normal Proto generation emits `_query.ts` companions for eligible Entity states, including nested
|
|
28
|
+
states. Their exported `StateQuery.create()` methods expose marked columns and the `version`,
|
|
29
|
+
`archived`, and `deleted` system columns. `build()` returns an independent query description with
|
|
30
|
+
no actor or tenant context; it can be reused by a Process Manager, browser client, or subscription.
|
|
31
|
+
Successive comparisons form a conjunction, and `either(...)` combines synchronous condition-only
|
|
32
|
+
callback branches as a disjunction. Apply IDs, ordering, limits, and build to the outer query.
|
|
33
|
+
Generated field methods that conflict with `build`, `either`, `byId`, `orderBy`,
|
|
34
|
+
`limit`, `create`, `constructor`, or JavaScript object methods receive a `Column` suffix; a
|
|
35
|
+
numeric suffix resolves a further collision. Nested query exports join message names with `_`.
|
|
36
|
+
The first declared state field supplies the ID type even when its name is not `id`.
|
|
37
|
+
|
|
38
|
+
## Subscription lifecycle SPI
|
|
39
|
+
|
|
40
|
+
Framework integrations that coordinate subscription activation import
|
|
41
|
+
`SUBSCRIPTION_ACTIVATION_HANDSHAKE_MS` from
|
|
42
|
+
`@spine-event-engine/core/spi/subscription-lifecycle`. This is not an
|
|
43
|
+
application subscription API and is not exported from the core root.
|
|
44
|
+
|
|
21
45
|
## Storage value helpers
|
|
22
46
|
|
|
23
47
|
`Identifiers` packs and unpacks the generated-message and supported primitive
|
|
@@ -62,9 +86,13 @@ non-empty and whitespace-free. `AnyMessages.pack()` validates unless
|
|
|
62
86
|
Spine-aware `Any`. `unpack()` and `unpackUsing()` return `undefined` for an
|
|
63
87
|
unknown/mismatched URL or malformed bytes.
|
|
64
88
|
|
|
65
|
-
`SignalEnvelopes.command()` and `.event()`
|
|
66
|
-
contexts and pack the supplied domain message. They
|
|
67
|
-
|
|
89
|
+
`SignalEnvelopes.command()` and `.event()` generate fresh secure UUID v4 IDs,
|
|
90
|
+
clone caller-supplied contexts, and pack the supplied domain message. They use
|
|
91
|
+
`crypto.randomUUID()` when available, otherwise secure `crypto.getRandomValues()`;
|
|
92
|
+
they fail when neither secure API exists. They do not create timestamps, actor
|
|
93
|
+
context, tenant context, storage records, or routing data. Existing envelope
|
|
94
|
+
transport and storage continue to retain their supplied IDs without validating
|
|
95
|
+
that those existing values have UUID format.
|
|
68
96
|
|
|
69
97
|
## Registry
|
|
70
98
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated-code-only Entity column declaration helpers.
|
|
3
|
+
*/
|
|
4
|
+
export { GeneratedEntityColumns } from "../entity/entity-column.js";
|
|
5
|
+
export { GeneratedEntityQueries } from "../query/generated-entity-query.js";
|
|
6
|
+
export { EntityQueryDescription } from "../query/entity-query.js";
|
|
7
|
+
export type { EntityQueryDraft } from "../query/entity-query.js";
|
|
8
|
+
export type { GeneratedQueryBuilder } from "../query/generated-entity-query.js";
|
|
9
|
+
export type { EntityColumnDefinition, EntityColumnDefinitionEntry, } from "../entity/entity-column.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAcA;;GAEG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,YAAY,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAChF,YAAY,EACV,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, CodeMatters. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
5
|
+
* in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
10
|
+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
11
|
+
* or implied. See the License for the specific language governing permissions and limitations under
|
|
12
|
+
* the License.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Generated-code-only Entity column declaration helpers.
|
|
16
|
+
*/
|
|
17
|
+
export { GeneratedEntityColumns } from "../entity/entity-column.js";
|
|
18
|
+
export { GeneratedEntityQueries } from "../query/generated-entity-query.js";
|
|
19
|
+
export { EntityQueryDescription } from "../query/entity-query.js";
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;GAEG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { type DescField, type Message, type MessageShape } from "@bufbuild/protobuf";
|
|
2
|
+
import type { GenMessage } from "@bufbuild/protobuf/codegenv2";
|
|
3
|
+
import { type Version } from "@spine-event-engine/proto";
|
|
4
|
+
/**
|
|
5
|
+
* Operators available for every Entity column.
|
|
6
|
+
*/
|
|
7
|
+
export type EntityEqualityOperator = "equal";
|
|
8
|
+
/**
|
|
9
|
+
* Operators available for naturally ordered Entity column values.
|
|
10
|
+
*/
|
|
11
|
+
export type EntityOrderingOperator = EntityEqualityOperator | "greaterThan" | "lessThan" | "greaterOrEqual" | "lessOrEqual";
|
|
12
|
+
/**
|
|
13
|
+
* Comparison family derived from a column's Protobuf field descriptor.
|
|
14
|
+
*/
|
|
15
|
+
export type EntityComparison = "equality" | "ordering";
|
|
16
|
+
/**
|
|
17
|
+
* Runtime value category derived from a column's Protobuf field descriptor.
|
|
18
|
+
*/
|
|
19
|
+
export type EntityColumnValueKind = "bigint" | "boolean" | "bytes" | "enum" | "message" | "number" | "string";
|
|
20
|
+
/**
|
|
21
|
+
* Represents one generated column declaration paired with its descriptor.
|
|
22
|
+
*/
|
|
23
|
+
export interface EntityColumnDefinitionEntry<Comparison extends EntityComparison = EntityComparison> {
|
|
24
|
+
/**
|
|
25
|
+
* Identifies the generated Protobuf field declared as a column.
|
|
26
|
+
*/
|
|
27
|
+
readonly field: DescField;
|
|
28
|
+
/**
|
|
29
|
+
* Identifies the comparison family supported by the field.
|
|
30
|
+
*/
|
|
31
|
+
readonly comparison: Comparison;
|
|
32
|
+
}
|
|
33
|
+
type StateFieldName<Schema extends GenMessage<Message>> = Exclude<keyof MessageShape<Schema>, "$typeName" | "$unknown"> & string;
|
|
34
|
+
type CollectionFieldName<Schema extends GenMessage<Message>> = {
|
|
35
|
+
[Name in StateFieldName<Schema>]: NonNullable<MessageShape<Schema>[Name]> extends readonly unknown[] ? Name : NonNullable<MessageShape<Schema>[Name]> extends object ? string extends keyof NonNullable<MessageShape<Schema>[Name]> ? Name : never : never;
|
|
36
|
+
}[StateFieldName<Schema>];
|
|
37
|
+
type SupportedStateFieldName<Schema extends GenMessage<Message>> = Exclude<StateFieldName<Schema> & keyof Schema["field"], CollectionFieldName<Schema>>;
|
|
38
|
+
type EntityColumnEntries = Readonly<Record<string, EntityColumnDefinitionEntry>>;
|
|
39
|
+
type SupportedEntryConstraint<Schema extends GenMessage<Message>, Entries extends EntityColumnEntries> = Exclude<keyof Entries, SupportedStateFieldName<Schema>> extends never ? unknown : never;
|
|
40
|
+
/**
|
|
41
|
+
* Brands generated column definitions for nominal type safety.
|
|
42
|
+
*/
|
|
43
|
+
declare const generatedDefinitionBrand: unique symbol;
|
|
44
|
+
/**
|
|
45
|
+
* Describes descriptor-backed column metadata emitted next to an Entity schema.
|
|
46
|
+
*
|
|
47
|
+
* The package root exports this type, but not its value constructor. Application
|
|
48
|
+
* code therefore consumes generated metadata instead of authoring string keys.
|
|
49
|
+
*/
|
|
50
|
+
export interface EntityColumnDefinition<Schema extends GenMessage<Message>, Entries extends EntityColumnEntries> {
|
|
51
|
+
/**
|
|
52
|
+
* Brands metadata with its owning schema and generated entries.
|
|
53
|
+
*/
|
|
54
|
+
readonly [generatedDefinitionBrand]: readonly [Schema, Entries];
|
|
55
|
+
/**
|
|
56
|
+
* Stores exact generated entries, validated again during registration.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
readonly entries: Entries;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Creates immutable Entity-column metadata emitted by the companion generator.
|
|
63
|
+
*/
|
|
64
|
+
export declare const GeneratedEntityColumns: Readonly<{
|
|
65
|
+
/**
|
|
66
|
+
* Creates immutable metadata for one generated Entity schema.
|
|
67
|
+
*
|
|
68
|
+
* @param schema Generated Protobuf schema that owns the declared fields.
|
|
69
|
+
* @param entries Generated field descriptors and their comparison families.
|
|
70
|
+
* @returns Metadata accepted by {@link EntityColumn.register}.
|
|
71
|
+
*/
|
|
72
|
+
define<Schema extends GenMessage<Message>, const Entries extends EntityColumnEntries>(schema: Schema, entries: Entries & SupportedEntryConstraint<NoInfer<Schema>, Entries>): EntityColumnDefinition<Schema, Entries>;
|
|
73
|
+
}>;
|
|
74
|
+
type OperatorsFor<Entry> = Entry extends EntityColumnDefinitionEntry<"ordering"> ? EntityOrderingOperator : EntityEqualityOperator;
|
|
75
|
+
/**
|
|
76
|
+
* Represents the typed column collection returned for one generated Entity definition.
|
|
77
|
+
*/
|
|
78
|
+
export type EntityColumns<Schema extends GenMessage<Message>, Entries extends EntityColumnEntries> = Readonly<{
|
|
79
|
+
[Name in keyof Entries & StateFieldName<Schema>]: EntityColumn<Schema, Name, MessageShape<Schema>[Name], OperatorsFor<Entries[Name]>>;
|
|
80
|
+
} & {
|
|
81
|
+
/**
|
|
82
|
+
* Identifies the Entity version system column.
|
|
83
|
+
*/
|
|
84
|
+
readonly version: EntityColumn<Schema, "version", Version>;
|
|
85
|
+
/**
|
|
86
|
+
* Identifies the Entity archived system column.
|
|
87
|
+
*/
|
|
88
|
+
readonly archived: EntityColumn<Schema, "archived", boolean, EntityEqualityOperator>;
|
|
89
|
+
/**
|
|
90
|
+
* Identifies the Entity deleted system column.
|
|
91
|
+
*/
|
|
92
|
+
readonly deleted: EntityColumn<Schema, "deleted", boolean, EntityEqualityOperator>;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Extracts the value type carried by an Entity column.
|
|
96
|
+
*/
|
|
97
|
+
export type EntityColumnValue<Column extends EntityColumn> = Column extends EntityColumn<GenMessage<Message>, string, infer Value> ? Value : never;
|
|
98
|
+
/**
|
|
99
|
+
* Extracts the legal operator union carried by an Entity column.
|
|
100
|
+
*/
|
|
101
|
+
export type EntityColumnOperator<Column extends EntityColumn> = Column extends EntityColumn<GenMessage<Message>, string, unknown, infer Operator> ? Operator : never;
|
|
102
|
+
/**
|
|
103
|
+
* An immutable, nominal, descriptor-backed Entity column.
|
|
104
|
+
*
|
|
105
|
+
* Instances can only be obtained by registering generated metadata for a
|
|
106
|
+
* Entity schema. This prevents consumers from constructing arbitrary
|
|
107
|
+
* string columns that have no corresponding Protobuf declaration.
|
|
108
|
+
*/
|
|
109
|
+
export declare class EntityColumn<Schema extends GenMessage<Message> = GenMessage<Message>, Name extends string = string, Value = unknown, Operator extends EntityOrderingOperator = EntityOrderingOperator> {
|
|
110
|
+
private readonly entityColumnBrand;
|
|
111
|
+
/**
|
|
112
|
+
* Generated Protobuf schema that owns this column.
|
|
113
|
+
*/
|
|
114
|
+
readonly schema: Schema;
|
|
115
|
+
/**
|
|
116
|
+
* Protobuf field name, or the canonical system-column name.
|
|
117
|
+
*/
|
|
118
|
+
readonly name: string;
|
|
119
|
+
/**
|
|
120
|
+
* Protobuf-ES property name, or the canonical system-column name.
|
|
121
|
+
*/
|
|
122
|
+
readonly localName: Name;
|
|
123
|
+
/**
|
|
124
|
+
* Whether the column came from the state descriptor or Entity storage metadata.
|
|
125
|
+
*/
|
|
126
|
+
readonly source: "declared" | "system";
|
|
127
|
+
/**
|
|
128
|
+
* Exact declared field descriptor; system columns have no state field.
|
|
129
|
+
*/
|
|
130
|
+
readonly descriptor: DescField | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* Runtime value category derived from the descriptor.
|
|
133
|
+
*/
|
|
134
|
+
readonly valueKind: EntityColumnValueKind;
|
|
135
|
+
/**
|
|
136
|
+
* Fully qualified message type for message-valued columns.
|
|
137
|
+
*/
|
|
138
|
+
readonly messageType: string | undefined;
|
|
139
|
+
/**
|
|
140
|
+
* Supported comparison family.
|
|
141
|
+
*/
|
|
142
|
+
readonly comparison: EntityComparison;
|
|
143
|
+
/**
|
|
144
|
+
* Exact operator set accepted for this column.
|
|
145
|
+
*/
|
|
146
|
+
readonly operators: readonly Operator[];
|
|
147
|
+
private constructor();
|
|
148
|
+
/**
|
|
149
|
+
* Registers generated Entity-column metadata for a schema.
|
|
150
|
+
*
|
|
151
|
+
* @param schema Generated Entity schema that owns the metadata.
|
|
152
|
+
* @param definition Immutable generated field metadata.
|
|
153
|
+
* @returns Stable immutable columns for the Entity schema.
|
|
154
|
+
*/
|
|
155
|
+
static register<Schema extends GenMessage<Message>, const Entries extends EntityColumnEntries>(schema: Schema, definition: EntityColumnDefinition<Schema, Entries>): EntityColumns<Schema, Entries>;
|
|
156
|
+
}
|
|
157
|
+
export {};
|
|
158
|
+
//# sourceMappingURL=entity-column.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-column.d.ts","sourceRoot":"","sources":["../../src/entity/entity-column.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, CodeMatters. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
5
|
+
* in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
10
|
+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
11
|
+
* or implied. See the License for the specific language governing permissions and limitations under
|
|
12
|
+
* the License.
|
|
13
|
+
*/
|
|
14
|
+
import { getOption, hasOption, } from "@bufbuild/protobuf";
|
|
15
|
+
import { column, entity, EntityOption_Kind } from "@spine-event-engine/proto";
|
|
16
|
+
import { EntityFieldClassification } from "../query/entity-field-classification.js";
|
|
17
|
+
/**
|
|
18
|
+
* Creates immutable Entity-column metadata emitted by the companion generator.
|
|
19
|
+
*/
|
|
20
|
+
export const GeneratedEntityColumns = Object.freeze({
|
|
21
|
+
define(schema, entries) {
|
|
22
|
+
const captured = EntityColumnFacts.captureDefinition(schema, entries);
|
|
23
|
+
const copiedEntries = Object.fromEntries(Object.entries(entries).map(([name, entry]) => {
|
|
24
|
+
EntityColumnFacts.deepFreeze(entry.field);
|
|
25
|
+
return [name, Object.freeze({ field: entry.field, comparison: entry.comparison })];
|
|
26
|
+
}));
|
|
27
|
+
const definition = Object.freeze({
|
|
28
|
+
entries: Object.freeze(copiedEntries),
|
|
29
|
+
});
|
|
30
|
+
capturedDefinitions.set(definition, captured);
|
|
31
|
+
return definition;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const equalityOperators = Object.freeze(["equal"]);
|
|
35
|
+
const orderingOperators = Object.freeze([
|
|
36
|
+
"equal",
|
|
37
|
+
"greaterThan",
|
|
38
|
+
"lessThan",
|
|
39
|
+
"greaterOrEqual",
|
|
40
|
+
"lessOrEqual",
|
|
41
|
+
]);
|
|
42
|
+
const systemNames = new Set(["version", "archived", "deleted"]);
|
|
43
|
+
const cache = new WeakMap();
|
|
44
|
+
const entityColumnConstructionToken = Symbol("EntityColumnConstructionToken");
|
|
45
|
+
const capturedDefinitions = new WeakMap();
|
|
46
|
+
/**
|
|
47
|
+
* An immutable, nominal, descriptor-backed Entity column.
|
|
48
|
+
*
|
|
49
|
+
* Instances can only be obtained by registering generated metadata for a
|
|
50
|
+
* Entity schema. This prevents consumers from constructing arbitrary
|
|
51
|
+
* string columns that have no corresponding Protobuf declaration.
|
|
52
|
+
*/
|
|
53
|
+
export class EntityColumn {
|
|
54
|
+
/**
|
|
55
|
+
* Generated Protobuf schema that owns this column.
|
|
56
|
+
*/
|
|
57
|
+
schema;
|
|
58
|
+
/**
|
|
59
|
+
* Protobuf field name, or the canonical system-column name.
|
|
60
|
+
*/
|
|
61
|
+
name;
|
|
62
|
+
/**
|
|
63
|
+
* Protobuf-ES property name, or the canonical system-column name.
|
|
64
|
+
*/
|
|
65
|
+
localName;
|
|
66
|
+
/**
|
|
67
|
+
* Whether the column came from the state descriptor or Entity storage metadata.
|
|
68
|
+
*/
|
|
69
|
+
source;
|
|
70
|
+
/**
|
|
71
|
+
* Exact declared field descriptor; system columns have no state field.
|
|
72
|
+
*/
|
|
73
|
+
descriptor;
|
|
74
|
+
/**
|
|
75
|
+
* Runtime value category derived from the descriptor.
|
|
76
|
+
*/
|
|
77
|
+
valueKind;
|
|
78
|
+
/**
|
|
79
|
+
* Fully qualified message type for message-valued columns.
|
|
80
|
+
*/
|
|
81
|
+
messageType;
|
|
82
|
+
/**
|
|
83
|
+
* Supported comparison family.
|
|
84
|
+
*/
|
|
85
|
+
comparison;
|
|
86
|
+
/**
|
|
87
|
+
* Exact operator set accepted for this column.
|
|
88
|
+
*/
|
|
89
|
+
operators;
|
|
90
|
+
constructor(input, token) {
|
|
91
|
+
if (token !== entityColumnConstructionToken) {
|
|
92
|
+
throw new TypeError("Entity columns can only be constructed during registration.");
|
|
93
|
+
}
|
|
94
|
+
this.schema = input.schema;
|
|
95
|
+
this.name = input.name;
|
|
96
|
+
this.localName = input.localName;
|
|
97
|
+
this.source = input.source;
|
|
98
|
+
this.descriptor = input.descriptor;
|
|
99
|
+
this.valueKind = input.valueKind;
|
|
100
|
+
this.messageType = input.messageType;
|
|
101
|
+
this.comparison = input.comparison;
|
|
102
|
+
this.operators = input.operators;
|
|
103
|
+
Object.freeze(this);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Registers generated Entity-column metadata for a schema.
|
|
107
|
+
*
|
|
108
|
+
* @param schema Generated Entity schema that owns the metadata.
|
|
109
|
+
* @param definition Immutable generated field metadata.
|
|
110
|
+
* @returns Stable immutable columns for the Entity schema.
|
|
111
|
+
*/
|
|
112
|
+
static register(schema, definition) {
|
|
113
|
+
const existing = cache.get(schema);
|
|
114
|
+
if (existing?.definitions.has(definition) === true) {
|
|
115
|
+
return existing.columns;
|
|
116
|
+
}
|
|
117
|
+
const captured = capturedDefinitions.get(definition);
|
|
118
|
+
EntityColumnFacts.validateSchema(schema, captured);
|
|
119
|
+
const declared = EntityColumnFacts.validateDefinition(schema, definition, captured);
|
|
120
|
+
if (existing !== undefined) {
|
|
121
|
+
existing.definitions.add(definition);
|
|
122
|
+
return existing.columns;
|
|
123
|
+
}
|
|
124
|
+
const result = {};
|
|
125
|
+
for (const [localName, field, fieldName, metadata] of declared) {
|
|
126
|
+
result[localName] = new EntityColumn({
|
|
127
|
+
schema,
|
|
128
|
+
name: fieldName,
|
|
129
|
+
localName,
|
|
130
|
+
source: "declared",
|
|
131
|
+
descriptor: field,
|
|
132
|
+
valueKind: metadata.valueKind,
|
|
133
|
+
messageType: metadata.messageType,
|
|
134
|
+
comparison: metadata.comparison,
|
|
135
|
+
operators: EntityColumnFacts.operatorsFor(metadata.comparison),
|
|
136
|
+
}, entityColumnConstructionToken);
|
|
137
|
+
}
|
|
138
|
+
result.version = new EntityColumn({
|
|
139
|
+
schema,
|
|
140
|
+
name: "version",
|
|
141
|
+
localName: "version",
|
|
142
|
+
source: "system",
|
|
143
|
+
valueKind: "message",
|
|
144
|
+
messageType: "spine.core.Version",
|
|
145
|
+
comparison: "ordering",
|
|
146
|
+
operators: orderingOperators,
|
|
147
|
+
}, entityColumnConstructionToken);
|
|
148
|
+
for (const name of ["archived", "deleted"]) {
|
|
149
|
+
result[name] = new EntityColumn({
|
|
150
|
+
schema,
|
|
151
|
+
name,
|
|
152
|
+
localName: name,
|
|
153
|
+
source: "system",
|
|
154
|
+
valueKind: "boolean",
|
|
155
|
+
comparison: "equality",
|
|
156
|
+
operators: equalityOperators,
|
|
157
|
+
}, entityColumnConstructionToken);
|
|
158
|
+
}
|
|
159
|
+
const columns = Object.freeze(result);
|
|
160
|
+
cache.set(schema, {
|
|
161
|
+
definitions: new WeakSet([definition]),
|
|
162
|
+
columns,
|
|
163
|
+
});
|
|
164
|
+
return columns;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Internal descriptor facts used while generated column metadata is registered.
|
|
169
|
+
*/
|
|
170
|
+
const EntityColumnFacts = Object.freeze({
|
|
171
|
+
// prettier-ignore
|
|
172
|
+
/**
|
|
173
|
+
* Validates that a schema declares one supported Entity kind.
|
|
174
|
+
*/
|
|
175
|
+
validateSchema(schema, captured) {
|
|
176
|
+
const entityKind = captured?.schema === schema
|
|
177
|
+
? captured.entityKind
|
|
178
|
+
: hasOption(schema, entity)
|
|
179
|
+
? getOption(schema, entity).kind
|
|
180
|
+
: undefined;
|
|
181
|
+
if (entityKind !== EntityOption_Kind.AGGREGATE &&
|
|
182
|
+
entityKind !== EntityOption_Kind.PROJECTION &&
|
|
183
|
+
entityKind !== EntityOption_Kind.PROCESS_MANAGER) {
|
|
184
|
+
throw new TypeError(`Entity column schema "${schema.typeName}" must declare Entity kind.`);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
/**
|
|
188
|
+
* Validates and describes every declared generated column.
|
|
189
|
+
*/
|
|
190
|
+
validateDefinition(schema, definition, captured) {
|
|
191
|
+
const facts = captured?.schema === schema
|
|
192
|
+
? captured
|
|
193
|
+
: EntityColumnFacts.captureDefinition(schema, definition.entries);
|
|
194
|
+
const annotated = new Map(facts.annotated);
|
|
195
|
+
const result = [];
|
|
196
|
+
for (const [localName, entry] of Object.entries(definition.entries)) {
|
|
197
|
+
if (systemNames.has(localName)) {
|
|
198
|
+
throw new TypeError(`Entity column definition cannot replace system column "${localName}".`);
|
|
199
|
+
}
|
|
200
|
+
if (entry === undefined)
|
|
201
|
+
continue;
|
|
202
|
+
const fieldFacts = facts.entries.get(localName) ?? EntityColumnFacts.captureField(entry.field);
|
|
203
|
+
if (fieldFacts.parent !== schema || fieldFacts.localName !== localName) {
|
|
204
|
+
throw new TypeError(`Entity column definition key "${localName}" must reference field "${localName}".`);
|
|
205
|
+
}
|
|
206
|
+
if (!fieldFacts.markedColumn) {
|
|
207
|
+
throw new TypeError(`Entity field "${localName}" is not marked (column).`);
|
|
208
|
+
}
|
|
209
|
+
const metadata = EntityColumnFacts.describeField(fieldFacts);
|
|
210
|
+
if (entry.comparison !== metadata.comparison) {
|
|
211
|
+
throw new TypeError(`Entity column "${localName}" requires ${metadata.comparison} comparison metadata.`);
|
|
212
|
+
}
|
|
213
|
+
annotated.delete(localName);
|
|
214
|
+
result.push([localName, fieldFacts.field, fieldFacts.name, metadata]);
|
|
215
|
+
}
|
|
216
|
+
const missing = annotated.keys().next().value;
|
|
217
|
+
if (missing !== undefined) {
|
|
218
|
+
throw new TypeError(`Entity column definition is missing annotated field "${missing}".`);
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
},
|
|
222
|
+
/**
|
|
223
|
+
* Derives runtime metadata from one validated descriptor.
|
|
224
|
+
*/
|
|
225
|
+
describeField(field) {
|
|
226
|
+
const metadata = field.classification;
|
|
227
|
+
if (!metadata.supported && metadata.reason === "singular") {
|
|
228
|
+
throw new TypeError(`Entity column "${field.localName}" must be singular; repeated and map fields are unsupported.`);
|
|
229
|
+
}
|
|
230
|
+
if (!metadata.supported) {
|
|
231
|
+
throw new TypeError(`Entity column "${field.localName}" cannot belong to a oneof.`);
|
|
232
|
+
}
|
|
233
|
+
return metadata;
|
|
234
|
+
},
|
|
235
|
+
/**
|
|
236
|
+
* Captures immutable facts from one generated definition.
|
|
237
|
+
*/
|
|
238
|
+
captureDefinition(schema, entries) {
|
|
239
|
+
const annotated = new Map(schema.fields
|
|
240
|
+
.filter((field) => hasOption(field, column) && getOption(field, column))
|
|
241
|
+
.map((field) => [field.localName, field]));
|
|
242
|
+
const capturedEntries = new Map();
|
|
243
|
+
for (const [localName, entry] of Object.entries(entries)) {
|
|
244
|
+
capturedEntries.set(localName, EntityColumnFacts.captureField(entry.field));
|
|
245
|
+
}
|
|
246
|
+
return Object.freeze({
|
|
247
|
+
schema,
|
|
248
|
+
entityKind: hasOption(schema, entity) ? getOption(schema, entity).kind : undefined,
|
|
249
|
+
annotated,
|
|
250
|
+
entries: capturedEntries,
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
/**
|
|
254
|
+
* Captures immutable facts from one generated field descriptor.
|
|
255
|
+
*/
|
|
256
|
+
captureField(field) {
|
|
257
|
+
return Object.freeze({
|
|
258
|
+
field,
|
|
259
|
+
parent: field.parent,
|
|
260
|
+
localName: field.localName,
|
|
261
|
+
name: field.name,
|
|
262
|
+
markedColumn: hasOption(field, column) && getOption(field, column),
|
|
263
|
+
classification: Object.freeze(EntityFieldClassification.classify(field)),
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
/**
|
|
267
|
+
* Deeply freezes a descriptor graph without freezing typed-array views.
|
|
268
|
+
*/
|
|
269
|
+
deepFreeze(root) {
|
|
270
|
+
const pending = [root];
|
|
271
|
+
const visited = new WeakSet();
|
|
272
|
+
while (pending.length > 0) {
|
|
273
|
+
const current = pending.pop();
|
|
274
|
+
if (current === undefined || visited.has(current))
|
|
275
|
+
continue;
|
|
276
|
+
visited.add(current);
|
|
277
|
+
if (ArrayBuffer.isView(current))
|
|
278
|
+
continue;
|
|
279
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
280
|
+
const property = Object.getOwnPropertyDescriptor(current, key);
|
|
281
|
+
if (property !== undefined &&
|
|
282
|
+
"value" in property &&
|
|
283
|
+
EntityColumnFacts.isObject(property.value)) {
|
|
284
|
+
pending.push(property.value);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
Object.freeze(current);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
/**
|
|
291
|
+
* Checks whether a value can participate in a descriptor graph.
|
|
292
|
+
*/
|
|
293
|
+
isObject(value) {
|
|
294
|
+
return (typeof value === "object" && value !== null) || typeof value === "function";
|
|
295
|
+
},
|
|
296
|
+
/**
|
|
297
|
+
* Returns the supported operator set for a comparison family.
|
|
298
|
+
*/
|
|
299
|
+
operatorsFor(comparison) {
|
|
300
|
+
return comparison === "ordering" ? orderingOperators : equalityOperators;
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
//# sourceMappingURL=entity-column.js.map
|