@spine-event-engine/storage-mysql 2.0.0-snapshot.23 → 2.0.0-snapshot.25
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 +38 -30
- package/REFERENCE.md +68 -59
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mysql/agent-history.d.ts +47 -8
- package/dist/mysql/agent-history.d.ts.map +1 -1
- package/dist/mysql/agent-history.js +85 -19
- package/dist/mysql/agent-history.js.map +1 -1
- package/dist/mysql/agent-transaction.d.ts +56 -0
- package/dist/mysql/agent-transaction.d.ts.map +1 -0
- package/dist/mysql/{entity-commit.js → agent-transaction.js} +33 -45
- package/dist/mysql/agent-transaction.js.map +1 -0
- package/dist/mysql/column-mapping.d.ts +1 -0
- package/dist/mysql/column-mapping.d.ts.map +1 -1
- package/dist/mysql/column-mapping.js +26 -3
- package/dist/mysql/column-mapping.js.map +1 -1
- package/dist/mysql/entity-history.d.ts +61 -35
- package/dist/mysql/entity-history.d.ts.map +1 -1
- package/dist/mysql/entity-history.js +469 -109
- package/dist/mysql/entity-history.js.map +1 -1
- package/dist/mysql/errors.d.ts +3 -2
- package/dist/mysql/errors.d.ts.map +1 -1
- package/dist/mysql/errors.js +3 -2
- package/dist/mysql/errors.js.map +1 -1
- package/dist/mysql/id-column.d.ts +7 -0
- package/dist/mysql/id-column.d.ts.map +1 -1
- package/dist/mysql/id-column.js +27 -3
- package/dist/mysql/id-column.js.map +1 -1
- package/dist/mysql/record-storage.d.ts +242 -72
- package/dist/mysql/record-storage.d.ts.map +1 -1
- package/dist/mysql/record-storage.js +509 -291
- package/dist/mysql/record-storage.js.map +1 -1
- package/dist/mysql/storage-factory.d.ts +286 -36
- package/dist/mysql/storage-factory.d.ts.map +1 -1
- package/dist/mysql/storage-factory.js +1294 -217
- package/dist/mysql/storage-factory.js.map +1 -1
- package/dist/mysql/table-resolver.d.ts +32 -1
- package/dist/mysql/table-resolver.d.ts.map +1 -1
- package/dist/mysql/table-resolver.js +81 -23
- package/dist/mysql/table-resolver.js.map +1 -1
- package/dist/mysql/table-spec.d.ts +0 -4
- package/dist/mysql/table-spec.d.ts.map +1 -1
- package/dist/mysql/table-spec.js +1 -33
- package/dist/mysql/table-spec.js.map +1 -1
- package/dist/mysql/testing.d.ts +8 -0
- package/dist/mysql/testing.d.ts.map +1 -1
- package/dist/mysql/testing.js +23 -2
- package/dist/mysql/testing.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/dist/mysql/agent-execution.d.ts +0 -278
- package/dist/mysql/agent-execution.d.ts.map +0 -1
- package/dist/mysql/agent-execution.js +0 -638
- package/dist/mysql/agent-execution.js.map +0 -1
- package/dist/mysql/delivery-cleanup.d.ts +0 -37
- package/dist/mysql/delivery-cleanup.d.ts.map +0 -1
- package/dist/mysql/delivery-cleanup.js +0 -108
- package/dist/mysql/delivery-cleanup.js.map +0 -1
- package/dist/mysql/entity-commit.d.ts +0 -79
- package/dist/mysql/entity-commit.d.ts.map +0 -1
- package/dist/mysql/entity-commit.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# MySQL storage for Spine TS
|
|
2
2
|
|
|
3
|
+
See [storage layout and database preparation](https://github.com/SpineEventEngine/spine-ts/blob/master/docs/STORAGE_LAYOUT.md) for
|
|
4
|
+
physical structures, tenant placement, prerequisites, and customization.
|
|
5
|
+
|
|
3
6
|
`@spine-event-engine/storage-mysql` stores Spine TS records in MySQL. It is a
|
|
4
7
|
durable implementation of `@spine-event-engine/storage` and manages the mysql2
|
|
5
8
|
connection pool and its private tables. PostgreSQL is not supported by this
|
|
@@ -11,14 +14,15 @@ record schemas, and a reachable MySQL database.
|
|
|
11
14
|
For database requirements, query limits, lifecycle, and error details, see
|
|
12
15
|
[REFERENCE documentation for agents](REFERENCE.md).
|
|
13
16
|
|
|
14
|
-
For an application using Agents, this provider
|
|
15
|
-
|
|
17
|
+
For an application using Agents, this provider retains typed admission receipts,
|
|
18
|
+
Entity state, and recorded interactions across restarts, without resuming an
|
|
19
|
+
interrupted handler. Use InnoDB for Agent repositories;
|
|
16
20
|
see the [coding-agent reference](REFERENCE.md) for database requirements.
|
|
17
21
|
|
|
18
22
|
## 💡 Why use it?
|
|
19
23
|
|
|
20
24
|
- ✅ Stores Spine records durably in a MySQL database.
|
|
21
|
-
- ✅ Manages a bounded `mysql2` connection pool
|
|
25
|
+
- ✅ Manages a bounded `mysql2` connection pool and atomic single-record conditions.
|
|
22
26
|
- ✅ Supports declared-column filters, sorting, offsets, limits, and
|
|
23
27
|
continuations.
|
|
24
28
|
- ✅ Keeps adapter tables private behind the common `RecordStorage` API.
|
|
@@ -38,9 +42,12 @@ The snapshot tag can change before a stable release.
|
|
|
38
42
|
|
|
39
43
|
## 🔌 Create and close a MySQL factory
|
|
40
44
|
|
|
41
|
-
Provide a MySQL URL that includes a database name. Building the factory
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
Provide a MySQL URL that includes a database name. Building the factory
|
|
46
|
+
validates settings locally and opens no pool. `checkConnection()` optionally
|
|
47
|
+
probes the shared database with a read-only query. It reuses an existing
|
|
48
|
+
runtime pool or opens and releases a temporary pool. The first storage
|
|
49
|
+
operation binds its database and creates or verifies
|
|
50
|
+
its private table lazily.
|
|
44
51
|
|
|
45
52
|
<!-- docs-snippet-path: packages/storage-mysql/src/index.ts -->
|
|
46
53
|
|
|
@@ -74,7 +81,7 @@ try {
|
|
|
74
81
|
if (stored?.value !== "task-42") throw new Error("The record was not stored.");
|
|
75
82
|
} finally {
|
|
76
83
|
await records.close();
|
|
77
|
-
factory.close();
|
|
84
|
+
await factory.close();
|
|
78
85
|
}
|
|
79
86
|
```
|
|
80
87
|
|
|
@@ -83,8 +90,9 @@ require TLS and provide the issuing CA explicitly; do not enable certificate
|
|
|
83
90
|
verification without the CA material needed by your managed MySQL endpoint.
|
|
84
91
|
|
|
85
92
|
For a multitenant application, assign each complete generated `TenantId` to a
|
|
86
|
-
different database. The factory
|
|
87
|
-
|
|
93
|
+
different database. The factory resolves each tenant's database and opens
|
|
94
|
+
its pool on first use; concurrent first uses share the binding. It selects
|
|
95
|
+
that pool before opening a table or starting a transaction:
|
|
88
96
|
|
|
89
97
|
```ts
|
|
90
98
|
import { create } from "@bufbuild/protobuf";
|
|
@@ -95,6 +103,7 @@ const acme = create(TenantIdSchema, { kind: { case: "value", value: "acme" } });
|
|
|
95
103
|
const globex = create(TenantIdSchema, { kind: { case: "value", value: "globex" } });
|
|
96
104
|
|
|
97
105
|
const factory = await MysqlStorageFactory.newBuilder()
|
|
106
|
+
.setOptions({ url: "mysql://user:password@db/shared" })
|
|
98
107
|
.setTenantOptions([
|
|
99
108
|
{ tenantId: acme, options: { url: "mysql://user:password@db/acme" } },
|
|
100
109
|
{ tenantId: globex, options: { url: "mysql://user:password@db/globex" } },
|
|
@@ -102,6 +111,12 @@ const factory = await MysqlStorageFactory.newBuilder()
|
|
|
102
111
|
.build();
|
|
103
112
|
```
|
|
104
113
|
|
|
114
|
+
Alternatively, `setTenantResolver(async (tenantId) => options)` resolves each
|
|
115
|
+
tenant's destination on first use. Fixed tenant entries and a resolver cannot
|
|
116
|
+
be combined. Either mode requires shared `setOptions`; tenants must use
|
|
117
|
+
distinct databases. Shared and tenant settings may point to the same database
|
|
118
|
+
only when their physical table families do not collide.
|
|
119
|
+
|
|
105
120
|
Use a dedicated database account. The account must be able to create and
|
|
106
121
|
inspect the adapter tables and perform normal reads and writes. Do not commit
|
|
107
122
|
connection URLs or credentials.
|
|
@@ -118,13 +133,16 @@ pnpm typecheck:build
|
|
|
118
133
|
## ✨ Supported records and queries
|
|
119
134
|
|
|
120
135
|
The adapter performs record CRUD, payload-based compare-and-set, named-column
|
|
121
|
-
queries, sort order, offsets, limits, and keyset continuations.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
queries, sort order, offsets, limits, and keyset continuations. Batches and
|
|
137
|
+
Entity commits use independent ordered records on InnoDB, MyISAM, and Aria.
|
|
138
|
+
A failed operation can leave its completed prefix; an identical Entity retry
|
|
139
|
+
reuses prepared versions and Event IDs. It supports primitive and structured
|
|
125
140
|
storage IDs. Declared columns support strings, booleans, integral Protobuf
|
|
126
|
-
numbers, enums, bytes, messages, `Timestamp`, and `Version`.
|
|
127
|
-
|
|
141
|
+
numbers, enums, bytes, messages, `Timestamp`, and `Version`. The provider-only
|
|
142
|
+
live Agent commit is a separate InnoDB transaction: it conditionally checks
|
|
143
|
+
the initial Entity version and complete model preferences before committing
|
|
144
|
+
any changed state, final audit, and preferences together. Spine JVM JDBC does
|
|
145
|
+
not support `float` or `double` record columns, so this adapter rejects
|
|
128
146
|
them too.
|
|
129
147
|
|
|
130
148
|
Indexed `Timestamp` columns store epoch nanoseconds in signed 64-bit `BIGINT`.
|
|
@@ -228,21 +246,11 @@ TLS, and plan backups, monitoring, and migrations. Indexed `bigint` values must
|
|
|
228
246
|
fit the exact signed 64-bit MySQL range. PostgreSQL is provided separately by
|
|
229
247
|
[`@spine-event-engine/storage-postgres`](https://github.com/SpineEventEngine/spine-ts/blob/master/packages/storage-postgres/README.md).
|
|
230
248
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
--url 'mysql://user:password@127.0.0.1:3306/tenant_a' \
|
|
237
|
-
--url 'mysql://user:password@127.0.0.1:3306/tenant_b'
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
The command exits nonzero if a database is unreachable or contains the old
|
|
241
|
-
`_scope`, `_revision`, or compound scope key. Do not start the corrected
|
|
242
|
-
runtime until it passes. The factory repeats this check for every configured
|
|
243
|
-
database before it becomes usable. The runtime does not read both layouts or choose
|
|
244
|
-
between rows that old Bounded Context partitions would collapse onto the same
|
|
245
|
-
new `ID`; migrate those rows offline and stop on every conflict.
|
|
249
|
+
Factory construction validates configuration without connecting. Explicit preparation
|
|
250
|
+
or first use checks the selected record families; it does not scan unrelated
|
|
251
|
+
tables. Existing tables must satisfy the expected layout. See the
|
|
252
|
+
[storage layout reference](https://github.com/SpineEventEngine/spine-ts/blob/master/docs/STORAGE_LAYOUT.md#6-startup-sequence) for
|
|
253
|
+
creation and compatibility-check timing.
|
|
246
254
|
|
|
247
255
|
## 🔗 Learn more
|
|
248
256
|
|
package/REFERENCE.md
CHANGED
|
@@ -5,6 +5,7 @@ This reference is for coding agents configuring the supported MySQL storage adap
|
|
|
5
5
|
## Public entry point
|
|
6
6
|
|
|
7
7
|
Import `MysqlStorageFactory`, `MysqlStorageOptions`, `MysqlTenantStorageOptions`,
|
|
8
|
+
`MysqlTenantResolver`,
|
|
8
9
|
`MysqlEntityStorageHandle`, `MysqlStorageFactoryBuilder`,
|
|
9
10
|
`MysqlCreateOperation`, `CreateOperationFactory`, `MysqlTableSpec`,
|
|
10
11
|
`MysqlColumnSpec`, `MysqlStorageConfigurationError`,
|
|
@@ -15,12 +16,27 @@ Import `MysqlStorageFactory`, `MysqlStorageOptions`, `MysqlTenantStorageOptions`
|
|
|
15
16
|
## Connection and schema
|
|
16
17
|
|
|
17
18
|
`MysqlStorageFactory.newBuilder().setOptions(options).build()` configures one
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
shared database without connecting. `setTenantOptions(entries)` configures
|
|
20
|
+
distinct databases for complete generated `TenantId` values;
|
|
21
|
+
`setTenantResolver(resolver)` selects them asynchronously on first use. Fixed
|
|
22
|
+
entries and a resolver are mutually exclusive. Shared options are required in
|
|
23
|
+
both modes. The first storage operation binds each database and its pool;
|
|
24
|
+
concurrent first uses share that binding, while a failed binding can retry.
|
|
25
|
+
`checkConnection()` probes only the shared database with `SELECT 1`, performs
|
|
26
|
+
no schema checks or DDL, and releases a temporary pool unless runtime work
|
|
27
|
+
already bound one. Successful checks are remembered; failed checks can retry.
|
|
28
|
+
The provider-only `StoragePreparations` port accepts declared Record, current
|
|
29
|
+
Entity and enabled history, or surviving Agent audit requirements. Its `check`
|
|
30
|
+
mode reads physical metadata without creating tables or indexes. Its `create`
|
|
31
|
+
mode creates missing structures and verifies their complete layout. Both modes
|
|
32
|
+
reuse a bound runtime pool or release temporary pools, and compatible results
|
|
33
|
+
are shared with later ordinary use. A failed attempt remains retryable.
|
|
34
|
+
The default shared registry converts message-valued IDs and ordinary message
|
|
35
|
+
columns using generated descriptors registered on module import.
|
|
36
|
+
`setStringifierRegistry(registry)` accepts a custom registry; the builder
|
|
37
|
+
snapshots it so later caller changes do not alter stored or queried values.
|
|
38
|
+
`setEventStoreTable(contextName, table, role?)` selects one physical Event Store
|
|
39
|
+
table for the exact bounded-context name and domain or system role.
|
|
24
40
|
`Identifiers` choose the storage ID representation; the configured
|
|
25
41
|
`StringifierRegistry` supplies the matching reversible representation for
|
|
26
42
|
message-valued IDs and declared message columns. `RecordQuery<I>` statically
|
|
@@ -28,7 +44,8 @@ types IDs only. Filter names and values are runtime inputs: MySQL checks the
|
|
|
28
44
|
name against the declared columns and maps each value through the corresponding
|
|
29
45
|
descriptor/column mapping, rather than accepting an application-constructed
|
|
30
46
|
provider string.
|
|
31
|
-
|
|
47
|
+
Without explicit preparation, per-family tables are created and verified lazily
|
|
48
|
+
on first use. Options support
|
|
32
49
|
`connectionLimit`, `connectTimeoutMs`, and TLS material. Failure to validate
|
|
33
50
|
configuration throws `MysqlStorageConfigurationError`; inaccessible connections
|
|
34
51
|
are reported as `MysqlStorageConnectionError`; an
|
|
@@ -45,34 +62,36 @@ one-byte binary collation, such as `latin1_bin`, and can store only IDs
|
|
|
45
62
|
representable in that character set. An `utf8mb4` ID of that declared width
|
|
46
63
|
cannot be converted to MyISAM.
|
|
47
64
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
upgraded application. Factory build repeats the inspection for every configured
|
|
53
|
-
database; the runtime has no dual-layout reader or automatic
|
|
54
|
-
conflict winner.
|
|
65
|
+
The optional `inventory:legacy` command remains available for operators
|
|
66
|
+
examining older layouts. Factory build and `checkConnection()` do not scan
|
|
67
|
+
unrelated tables or perform an upgrade-layout inventory. Requested record
|
|
68
|
+
families are checked when normal storage operations first use them.
|
|
55
69
|
|
|
56
70
|
## Lifecycle and tenancy
|
|
57
71
|
|
|
58
|
-
`close(): void
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
`close(): Promise<void>` immediately fences new public operations, waits for
|
|
73
|
+
complete admitted record, Entity, Agent, and commit work, then closes live
|
|
74
|
+
handles and drains pools. Await it to
|
|
75
|
+
observe cleanup failures; repeated calls return the same completion. A record
|
|
76
|
+
handle closes independently. Shared operations use the configured shared
|
|
77
|
+
destination. Creating a multitenant storage handle requires a complete
|
|
78
|
+
generated `TenantId` in its `StorageContext`; that captured tenant selects the
|
|
79
|
+
database for operations through the handle. Bounded Context names are
|
|
80
|
+
diagnostic only and never partition rows, tables, locks, or queries.
|
|
81
|
+
|
|
82
|
+
Each Entity source and history family has an independent table. Top-level
|
|
83
|
+
provider error messages and public responses omit connection URLs,
|
|
84
|
+
credentials, and driver details. The original driver failure remains available
|
|
85
|
+
as the internal `cause`; do not expose it in public responses or unfiltered
|
|
86
|
+
logs.
|
|
67
87
|
|
|
68
88
|
## Records and queries
|
|
69
89
|
|
|
70
|
-
CRUD operations use normal statements. Payload-based compare-and-set uses
|
|
71
|
-
|
|
72
|
-
`writeAll` encodes
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
identical retry. Repeated storage slots in a batch leave the last entry as the
|
|
90
|
+
CRUD operations use normal statements. Payload-based compare-and-set uses
|
|
91
|
+
atomic conditional statements for one storage slot on InnoDB, MyISAM, and Aria.
|
|
92
|
+
`writeAll` encodes input first and writes entries in input order; a later failure
|
|
93
|
+
leaves completed rows in place on every engine. Repeated storage slots in a
|
|
94
|
+
batch leave the last completed entry as the
|
|
76
95
|
stored value. IDs are declared as generated message IDs or the supported
|
|
77
96
|
primitive `string`, `int32`, and `int64` kinds. Primitive IDs use native MySQL
|
|
78
97
|
values; message IDs use their reversible stringifier, compact Proto JSON by
|
|
@@ -121,17 +140,18 @@ acquired.
|
|
|
121
140
|
## Entity history seam
|
|
122
141
|
|
|
123
142
|
The provider/framework Entity handle returns current records plus immutable
|
|
124
|
-
state and event history. It is not a remote application history API. State
|
|
125
|
-
|
|
143
|
+
state and event history. It is not a remote application history API. State and
|
|
144
|
+
diagnostic append use immutable per-record insertion. Trim and truncate use
|
|
126
145
|
fixed-size chunks. A failed chunk can leave earlier chunks durable, so callers
|
|
127
146
|
retry maintenance. Current-record and history actions are separate calls, not
|
|
128
147
|
one cross-storage transaction.
|
|
129
148
|
|
|
130
|
-
The internal
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
operations remain separate
|
|
149
|
+
The internal Entity commit port writes state history, diagnostic Events,
|
|
150
|
+
framework delivery Events, then current state as independent records. A failure
|
|
151
|
+
can leave a completed prefix on every engine. One classified deadlock retry
|
|
152
|
+
replays captured input with unchanged versions and Event IDs. Standalone
|
|
153
|
+
history operations remain separate; a delivery Event alone does not prove the
|
|
154
|
+
current-state write completed.
|
|
135
155
|
|
|
136
156
|
## Agent history
|
|
137
157
|
|
|
@@ -150,26 +170,15 @@ misordered, or otherwise incompatible same-name indexes reject when the handle
|
|
|
150
170
|
opens. Pages use bounded native SQL chunks and sum serialized entry bytes; a
|
|
151
171
|
fetched chunk can be larger than the returned byte budget.
|
|
152
172
|
|
|
153
|
-
## Agent
|
|
154
|
-
|
|
155
|
-
Agent
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
per-instance, and Agent history records must each fit the 65,535-byte BLOB limit. The runtime must
|
|
166
|
-
check its response reservation against the provider limits before inference.
|
|
167
|
-
Reopening a factory can read the saved work. No transaction remains open during
|
|
168
|
-
a model request.
|
|
169
|
-
|
|
170
|
-
`MysqlStorageSchemaError` reports incompatible layouts,
|
|
171
|
-
`MysqlStorageDataError` reports stored bytes that cannot be decoded, and
|
|
172
|
-
`MysqlStorageOperationError` reports sanitized bounds, query, collision, and
|
|
173
|
-
driver-operation failures. Custom creation uses `useOperationFactory()`; its
|
|
174
|
-
resolved `MysqlTableSpec` has the same native column types and naming precedence
|
|
175
|
-
as default DDL (explicit grouped name, explicit record name, then default).
|
|
173
|
+
## Agent live persistence
|
|
174
|
+
|
|
175
|
+
The live Agent path is exercised on MySQL and MariaDB. Agent repositories
|
|
176
|
+
require transactional InnoDB tables for atomic Agent commits. The handle
|
|
177
|
+
rejects a nontransactional engine before accepting Agent work; other Entity
|
|
178
|
+
families retain their configured engine behavior. `appendAll()` commits an
|
|
179
|
+
immutable Agent history group in one transaction. `commitAgent()` compares
|
|
180
|
+
the exact Entity image and model preferences, then writes accepted state,
|
|
181
|
+
preference, and history changes atomically. A no-op or preference-only turn
|
|
182
|
+
can leave the Entity Version unchanged. The separate typed receipt records
|
|
183
|
+
admission, not executable work for replay. Connection binding is scoped to an
|
|
184
|
+
asynchronous operation; no transaction stays open during a model request.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { MysqlStorageConfigurationError, MysqlStorageConnectionError, MysqlStorageDataError, MysqlStorageFactory, MysqlStorageOperationError, MysqlStorageSchemaError, type CreateOperationFactory, type MysqlColumnSpec, type MysqlCreateOperation, type MysqlStorageFactoryBuilder, type MysqlStorageOptions, type MysqlTenantStorageOptions, type MysqlTableSpec, } from "./mysql/storage-factory.js";
|
|
1
|
+
export { MysqlStorageConfigurationError, MysqlStorageConnectionError, MysqlStorageDataError, MysqlStorageFactory, MysqlStorageOperationError, MysqlStorageSchemaError, type CreateOperationFactory, type MysqlColumnSpec, type MysqlCreateOperation, type MysqlStorageFactoryBuilder, type MysqlStorageOptions, type MysqlTenantStorageOptions, type MysqlTenantResolver, type MysqlTableSpec, } from "./mysql/storage-factory.js";
|
|
2
2
|
export type { MysqlEntityStorageHandle } from "./mysql/entity-history.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,GACpB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,GASxB,MAAM,4BAA4B,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { AgentHistoryEntry } from "@spine-event-engine/proto/agent";
|
|
2
|
-
import type
|
|
3
|
-
import { type AgentHistoryPage, type AgentHistoryRead, type AgentHistoryStorage, type AgentHistoryStorageInput } from "@spine-event-engine/storage/provider";
|
|
4
|
-
import { MysqlRecordStorage } from "./record-storage.js";
|
|
2
|
+
import { type AgentHistoryRecord } from "@spine-event-engine/proto/generated/spine/server/agent/history_record_pb.js";
|
|
3
|
+
import { type AgentHistoryCapacity, type AgentHistoryPage, type AgentHistoryRead, type AgentHistoryStorage, type AgentHistoryStorageInput } from "@spine-event-engine/storage/provider";
|
|
4
|
+
import { MysqlRecordStorage, type OnPublicOperation } from "./record-storage.js";
|
|
5
|
+
import { MysqlAgentTransaction } from "./agent-transaction.js";
|
|
5
6
|
/**
|
|
6
7
|
* Hashes complete identities into bounded physical IDs and indexed scope keys.
|
|
7
8
|
*/
|
|
@@ -14,6 +15,10 @@ export declare const AgentHistoryHash: {
|
|
|
14
15
|
*/
|
|
15
16
|
value(value: string): string;
|
|
16
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Declares the complete native indexes required by MySQL Agent history.
|
|
20
|
+
*/
|
|
21
|
+
export declare const AgentHistoryIndexes: readonly [readonly ["agent_history_full", readonly ["scope_digest", "order_key"]], readonly ["agent_history_category", readonly ["scope_digest", "category", "order_key"]], readonly ["agent_history_conversation", readonly ["scope_digest", "conversation_digest", "order_key"]]];
|
|
17
22
|
/**
|
|
18
23
|
* Stores Agent history in the shared MySQL immutable record family.
|
|
19
24
|
*
|
|
@@ -23,13 +28,36 @@ export declare class MysqlAgentHistory<Id> implements AgentHistoryStorage<Id> {
|
|
|
23
28
|
#private;
|
|
24
29
|
private readonly input;
|
|
25
30
|
private readonly records;
|
|
31
|
+
private readonly onAdmit;
|
|
32
|
+
private readonly transaction?;
|
|
33
|
+
/**
|
|
34
|
+
* Maximum immutable Agent history record bytes supported by MySQL.
|
|
35
|
+
*/
|
|
36
|
+
readonly capacity: AgentHistoryCapacity;
|
|
26
37
|
/**
|
|
27
38
|
* Binds one typed Agent repository to the provider record handle.
|
|
28
39
|
*
|
|
29
40
|
* @param input Repository and complete tenant scope.
|
|
30
41
|
* @param records Native record-family storage.
|
|
42
|
+
* @param onAdmit Admits complete public history operations.
|
|
43
|
+
* @param transaction Coordinates native immutable audit groups.
|
|
44
|
+
*/
|
|
45
|
+
constructor(input: AgentHistoryStorageInput<Id>, records: MysqlRecordStorage<string, AgentHistoryRecord>, onAdmit?: OnPublicOperation, transaction?: MysqlAgentTransaction | undefined);
|
|
46
|
+
/**
|
|
47
|
+
* Writes atomically writes captured immutable rows in the selected tenant database.
|
|
48
|
+
* @param entityId Typed Agent identifier.
|
|
49
|
+
* @param entries Complete Agent audit group.
|
|
50
|
+
* @returns Completion after the native transaction commits.
|
|
51
|
+
*/
|
|
52
|
+
appendAll(entityId: Id, entries: readonly AgentHistoryEntry[]): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Performs one admitted Agent audit group on a native connection.
|
|
55
|
+
*
|
|
56
|
+
* @param entityId Typed Entity identifier.
|
|
57
|
+
* @param entries Complete immutable history group.
|
|
58
|
+
* @returns Completion after the complete immutable audit group commits atomically.
|
|
31
59
|
*/
|
|
32
|
-
|
|
60
|
+
private appendAllAdmitted;
|
|
33
61
|
/**
|
|
34
62
|
* Writes one original entry or confirms byte-identical content.
|
|
35
63
|
*
|
|
@@ -38,6 +66,14 @@ export declare class MysqlAgentHistory<Id> implements AgentHistoryStorage<Id> {
|
|
|
38
66
|
* @returns Completion after immutable storage confirms the entry.
|
|
39
67
|
*/
|
|
40
68
|
append(entityId: Id, entry: AgentHistoryEntry): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Executes an admitted provider operation.
|
|
71
|
+
*
|
|
72
|
+
* @param entityId Typed Agent identifier.
|
|
73
|
+
* @param entry Original entry.
|
|
74
|
+
* @returns Completion after immutable storage.
|
|
75
|
+
*/
|
|
76
|
+
private appendAdmitted;
|
|
41
77
|
/**
|
|
42
78
|
* Reads a bounded indexed page in full Timestamp/category/UTF-8 order.
|
|
43
79
|
*
|
|
@@ -46,9 +82,14 @@ export declare class MysqlAgentHistory<Id> implements AgentHistoryStorage<Id> {
|
|
|
46
82
|
*/
|
|
47
83
|
read(request: AgentHistoryRead<Id>): Promise<AgentHistoryPage>;
|
|
48
84
|
/**
|
|
49
|
-
|
|
85
|
+
* Executes an admitted provider operation.
|
|
86
|
+
*
|
|
87
|
+
* @param request Typed history page request.
|
|
88
|
+
* @returns Original entries and continuation status.
|
|
89
|
+
*/
|
|
90
|
+
private readAdmitted;
|
|
91
|
+
/**
|
|
50
92
|
* Closes this handle without deleting stored records.
|
|
51
|
-
|
|
52
93
|
*/
|
|
53
94
|
close(): void;
|
|
54
95
|
/**
|
|
@@ -95,9 +136,7 @@ export declare class MysqlAgentHistory<Id> implements AgentHistoryStorage<Id> {
|
|
|
95
136
|
*/
|
|
96
137
|
private key;
|
|
97
138
|
/**
|
|
98
|
-
|
|
99
139
|
* Rejects calls after this handle closes.
|
|
100
|
-
|
|
101
140
|
*/
|
|
102
141
|
private requireOpen;
|
|
103
142
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-history.d.ts","sourceRoot":"","sources":["../../src/mysql/agent-history.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent-history.d.ts","sourceRoot":"","sources":["../../src/mysql/agent-history.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,6EAA6E,CAAC;AACrF,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAG/D;;GAEG;AACH,eAAO,MAAM,gBAAgB;IAC3B;;;;;OAKG;iBACU,MAAM,GAAG,MAAM;CAG7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,qRAItB,CAAC;AAEX;;;;GAIG;AACH,qBAAa,iBAAiB,CAAC,EAAE,CAAE,YAAW,mBAAmB,CAAC,EAAE,CAAC;;IAyBjE,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IA3B/B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAiD;IAYxF;;;;;;;OAOG;gBAEgB,KAAK,EAAE,wBAAwB,CAAC,EAAE,CAAC,EACnC,OAAO,EAAE,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACvD,OAAO,GAAE,iBAAoC,EAC7C,WAAW,CAAC,EAAE,qBAAqB,GAAG,SAAS;IAGlE;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;;;;;OAMG;YACW,iBAAiB;IA+B/B;;;;;;OAMG;IACG,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAInE;;;;;;OAMG;YACW,cAAc;IAY5B;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIpE;;;;;OAKG;YACW,YAAY;IAY1B;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAOb;;;;OAIG;YACW,UAAU;IAMxB;;;;;;;;OAQG;YACW,KAAK;IA4BnB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;OAKG;IACH,OAAO,CAAC,GAAG;IAMX;;OAEG;IACH,OAAO,CAAC,WAAW;CAGpB"}
|
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
* the License.
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
|
+
import { clone, toBinary } from "@bufbuild/protobuf";
|
|
16
|
+
import { AgentHistoryRecordSchema, } from "@spine-event-engine/proto/generated/spine/server/agent/history_record_pb.js";
|
|
15
17
|
import { AgentHistoryKeys, AgentHistoryPages, } from "@spine-event-engine/storage/provider";
|
|
16
18
|
import { AgentHistoryRecords } from "@spine-event-engine/storage/provider";
|
|
17
19
|
import { MysqlRecordStorage } from "./record-storage.js";
|
|
20
|
+
import { MysqlAgentTransaction } from "./agent-transaction.js";
|
|
18
21
|
import { AgentHistoryIndexBytes } from "./table-spec.js";
|
|
19
22
|
/**
|
|
20
23
|
* Hashes complete identities into bounded physical IDs and indexed scope keys.
|
|
@@ -30,6 +33,14 @@ export const AgentHistoryHash = {
|
|
|
30
33
|
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
31
34
|
},
|
|
32
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Declares the complete native indexes required by MySQL Agent history.
|
|
38
|
+
*/
|
|
39
|
+
export const AgentHistoryIndexes = [
|
|
40
|
+
["agent_history_full", ["scope_digest", "order_key"]],
|
|
41
|
+
["agent_history_category", ["scope_digest", "category", "order_key"]],
|
|
42
|
+
["agent_history_conversation", ["scope_digest", "conversation_digest", "order_key"]],
|
|
43
|
+
];
|
|
33
44
|
/**
|
|
34
45
|
* Stores Agent history in the shared MySQL immutable record family.
|
|
35
46
|
*
|
|
@@ -38,14 +49,18 @@ export const AgentHistoryHash = {
|
|
|
38
49
|
export class MysqlAgentHistory {
|
|
39
50
|
input;
|
|
40
51
|
records;
|
|
52
|
+
onAdmit;
|
|
53
|
+
transaction;
|
|
54
|
+
/**
|
|
55
|
+
* Maximum immutable Agent history record bytes supported by MySQL.
|
|
56
|
+
*/
|
|
57
|
+
capacity = Object.freeze({ historyRecordBytes: 65_535 });
|
|
41
58
|
/**
|
|
42
59
|
* Records whether this handle accepts calls.
|
|
43
60
|
*/
|
|
44
61
|
#open = true;
|
|
45
62
|
/**
|
|
46
|
-
|
|
47
63
|
* Reuses one table and index preparation promise.
|
|
48
|
-
|
|
49
64
|
*/
|
|
50
65
|
#ready;
|
|
51
66
|
/**
|
|
@@ -53,10 +68,52 @@ export class MysqlAgentHistory {
|
|
|
53
68
|
*
|
|
54
69
|
* @param input Repository and complete tenant scope.
|
|
55
70
|
* @param records Native record-family storage.
|
|
71
|
+
* @param onAdmit Admits complete public history operations.
|
|
72
|
+
* @param transaction Coordinates native immutable audit groups.
|
|
56
73
|
*/
|
|
57
|
-
constructor(input, records) {
|
|
74
|
+
constructor(input, records, onAdmit = (work) => work(), transaction) {
|
|
58
75
|
this.input = input;
|
|
59
76
|
this.records = records;
|
|
77
|
+
this.onAdmit = onAdmit;
|
|
78
|
+
this.transaction = transaction;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Writes atomically writes captured immutable rows in the selected tenant database.
|
|
82
|
+
* @param entityId Typed Agent identifier.
|
|
83
|
+
* @param entries Complete Agent audit group.
|
|
84
|
+
* @returns Completion after the native transaction commits.
|
|
85
|
+
*/
|
|
86
|
+
appendAll(entityId, entries) {
|
|
87
|
+
return this.onAdmit(() => this.appendAllAdmitted(entityId, entries));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Performs one admitted Agent audit group on a native connection.
|
|
91
|
+
*
|
|
92
|
+
* @param entityId Typed Entity identifier.
|
|
93
|
+
* @param entries Complete immutable history group.
|
|
94
|
+
* @returns Completion after the complete immutable audit group commits atomically.
|
|
95
|
+
*/
|
|
96
|
+
async appendAllAdmitted(entityId, entries) {
|
|
97
|
+
this.requireOpen();
|
|
98
|
+
const key = this.key(entityId);
|
|
99
|
+
const rows = entries.map((entry) => {
|
|
100
|
+
if (AgentHistoryKeys.indexValue(AgentHistoryKeys.fromEntry(entry)).length >
|
|
101
|
+
AgentHistoryIndexBytes)
|
|
102
|
+
throw new RangeError("Agent history ordering key exceeds the MySQL index capacity.");
|
|
103
|
+
const row = clone(AgentHistoryRecordSchema, AgentHistoryRecords.record(this.input.stateType, key, entry));
|
|
104
|
+
if (toBinary(AgentHistoryRecordSchema, row).length > 65_535)
|
|
105
|
+
throw new RangeError("Agent history record exceeds the MySQL row capacity.");
|
|
106
|
+
return row;
|
|
107
|
+
});
|
|
108
|
+
if (rows.length === 0)
|
|
109
|
+
return;
|
|
110
|
+
if (this.transaction === undefined)
|
|
111
|
+
throw new Error("MySQL Agent history transaction is unavailable.");
|
|
112
|
+
await this.ready();
|
|
113
|
+
await this.transaction.commit([this.records.tableName], (connection) => this.records.withConnection(connection, async () => {
|
|
114
|
+
for (const row of rows)
|
|
115
|
+
await this.records.writeImmutable(row);
|
|
116
|
+
}));
|
|
60
117
|
}
|
|
61
118
|
/**
|
|
62
119
|
* Writes one original entry or confirms byte-identical content.
|
|
@@ -66,6 +123,16 @@ export class MysqlAgentHistory {
|
|
|
66
123
|
* @returns Completion after immutable storage confirms the entry.
|
|
67
124
|
*/
|
|
68
125
|
async append(entityId, entry) {
|
|
126
|
+
return this.onAdmit(() => this.appendAdmitted(entityId, entry));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Executes an admitted provider operation.
|
|
130
|
+
*
|
|
131
|
+
* @param entityId Typed Agent identifier.
|
|
132
|
+
* @param entry Original entry.
|
|
133
|
+
* @returns Completion after immutable storage.
|
|
134
|
+
*/
|
|
135
|
+
async appendAdmitted(entityId, entry) {
|
|
69
136
|
this.requireOpen();
|
|
70
137
|
if (AgentHistoryKeys.indexValue(AgentHistoryKeys.fromEntry(entry)).length > AgentHistoryIndexBytes)
|
|
71
138
|
throw new RangeError("Agent history ordering key exceeds the MySQL index capacity.");
|
|
@@ -79,6 +146,15 @@ export class MysqlAgentHistory {
|
|
|
79
146
|
* @returns Original entries and continuation status.
|
|
80
147
|
*/
|
|
81
148
|
async read(request) {
|
|
149
|
+
return this.onAdmit(() => this.readAdmitted(request));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Executes an admitted provider operation.
|
|
153
|
+
*
|
|
154
|
+
* @param request Typed history page request.
|
|
155
|
+
* @returns Original entries and continuation status.
|
|
156
|
+
*/
|
|
157
|
+
async readAdmitted(request) {
|
|
82
158
|
this.requireOpen();
|
|
83
159
|
await this.ready();
|
|
84
160
|
const agentKey = this.key(request.entityId);
|
|
@@ -87,9 +163,7 @@ export class MysqlAgentHistory {
|
|
|
87
163
|
return AgentHistoryPages.read(request, after, (cursor, limit) => this.fetch(agentKey, request, cursor, limit));
|
|
88
164
|
}
|
|
89
165
|
/**
|
|
90
|
-
|
|
91
166
|
* Closes this handle without deleting stored records.
|
|
92
|
-
|
|
93
167
|
*/
|
|
94
168
|
close() {
|
|
95
169
|
if (!this.#open)
|
|
@@ -103,7 +177,10 @@ export class MysqlAgentHistory {
|
|
|
103
177
|
* @returns Shared preparation promise for this handle.
|
|
104
178
|
*/
|
|
105
179
|
ready() {
|
|
106
|
-
return (this.#ready ??= this.initialize())
|
|
180
|
+
return (this.#ready ??= this.initialize().catch((error) => {
|
|
181
|
+
this.#ready = undefined;
|
|
182
|
+
throw error;
|
|
183
|
+
}));
|
|
107
184
|
}
|
|
108
185
|
/**
|
|
109
186
|
* Creates each native index idempotently on the shared record family.
|
|
@@ -112,17 +189,8 @@ export class MysqlAgentHistory {
|
|
|
112
189
|
*/
|
|
113
190
|
async initialize() {
|
|
114
191
|
await this.records.prepare();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"scope_digest",
|
|
118
|
-
"category",
|
|
119
|
-
"order_key",
|
|
120
|
-
]);
|
|
121
|
-
await this.records.ensureHistoryIndex("agent_history_conversation", [
|
|
122
|
-
"scope_digest",
|
|
123
|
-
"conversation_digest",
|
|
124
|
-
"order_key",
|
|
125
|
-
]);
|
|
192
|
+
for (const [name, columns] of AgentHistoryIndexes)
|
|
193
|
+
await this.records.ensureHistoryIndex(name, columns);
|
|
126
194
|
}
|
|
127
195
|
/**
|
|
128
196
|
* Reads a bounded native page with complete scope and view predicates.
|
|
@@ -200,9 +268,7 @@ export class MysqlAgentHistory {
|
|
|
200
268
|
return key;
|
|
201
269
|
}
|
|
202
270
|
/**
|
|
203
|
-
|
|
204
271
|
* Rejects calls after this handle closes.
|
|
205
|
-
|
|
206
272
|
*/
|
|
207
273
|
requireOpen() {
|
|
208
274
|
if (!this.#open)
|