@c9up/eon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/EonProvider.d.ts +51 -0
- package/dist/EonProvider.d.ts.map +1 -0
- package/dist/EonProvider.js +154 -0
- package/dist/EonProvider.js.map +1 -0
- package/dist/connection/EonConnection.d.ts +116 -0
- package/dist/connection/EonConnection.d.ts.map +1 -0
- package/dist/connection/EonConnection.js +15 -0
- package/dist/connection/EonConnection.js.map +1 -0
- package/dist/connection/config.d.ts +56 -0
- package/dist/connection/config.d.ts.map +1 -0
- package/dist/connection/config.js +14 -0
- package/dist/connection/config.js.map +1 -0
- package/dist/connection/websocket.d.ts +16 -0
- package/dist/connection/websocket.d.ts.map +1 -0
- package/dist/connection/websocket.js +383 -0
- package/dist/connection/websocket.js.map +1 -0
- package/dist/decorators/superTable.d.ts +73 -0
- package/dist/decorators/superTable.d.ts.map +1 -0
- package/dist/decorators/superTable.js +98 -0
- package/dist/decorators/superTable.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/schemaless.d.ts +22 -0
- package/dist/ingest/schemaless.d.ts.map +1 -0
- package/dist/ingest/schemaless.js +161 -0
- package/dist/ingest/schemaless.js.map +1 -0
- package/dist/ingest/sql.d.ts +23 -0
- package/dist/ingest/sql.d.ts.map +1 -0
- package/dist/ingest/sql.js +59 -0
- package/dist/ingest/sql.js.map +1 -0
- package/dist/ingest/stmt.d.ts +82 -0
- package/dist/ingest/stmt.d.ts.map +1 -0
- package/dist/ingest/stmt.js +246 -0
- package/dist/ingest/stmt.js.map +1 -0
- package/dist/metadata-keys.d.ts +17 -0
- package/dist/metadata-keys.d.ts.map +1 -0
- package/dist/metadata-keys.js +17 -0
- package/dist/metadata-keys.js.map +1 -0
- package/dist/query/TimeSeriesQuery.d.ts +121 -0
- package/dist/query/TimeSeriesQuery.d.ts.map +1 -0
- package/dist/query/TimeSeriesQuery.js +291 -0
- package/dist/query/TimeSeriesQuery.js.map +1 -0
- package/dist/query/native.d.ts +22 -0
- package/dist/query/native.d.ts.map +1 -0
- package/dist/query/native.js +135 -0
- package/dist/query/native.js.map +1 -0
- package/dist/repository/SuperTableRepository.d.ts +80 -0
- package/dist/repository/SuperTableRepository.d.ts.map +1 -0
- package/dist/repository/SuperTableRepository.js +232 -0
- package/dist/repository/SuperTableRepository.js.map +1 -0
- package/dist/schema/CreateStableSpec.d.ts +140 -0
- package/dist/schema/CreateStableSpec.d.ts.map +1 -0
- package/dist/schema/CreateStableSpec.js +41 -0
- package/dist/schema/CreateStableSpec.js.map +1 -0
- package/dist/schema/EonMigrationRunner.d.ts +71 -0
- package/dist/schema/EonMigrationRunner.d.ts.map +1 -0
- package/dist/schema/EonMigrationRunner.js +295 -0
- package/dist/schema/EonMigrationRunner.js.map +1 -0
- package/dist/schema/EonSchema.d.ts +128 -0
- package/dist/schema/EonSchema.d.ts.map +1 -0
- package/dist/schema/EonSchema.js +309 -0
- package/dist/schema/EonSchema.js.map +1 -0
- package/dist/schema/Migration.d.ts +47 -0
- package/dist/schema/Migration.d.ts.map +1 -0
- package/dist/schema/Migration.js +55 -0
- package/dist/schema/Migration.js.map +1 -0
- package/dist/schema/compile.d.ts +33 -0
- package/dist/schema/compile.d.ts.map +1 -0
- package/dist/schema/compile.js +70 -0
- package/dist/schema/compile.js.map +1 -0
- package/dist/schema/sync.d.ts +45 -0
- package/dist/schema/sync.d.ts.map +1 -0
- package/dist/schema/sync.js +102 -0
- package/dist/schema/sync.js.map +1 -0
- package/dist/services/connection.d.ts +33 -0
- package/dist/services/connection.d.ts.map +1 -0
- package/dist/services/connection.js +71 -0
- package/dist/services/connection.js.map +1 -0
- package/dist/testing/Factory.d.ts +43 -0
- package/dist/testing/Factory.d.ts.map +1 -0
- package/dist/testing/Factory.js +134 -0
- package/dist/testing/Factory.js.map +1 -0
- package/dist/testing/FakeEonConnection.d.ts +43 -0
- package/dist/testing/FakeEonConnection.d.ts.map +1 -0
- package/dist/testing/FakeEonConnection.js +315 -0
- package/dist/testing/FakeEonConnection.js.map +1 -0
- package/dist/testing/index.d.ts +34 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +67 -0
- package/dist/testing/index.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +65 -0
- package/scripts/copy-napi.mjs +72 -0
- package/src/EonProvider.ts +222 -0
- package/src/connection/EonConnection.ts +146 -0
- package/src/connection/config.ts +60 -0
- package/src/connection/websocket.ts +436 -0
- package/src/decorators/superTable.ts +169 -0
- package/src/index.ts +118 -0
- package/src/ingest/schemaless.ts +211 -0
- package/src/ingest/sql.ts +73 -0
- package/src/ingest/stmt.ts +335 -0
- package/src/metadata-keys.ts +20 -0
- package/src/query/TimeSeriesQuery.ts +480 -0
- package/src/query/native.ts +180 -0
- package/src/repository/SuperTableRepository.ts +347 -0
- package/src/schema/CreateStableSpec.ts +184 -0
- package/src/schema/EonMigrationRunner.ts +378 -0
- package/src/schema/EonSchema.ts +396 -0
- package/src/schema/Migration.ts +66 -0
- package/src/schema/compile.ts +109 -0
- package/src/schema/sync.ts +142 -0
- package/src/services/connection.ts +89 -0
- package/src/testing/Factory.ts +186 -0
- package/src/testing/FakeEonConnection.ts +351 -0
- package/src/testing/index.ts +81 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SuperTableRepository<TPoint>` — the Atlas-shaped ingest API for a TDengine
|
|
3
|
+
* super-table (story 58.4).
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `@c9up/atlas` `BaseRepository` naming: `ingest` (≙ `create`) and
|
|
6
|
+
* `ingestMany` (≙ `createMany`, the bulk primitive), plus two extra write paths
|
|
7
|
+
* TDengine adds — `ingestSql` (literal INSERT) and `ingestSchemaless` (line
|
|
8
|
+
* protocol). It deliberately OMITS everything TDengine has no analogue for
|
|
9
|
+
* (AC8): no `save`/`upsert`/`firstOrCreate`, no `useTransaction`/BEGIN-COMMIT,
|
|
10
|
+
* no `RETURNING`, no DB-generated-PK hydrate-back. TDengine writes are
|
|
11
|
+
* append / last-write-wins by the caller-supplied timestamp.
|
|
12
|
+
*
|
|
13
|
+
* Schema is read ONLY through the 58.3 decorator metadata getters — never
|
|
14
|
+
* `key in instance` (the `@Column() declare x` pitfall, memory
|
|
15
|
+
* `project_atlas_declare_hydration`). The connection is injected structurally
|
|
16
|
+
* (agnostic leaf, AD1 — no `@c9up/ream` import).
|
|
17
|
+
*/
|
|
18
|
+
import type { EonConnection, EonSchemalessOptions } from "../connection/EonConnection.js";
|
|
19
|
+
import { type IngestPoint } from "../ingest/stmt.js";
|
|
20
|
+
import { TimeSeriesQuery } from "../query/TimeSeriesQuery.js";
|
|
21
|
+
import { type SuperTableClass } from "../schema/compile.js";
|
|
22
|
+
/** Options for {@link SuperTableRepository}. */
|
|
23
|
+
export interface SuperTableRepositoryOptions {
|
|
24
|
+
/** STMT chunk size (rows per child per bind). Default {@link DEFAULT_BATCH_SIZE}. */
|
|
25
|
+
readonly batchSize?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Repository for a TDengine super-table — atlas `BaseRepository` parity for the
|
|
29
|
+
* time-series model. NAMED deviation from atlas (prime directive: name any
|
|
30
|
+
* divergence): atlas types reads by constructing `new Entity()` instances; eon
|
|
31
|
+
* reads plain records and never constructs instances, so typed reads are opt-in
|
|
32
|
+
* through `query(mapPoint)` — the caller supplies the row → `E` mapper, composed
|
|
33
|
+
* on top of the bigint/timestamp-reviving base hydrator (no `as`, the decode
|
|
34
|
+
* stays honest). Called without a mapper, points stay `Record<string, unknown>`.
|
|
35
|
+
*/
|
|
36
|
+
export declare class SuperTableRepository {
|
|
37
|
+
#private;
|
|
38
|
+
constructor(entityClass: SuperTableClass, conn: EonConnection, options?: SuperTableRepositoryOptions);
|
|
39
|
+
/**
|
|
40
|
+
* A fluent time-series read builder bound to this repository's connection and
|
|
41
|
+
* the entity's 58.3 schema metadata. Rows hydrate through a metadata-driven
|
|
42
|
+
* closure — declared columns by their `@Column`/`@Tag` metadata (bigint /
|
|
43
|
+
* timestamp revived as `bigint`), NEVER `key in instance` (the `declare`-field
|
|
44
|
+
* pitfall), and window pseudo-columns / aggregate aliases attached raw.
|
|
45
|
+
*
|
|
46
|
+
* Pass `mapPoint` to project each decoded row into a typed `E` (atlas-parity
|
|
47
|
+
* typed reads, no `as`): the mapper composes on top of the base hydrator so
|
|
48
|
+
* bigint/timestamp revival still happens first. Without a mapper, points stay
|
|
49
|
+
* `Record<string, unknown>`.
|
|
50
|
+
*/
|
|
51
|
+
query(): TimeSeriesQuery<Record<string, unknown>>;
|
|
52
|
+
query<E extends object>(mapPoint: (row: Record<string, unknown>) => E): TimeSeriesQuery<E>;
|
|
53
|
+
/** Ingest a single point (≙ atlas `create`). */
|
|
54
|
+
ingest(point: IngestPoint): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Bulk ingest via the columnar STMT path (≙ atlas `createMany`) — the default
|
|
57
|
+
* high-throughput, injection-safe write. Groups by child table, binds SoA
|
|
58
|
+
* columns once per batch. Returns total rows affected. `async` so a plan
|
|
59
|
+
* error (e.g. an unsafe-integer timestamp) surfaces as a rejection, not a
|
|
60
|
+
* synchronous throw at the call site.
|
|
61
|
+
*/
|
|
62
|
+
ingestMany(points: readonly IngestPoint[]): Promise<{
|
|
63
|
+
rowsAffected: number;
|
|
64
|
+
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Bulk ingest via literal SQL INSERT (AC5) — a convenience/fallback path run
|
|
67
|
+
* through `exec`. One `INSERT … USING … TAGS … VALUES` per child table; all
|
|
68
|
+
* value literals are rendered by the Rust compiler (no TS interpolation).
|
|
69
|
+
*/
|
|
70
|
+
ingestSql(points: readonly IngestPoint[]): Promise<{
|
|
71
|
+
rowsAffected: number;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Bulk ingest via schemaless line protocol (AC4) — a documented ~8–10× slower
|
|
75
|
+
* helper, NOT the default bulk path. Renders points to InfluxDB line protocol
|
|
76
|
+
* from the entity metadata and passes them to the transport.
|
|
77
|
+
*/
|
|
78
|
+
ingestSchemaless(points: readonly IngestPoint[], options?: EonSchemalessOptions): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=SuperTableRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SuperTableRepository.d.ts","sourceRoot":"","sources":["../../src/repository/SuperTableRepository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACX,aAAa,EACb,oBAAoB,EACpB,MAAM,gCAAgC,CAAC;AASxC,OAAO,EAKN,KAAK,WAAW,EAGhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,sBAAsB,CAAC;AAE9B,gDAAgD;AAChD,MAAM,WAAW,2BAA2B;IAC3C,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAM/B,WAAW,EAAE,eAAe,EAC5B,IAAI,EAAE,aAAa,EACnB,OAAO,CAAC,EAAE,2BAA2B;IActC;;;;;;;;;;;OAWG;IACH,KAAK,IAAI,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,KAAK,CAAC,CAAC,SAAS,MAAM,EACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,GAC3C,eAAe,CAAC,CAAC,CAAC;IAqBrB,gDAAgD;IAC1C,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C;;;;;;OAMG;IACG,UAAU,CACf,MAAM,EAAE,SAAS,WAAW,EAAE,GAC5B,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAKpC;;;;OAIG;IACG,SAAS,CACd,MAAM,EAAE,SAAS,WAAW,EAAE,GAC5B,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAUpC;;;;OAIG;IACG,gBAAgB,CACrB,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,OAAO,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC;CAIhB"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SuperTableRepository<TPoint>` — the Atlas-shaped ingest API for a TDengine
|
|
3
|
+
* super-table (story 58.4).
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `@c9up/atlas` `BaseRepository` naming: `ingest` (≙ `create`) and
|
|
6
|
+
* `ingestMany` (≙ `createMany`, the bulk primitive), plus two extra write paths
|
|
7
|
+
* TDengine adds — `ingestSql` (literal INSERT) and `ingestSchemaless` (line
|
|
8
|
+
* protocol). It deliberately OMITS everything TDengine has no analogue for
|
|
9
|
+
* (AC8): no `save`/`upsert`/`firstOrCreate`, no `useTransaction`/BEGIN-COMMIT,
|
|
10
|
+
* no `RETURNING`, no DB-generated-PK hydrate-back. TDengine writes are
|
|
11
|
+
* append / last-write-wins by the caller-supplied timestamp.
|
|
12
|
+
*
|
|
13
|
+
* Schema is read ONLY through the 58.3 decorator metadata getters — never
|
|
14
|
+
* `key in instance` (the `@Column() declare x` pitfall, memory
|
|
15
|
+
* `project_atlas_declare_hydration`). The connection is injected structurally
|
|
16
|
+
* (agnostic leaf, AD1 — no `@c9up/ream` import).
|
|
17
|
+
*/
|
|
18
|
+
import { getColumnMetadata, getSuperTableMetadata, getTagMetadata, getTimestampColumn, } from "../decorators/superTable.js";
|
|
19
|
+
import { toLineProtocol } from "../ingest/schemaless.js";
|
|
20
|
+
import { buildLiteralInserts } from "../ingest/sql.js";
|
|
21
|
+
import { buildColumnarIngest, compileStmtTemplate, DEFAULT_BATCH_SIZE, toBindKind, } from "../ingest/stmt.js";
|
|
22
|
+
import { TimeSeriesQuery } from "../query/TimeSeriesQuery.js";
|
|
23
|
+
import { orderTimestampFirst, } from "../schema/compile.js";
|
|
24
|
+
/**
|
|
25
|
+
* Repository for a TDengine super-table — atlas `BaseRepository` parity for the
|
|
26
|
+
* time-series model. NAMED deviation from atlas (prime directive: name any
|
|
27
|
+
* divergence): atlas types reads by constructing `new Entity()` instances; eon
|
|
28
|
+
* reads plain records and never constructs instances, so typed reads are opt-in
|
|
29
|
+
* through `query(mapPoint)` — the caller supplies the row → `E` mapper, composed
|
|
30
|
+
* on top of the bigint/timestamp-reviving base hydrator (no `as`, the decode
|
|
31
|
+
* stays honest). Called without a mapper, points stay `Record<string, unknown>`.
|
|
32
|
+
*/
|
|
33
|
+
export class SuperTableRepository {
|
|
34
|
+
#conn;
|
|
35
|
+
#plan;
|
|
36
|
+
#querySchema;
|
|
37
|
+
constructor(entityClass, conn, options) {
|
|
38
|
+
// Fail loud on a null/undefined connection — almost always a failed IoC
|
|
39
|
+
// injection (atlas `BaseRepository.ts:245` parity), not a runtime NPE later.
|
|
40
|
+
if (conn === null || conn === undefined) {
|
|
41
|
+
throw new Error(`[E_EON_MISSING_CONNECTION] SuperTableRepository for '${entityClass?.name ?? "<unknown entity>"}' requires an EonConnection (got ${conn === null ? "null" : "undefined"}). Check IoC constructor injection is wired.`);
|
|
42
|
+
}
|
|
43
|
+
this.#conn = conn;
|
|
44
|
+
this.#plan = buildPlan(entityClass, options);
|
|
45
|
+
this.#querySchema = buildQuerySchema(entityClass);
|
|
46
|
+
}
|
|
47
|
+
query(mapPoint) {
|
|
48
|
+
const base = this.#querySchema.hydrate;
|
|
49
|
+
if (mapPoint) {
|
|
50
|
+
return new TimeSeriesQuery(this.#querySchema.stable, this.#conn, (row) => mapPoint(base(row)), this.#querySchema.known);
|
|
51
|
+
}
|
|
52
|
+
return new TimeSeriesQuery(this.#querySchema.stable, this.#conn, base, this.#querySchema.known);
|
|
53
|
+
}
|
|
54
|
+
/** Ingest a single point (≙ atlas `create`). */
|
|
55
|
+
async ingest(point) {
|
|
56
|
+
await this.ingestMany([point]);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Bulk ingest via the columnar STMT path (≙ atlas `createMany`) — the default
|
|
60
|
+
* high-throughput, injection-safe write. Groups by child table, binds SoA
|
|
61
|
+
* columns once per batch. Returns total rows affected. `async` so a plan
|
|
62
|
+
* error (e.g. an unsafe-integer timestamp) surfaces as a rejection, not a
|
|
63
|
+
* synchronous throw at the call site.
|
|
64
|
+
*/
|
|
65
|
+
async ingestMany(points) {
|
|
66
|
+
if (points.length === 0)
|
|
67
|
+
return { rowsAffected: 0 };
|
|
68
|
+
return this.#conn.ingestColumnar(buildColumnarIngest(this.#plan, points));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Bulk ingest via literal SQL INSERT (AC5) — a convenience/fallback path run
|
|
72
|
+
* through `exec`. One `INSERT … USING … TAGS … VALUES` per child table; all
|
|
73
|
+
* value literals are rendered by the Rust compiler (no TS interpolation).
|
|
74
|
+
*/
|
|
75
|
+
async ingestSql(points) {
|
|
76
|
+
if (points.length === 0)
|
|
77
|
+
return { rowsAffected: 0 };
|
|
78
|
+
let rowsAffected = 0;
|
|
79
|
+
for (const sql of buildLiteralInserts(this.#plan, points)) {
|
|
80
|
+
const result = await this.#conn.exec(sql);
|
|
81
|
+
rowsAffected += result.rowsAffected;
|
|
82
|
+
}
|
|
83
|
+
return { rowsAffected };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Bulk ingest via schemaless line protocol (AC4) — a documented ~8–10× slower
|
|
87
|
+
* helper, NOT the default bulk path. Renders points to InfluxDB line protocol
|
|
88
|
+
* from the entity metadata and passes them to the transport.
|
|
89
|
+
*/
|
|
90
|
+
async ingestSchemaless(points, options) {
|
|
91
|
+
if (points.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
await this.#conn.schemaless(toLineProtocol(this.#plan, points), options);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Resolve the immutable ingest plan from a decorated super-table class. */
|
|
97
|
+
function buildPlan(entityClass, options) {
|
|
98
|
+
const meta = getSuperTableMetadata(entityClass);
|
|
99
|
+
if (!meta) {
|
|
100
|
+
throw new Error(`[E_EON_NOT_A_SUPERTABLE] ${entityClass.name} is not decorated with @SuperTable`);
|
|
101
|
+
}
|
|
102
|
+
const tsProperty = getTimestampColumn(entityClass);
|
|
103
|
+
if (!tsProperty) {
|
|
104
|
+
throw new Error(`[E_EON_NO_TIMESTAMP] super-table '${meta.name}' has no @Timestamp column`);
|
|
105
|
+
}
|
|
106
|
+
const columnMeta = getColumnMetadata(entityClass);
|
|
107
|
+
const tsColumn = columnMeta.find((c) => c.propertyKey === tsProperty);
|
|
108
|
+
if (!tsColumn) {
|
|
109
|
+
throw new Error(`[E_EON_NO_TIMESTAMP] super-table '${meta.name}' @Timestamp column '${tsProperty}' is not registered`);
|
|
110
|
+
}
|
|
111
|
+
// Timestamp first, from the SAME shared ordering the STABLE was created with
|
|
112
|
+
// (`orderTimestampFirst`), so the positional STMT bind lines up with the
|
|
113
|
+
// STABLE's physical columns and can never drift.
|
|
114
|
+
const ordered = orderTimestampFirst(columnMeta, tsColumn, tsProperty);
|
|
115
|
+
const columns = ordered.map((c) => ({
|
|
116
|
+
property: c.propertyKey,
|
|
117
|
+
kind: toBindKind(c.type, c.propertyKey),
|
|
118
|
+
}));
|
|
119
|
+
const tags = getTagMetadata(entityClass).map((t) => ({
|
|
120
|
+
property: t.propertyKey,
|
|
121
|
+
kind: toBindKind(t.type, t.propertyKey),
|
|
122
|
+
}));
|
|
123
|
+
if (tags.length === 0) {
|
|
124
|
+
throw new Error(`[E_EON_NO_TAGS] super-table '${meta.name}' has no @Tag columns; a child table cannot be routed without tags`);
|
|
125
|
+
}
|
|
126
|
+
const batchSize = options?.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
127
|
+
if (!Number.isInteger(batchSize) || batchSize <= 0) {
|
|
128
|
+
throw new Error(`[E_EON_BATCH_SIZE] batchSize must be a positive integer (got ${batchSize})`);
|
|
129
|
+
}
|
|
130
|
+
const templateSql = compileStmtTemplate(meta.name, tags.map((t) => t.property), columns.map((c) => c.property));
|
|
131
|
+
return {
|
|
132
|
+
stable: meta.name,
|
|
133
|
+
templateSql,
|
|
134
|
+
tsProperty,
|
|
135
|
+
columns,
|
|
136
|
+
tags,
|
|
137
|
+
batchSize,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build the read-side schema from the 58.3 decorator getters. The `@Column`/
|
|
142
|
+
* `@Tag` metadata is the ONLY source of truth — never `key in instance` (the
|
|
143
|
+
* `@Column() declare x` pitfall, memory `project_atlas_declare_hydration`).
|
|
144
|
+
*/
|
|
145
|
+
function buildQuerySchema(entityClass) {
|
|
146
|
+
const meta = getSuperTableMetadata(entityClass);
|
|
147
|
+
if (!meta) {
|
|
148
|
+
throw new Error(`[E_EON_NOT_A_SUPERTABLE] ${entityClass.name} is not decorated with @SuperTable`);
|
|
149
|
+
}
|
|
150
|
+
const tsProperty = getTimestampColumn(entityClass);
|
|
151
|
+
if (!tsProperty) {
|
|
152
|
+
throw new Error(`[E_EON_NO_TIMESTAMP] super-table '${meta.name}' has no @Timestamp column`);
|
|
153
|
+
}
|
|
154
|
+
const columns = getColumnMetadata(entityClass);
|
|
155
|
+
const tags = getTagMetadata(entityClass);
|
|
156
|
+
const known = new Set([
|
|
157
|
+
...columns.map((c) => c.propertyKey),
|
|
158
|
+
...tags.map((t) => t.propertyKey),
|
|
159
|
+
]);
|
|
160
|
+
// A column reads back as `bigint` when it is the timestamp primary column or
|
|
161
|
+
// its logical type is a 64-bit integer — parity with the compile-boundary
|
|
162
|
+
// precision guard, so a nanosecond `ts` or a BIGINT metric never narrows to a
|
|
163
|
+
// lossy double.
|
|
164
|
+
const bigintColumns = new Set();
|
|
165
|
+
for (const column of columns) {
|
|
166
|
+
if (column.propertyKey === tsProperty || isBigintLogicalType(column.type)) {
|
|
167
|
+
bigintColumns.add(column.propertyKey);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const tag of tags) {
|
|
171
|
+
if (isBigintLogicalType(tag.type))
|
|
172
|
+
bigintColumns.add(tag.propertyKey);
|
|
173
|
+
}
|
|
174
|
+
const hydrate = (row) => {
|
|
175
|
+
// Decode by metadata membership, iterating the raw row's own keys — never
|
|
176
|
+
// `for (key in instance)` (a `declare` field is not an own property).
|
|
177
|
+
const point = {};
|
|
178
|
+
for (const [key, value] of Object.entries(row)) {
|
|
179
|
+
if (!known.has(key))
|
|
180
|
+
continue;
|
|
181
|
+
// `defineProperty`, not assignment: a column named `__proto__`/`constructor`
|
|
182
|
+
// would otherwise hit the inherited accessor and re-parent `point`.
|
|
183
|
+
Object.defineProperty(point, key, {
|
|
184
|
+
value: bigintColumns.has(key) ? reviveBigInt(value) : value,
|
|
185
|
+
writable: true,
|
|
186
|
+
enumerable: true,
|
|
187
|
+
configurable: true,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return point;
|
|
191
|
+
};
|
|
192
|
+
return { stable: meta.name, hydrate, known };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Logical types that read back as 64-bit integers. Matched case-insensitively
|
|
196
|
+
* and including the `bigInteger` alias, so it stays in lock-step with the DDL /
|
|
197
|
+
* bind side (`TYPE_KIND_MAP` accepts `bigint`/`bigInteger`/any-case `BIGINT`) —
|
|
198
|
+
* a divergence here would read a genuine BIGINT column back as a lossy `number`.
|
|
199
|
+
*/
|
|
200
|
+
function isBigintLogicalType(type) {
|
|
201
|
+
if (type === undefined)
|
|
202
|
+
return false;
|
|
203
|
+
const t = type.toLowerCase();
|
|
204
|
+
return t === "bigint" || t === "biginteger" || t === "timestamp";
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Revive a raw column value into a `bigint` for a timestamp / BIGINT column.
|
|
208
|
+
* Accepts an existing `bigint`, an integer `number`, or an all-digits string;
|
|
209
|
+
* passes `null`/`undefined` through (a nullable window aggregate). Anything else
|
|
210
|
+
* — a fractional number, an unexpected type — fails loud rather than silently
|
|
211
|
+
* narrowing precision.
|
|
212
|
+
*/
|
|
213
|
+
function reviveBigInt(value) {
|
|
214
|
+
if (typeof value === "bigint")
|
|
215
|
+
return value;
|
|
216
|
+
if (typeof value === "number") {
|
|
217
|
+
if (!Number.isInteger(value)) {
|
|
218
|
+
throw new Error(`[E_EON_HYDRATE_BIGINT] expected an integer for a bigint/timestamp column, got the non-integer ${value}`);
|
|
219
|
+
}
|
|
220
|
+
if (!Number.isSafeInteger(value)) {
|
|
221
|
+
throw new Error(`[E_EON_HYDRATE_BIGINT] integer ${value} exceeds the JS safe-integer range (2^53) and has already lost precision; the transport must hand a bigint/timestamp column back as bigint or a string, not a lossy number`);
|
|
222
|
+
}
|
|
223
|
+
return BigInt(value);
|
|
224
|
+
}
|
|
225
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) {
|
|
226
|
+
return BigInt(value);
|
|
227
|
+
}
|
|
228
|
+
if (value === null || value === undefined)
|
|
229
|
+
return value;
|
|
230
|
+
throw new Error(`[E_EON_HYDRATE_BIGINT] cannot revive a bigint/timestamp column from a ${typeof value}`);
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=SuperTableRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SuperTableRepository.js","sourceRoot":"","sources":["../../src/repository/SuperTableRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript spec shapes mirroring the Rust `crates/eon-query/src/ddl.rs`
|
|
3
|
+
* structs, plus the logical → compiler-kind type map (parallel to atlas
|
|
4
|
+
* `schema/types.ts`).
|
|
5
|
+
*
|
|
6
|
+
* The facade assembles these plain JSON objects from decorator metadata and
|
|
7
|
+
* hands them to `compileStatementNative`; the Rust compiler owns all quoting,
|
|
8
|
+
* type-mapping, and schema-rule validation — the TS side never string-builds
|
|
9
|
+
* SQL (D1, the injection seam stays in Rust).
|
|
10
|
+
*/
|
|
11
|
+
/** The user-facing logical type strings accepted on `@Column`/`@Tag`/`@Timestamp`. */
|
|
12
|
+
export type EonLogicalType = "timestamp" | "int" | "integer" | "bigint" | "biginteger" | "smallint" | "tinyint" | "float" | "double" | "bool" | "boolean" | "string" | "varchar" | "nchar" | "binary" | "varbinary" | "json" | "decimal";
|
|
13
|
+
export declare const TYPE_KIND_MAP: Record<string, string>;
|
|
14
|
+
/** A column or tag definition — the flattened `StableColumnDef` + `ColumnTypeSpec`. */
|
|
15
|
+
export interface EonColumnSpec {
|
|
16
|
+
name: string;
|
|
17
|
+
kind: string;
|
|
18
|
+
length: number | null;
|
|
19
|
+
precision: number | null;
|
|
20
|
+
scale: number | null;
|
|
21
|
+
}
|
|
22
|
+
export interface CreateStableSpec {
|
|
23
|
+
kind: "createStable";
|
|
24
|
+
name: string;
|
|
25
|
+
columns: EonColumnSpec[];
|
|
26
|
+
tags: EonColumnSpec[];
|
|
27
|
+
ifNotExists: boolean;
|
|
28
|
+
/** Optional STABLE `KEEP` retention (a 3.3.x+ TDengine feature). Validated as a duration in Rust. */
|
|
29
|
+
keep?: string;
|
|
30
|
+
}
|
|
31
|
+
/** One `ALTER STABLE` change — tagged on `op`, mirroring the Rust `AlterChange`. */
|
|
32
|
+
export type AlterChange = {
|
|
33
|
+
op: "addColumn";
|
|
34
|
+
name: string;
|
|
35
|
+
type: Omit<EonColumnSpec, "name">;
|
|
36
|
+
} | {
|
|
37
|
+
op: "dropColumn";
|
|
38
|
+
name: string;
|
|
39
|
+
} | {
|
|
40
|
+
op: "modifyColumn";
|
|
41
|
+
name: string;
|
|
42
|
+
type: Omit<EonColumnSpec, "name">;
|
|
43
|
+
} | {
|
|
44
|
+
op: "addTag";
|
|
45
|
+
name: string;
|
|
46
|
+
type: Omit<EonColumnSpec, "name">;
|
|
47
|
+
} | {
|
|
48
|
+
op: "dropTag";
|
|
49
|
+
name: string;
|
|
50
|
+
} | {
|
|
51
|
+
op: "modifyTag";
|
|
52
|
+
name: string;
|
|
53
|
+
type: Omit<EonColumnSpec, "name">;
|
|
54
|
+
} | {
|
|
55
|
+
op: "renameTag";
|
|
56
|
+
from: string;
|
|
57
|
+
to: string;
|
|
58
|
+
};
|
|
59
|
+
export interface AlterStableSpec {
|
|
60
|
+
kind: "alterStable";
|
|
61
|
+
name: string;
|
|
62
|
+
changes: AlterChange[];
|
|
63
|
+
}
|
|
64
|
+
export interface CreateChildTableSpec {
|
|
65
|
+
kind: "createChildTable";
|
|
66
|
+
name: string;
|
|
67
|
+
using: string;
|
|
68
|
+
tags: unknown[];
|
|
69
|
+
ifNotExists: boolean;
|
|
70
|
+
/** Inline tag values as SQL literals (58.3 `exec`) vs `?` placeholders (STMT, 58.4). */
|
|
71
|
+
literal: boolean;
|
|
72
|
+
/** Optional child-table `TTL` in whole days (`>= 0`, a 3.3.x+ TDengine feature). */
|
|
73
|
+
ttl?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface DropStableSpec {
|
|
76
|
+
kind: "dropStable";
|
|
77
|
+
name: string;
|
|
78
|
+
ifExists: boolean;
|
|
79
|
+
}
|
|
80
|
+
/** TDengine timestamp precision — the create-only `PRECISION` database option. */
|
|
81
|
+
export type EonPrecision = "ms" | "us" | "ns";
|
|
82
|
+
/** TDengine `CACHEMODEL` allowlist. */
|
|
83
|
+
export type EonCacheModel = "none" | "last_row" | "last_value" | "both";
|
|
84
|
+
/**
|
|
85
|
+
* `CREATE DATABASE` options (retention / storage). Every value is validated in
|
|
86
|
+
* Rust (durations, precision/cachemodel/wal-level allowlists, `KEEP >= 3x
|
|
87
|
+
* DURATION`). Options are emitted only when present, in a fixed order.
|
|
88
|
+
*/
|
|
89
|
+
export interface EonDatabaseOptions {
|
|
90
|
+
/** Retention duration (e.g. `"90d"`). Must be `>= 3 x DURATION` when both are set with the same unit. */
|
|
91
|
+
keep?: string;
|
|
92
|
+
/** Per-file time span (e.g. `"10d"`). Create-only — cannot be altered. */
|
|
93
|
+
duration?: string;
|
|
94
|
+
/** Timestamp precision. Create-only — cannot be altered. */
|
|
95
|
+
precision?: EonPrecision;
|
|
96
|
+
/** Write buffer size in MB. */
|
|
97
|
+
buffer?: number;
|
|
98
|
+
/** WAL level (1 or 2). */
|
|
99
|
+
walLevel?: 1 | 2;
|
|
100
|
+
/** Row cache mode. */
|
|
101
|
+
cachemodel?: EonCacheModel;
|
|
102
|
+
}
|
|
103
|
+
export interface CreateDatabaseSpec extends EonDatabaseOptions {
|
|
104
|
+
kind: "createDatabase";
|
|
105
|
+
name: string;
|
|
106
|
+
ifNotExists: boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* `ALTER DATABASE` — TDengine changes exactly ONE option per statement, so
|
|
110
|
+
* exactly one field must be set. Create-only options (`PRECISION`/`DURATION`)
|
|
111
|
+
* are intentionally absent here.
|
|
112
|
+
*/
|
|
113
|
+
export interface EonAlterDatabaseOptions {
|
|
114
|
+
keep?: string;
|
|
115
|
+
buffer?: number;
|
|
116
|
+
walLevel?: 1 | 2;
|
|
117
|
+
cachemodel?: EonCacheModel;
|
|
118
|
+
replica?: number;
|
|
119
|
+
minrows?: number;
|
|
120
|
+
}
|
|
121
|
+
export interface AlterDatabaseSpec {
|
|
122
|
+
kind: "alterDatabase";
|
|
123
|
+
name: string;
|
|
124
|
+
/** The Rust-side option name (e.g. `"keep"`, `"wal_level"`). */
|
|
125
|
+
option: string;
|
|
126
|
+
value: string | number;
|
|
127
|
+
}
|
|
128
|
+
/** A basic (non-super) table: first column `TIMESTAMP`, then columns, no `TAGS`. */
|
|
129
|
+
export interface CreateTableSpec {
|
|
130
|
+
kind: "createTable";
|
|
131
|
+
name: string;
|
|
132
|
+
columns: EonColumnSpec[];
|
|
133
|
+
ifNotExists: boolean;
|
|
134
|
+
}
|
|
135
|
+
export interface DropTableSpec {
|
|
136
|
+
kind: "dropTable";
|
|
137
|
+
name: string;
|
|
138
|
+
ifExists: boolean;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=CreateStableSpec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateStableSpec.d.ts","sourceRoot":"","sources":["../../src/schema/CreateStableSpec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,sFAAsF;AACtF,MAAM,MAAM,cAAc,GACvB,WAAW,GACX,KAAK,GACL,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,UAAU,GACV,SAAS,GACT,OAAO,GACP,QAAQ,GACR,MAAM,GACN,SAAS,GACT,QAAQ,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,WAAW,GACX,MAAM,GACN,SAAS,CAAC;AAgCb,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAqB,CAAC;AAEvE,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,qGAAqG;IACrG,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,oFAAoF;AACpF,MAAM,MAAM,WAAW,GACpB;IAAE,EAAE,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;CAAE,GACpE;IAAE,EAAE,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,EAAE,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;CAAE,GACvE;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;CAAE,GACjE;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;CAAE,GACpE;IAAE,EAAE,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,wFAAwF;IACxF,OAAO,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,kFAAkF;AAClF,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,uCAAuC;AACvC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,CAAC;AAExE;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,yGAAyG;IACzG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,sBAAsB;IACtB,UAAU,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC7D,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript spec shapes mirroring the Rust `crates/eon-query/src/ddl.rs`
|
|
3
|
+
* structs, plus the logical → compiler-kind type map (parallel to atlas
|
|
4
|
+
* `schema/types.ts`).
|
|
5
|
+
*
|
|
6
|
+
* The facade assembles these plain JSON objects from decorator metadata and
|
|
7
|
+
* hands them to `compileStatementNative`; the Rust compiler owns all quoting,
|
|
8
|
+
* type-mapping, and schema-rule validation — the TS side never string-builds
|
|
9
|
+
* SQL (D1, the injection seam stays in Rust).
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Map a logical type string to the compiler's `ColumnTypeKind` (serde
|
|
13
|
+
* camelCase). Common aliases collapse onto one kind (`int`/`integer` → `int`,
|
|
14
|
+
* `string`/`varchar` → `varchar`, `binary`/`varbinary` → `varbinary`). Physical
|
|
15
|
+
* TDengine types are resolved from these kinds in Rust (`Dialect::map_column_type`).
|
|
16
|
+
*/
|
|
17
|
+
const TYPE_KIND_ENTRIES = {
|
|
18
|
+
timestamp: "timestamp",
|
|
19
|
+
int: "int",
|
|
20
|
+
integer: "int",
|
|
21
|
+
bigint: "bigInt",
|
|
22
|
+
biginteger: "bigInt",
|
|
23
|
+
smallint: "smallInt",
|
|
24
|
+
tinyint: "tinyInt",
|
|
25
|
+
float: "float",
|
|
26
|
+
double: "double",
|
|
27
|
+
bool: "bool",
|
|
28
|
+
boolean: "bool",
|
|
29
|
+
string: "varchar",
|
|
30
|
+
varchar: "varchar",
|
|
31
|
+
nchar: "nchar",
|
|
32
|
+
binary: "varbinary",
|
|
33
|
+
varbinary: "varbinary",
|
|
34
|
+
json: "json",
|
|
35
|
+
decimal: "decimal",
|
|
36
|
+
// `satisfies` makes this exhaustive + exact against EonLogicalType (a missing
|
|
37
|
+
// or stray key is a compile error) while the exported map stays string-keyed
|
|
38
|
+
// so an arbitrary logical string can be looked up without a cast.
|
|
39
|
+
};
|
|
40
|
+
export const TYPE_KIND_MAP = TYPE_KIND_ENTRIES;
|
|
41
|
+
//# sourceMappingURL=CreateStableSpec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateStableSpec.js","sourceRoot":"","sources":["../../src/schema/CreateStableSpec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuBH;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG;IACzB,SAAS,EAAE,WAAW;IACtB,GAAG,EAAE,KAAK;IACV,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,QAAQ;IACpB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,WAAW;IACnB,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,8EAA8E;IAC9E,6EAA6E;IAC7E,kEAAkE;CACzB,CAAC;AAE3C,MAAM,CAAC,MAAM,aAAa,GAA2B,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EonMigrationRunner` — discovers, executes, and tracks eon (TDengine)
|
|
3
|
+
* migrations. Mirrors atlas `MigrationRunner` (`schema/MigrationRunner.ts`):
|
|
4
|
+
* sorted-file discovery, a `ream_`-prefixed tracking table, batch-based
|
|
5
|
+
* rollback, `init/status/migrate/rollback/reset/refresh/fresh/dryRun`.
|
|
6
|
+
*
|
|
7
|
+
* ── TWO named TDengine deviations from atlas (AC6) ──────────────────────────
|
|
8
|
+
*
|
|
9
|
+
* 1. **No transactions / no engine rollback.** TDengine DDL is non-transactional
|
|
10
|
+
* — a batch CANNOT be applied atomically. The runner executes statements
|
|
11
|
+
* **sequentially**; a mid-migration failure leaves earlier statements
|
|
12
|
+
* applied. The mitigation is idempotent DDL (`IF (NOT) EXISTS`, the
|
|
13
|
+
* `EonSchema` default) so a re-run converges. There is NO `runInTransaction`.
|
|
14
|
+
*
|
|
15
|
+
* 2. **No `UNIQUE`, no auto-increment.** The tracking table is a basic table
|
|
16
|
+
* `ream_eon_migrations(executed_at TIMESTAMP, name VARCHAR(255), batch INT)`;
|
|
17
|
+
* the applied-set is de-duped **by name in JS**, and each record is written
|
|
18
|
+
* with a strictly-increasing `executed_at` so rollback can delete it by that
|
|
19
|
+
* unique timestamp key (TDengine only allows a `DELETE` predicate on the
|
|
20
|
+
* primary timestamp column).
|
|
21
|
+
*
|
|
22
|
+
* Agnostic leaf: takes the connection structurally and a plain directory path —
|
|
23
|
+
* no `@c9up/ream` import, no app helper (`project_package_extraction`).
|
|
24
|
+
*/
|
|
25
|
+
import type { EonConnection } from "../connection/EonConnection.js";
|
|
26
|
+
export interface EonMigrationOptions {
|
|
27
|
+
/** Directory holding migration files. Default `database/eon-migrations`. */
|
|
28
|
+
migrationsDir?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Tracking-table name. Default `ream_eon_migrations`. Must match
|
|
31
|
+
* `/^[A-Za-z_][A-Za-z0-9_]*$/`. The `ream_` prefix marks it a framework/system
|
|
32
|
+
* table (`feedback_underscore_policy`) — keep it so cleanup helpers skip it.
|
|
33
|
+
*/
|
|
34
|
+
tableName?: string;
|
|
35
|
+
}
|
|
36
|
+
export type MigrationState = "applied" | "pending";
|
|
37
|
+
export interface MigrationStatus {
|
|
38
|
+
name: string;
|
|
39
|
+
state: MigrationState;
|
|
40
|
+
batch?: number;
|
|
41
|
+
}
|
|
42
|
+
export declare class EonMigrationRunner {
|
|
43
|
+
#private;
|
|
44
|
+
constructor(conn: EonConnection, options?: EonMigrationOptions);
|
|
45
|
+
/** Create the tracking table (`IF NOT EXISTS`) via the basic-table DDL path. */
|
|
46
|
+
init(): Promise<void>;
|
|
47
|
+
/** The status of every discovered migration (applied vs pending). */
|
|
48
|
+
status(): Promise<MigrationStatus[]>;
|
|
49
|
+
/** Run every pending migration (filename order), recording each. */
|
|
50
|
+
migrate(): Promise<string[]>;
|
|
51
|
+
/** Roll back the most-recent batch (files in reverse order), running `down()`. */
|
|
52
|
+
rollback(): Promise<string[]>;
|
|
53
|
+
/** Roll back every applied batch (Lucid `migrate:reset`). */
|
|
54
|
+
reset(): Promise<string[]>;
|
|
55
|
+
/** Reset then re-run every migration (Lucid `migrate:refresh`). */
|
|
56
|
+
refresh(): Promise<{
|
|
57
|
+
rolled: string[];
|
|
58
|
+
executed: string[];
|
|
59
|
+
}>;
|
|
60
|
+
/** Alias of {@link refresh} (Lucid `migrate:fresh`). */
|
|
61
|
+
fresh(): Promise<{
|
|
62
|
+
rolled: string[];
|
|
63
|
+
executed: string[];
|
|
64
|
+
}>;
|
|
65
|
+
/** Compute the SQL each pending migration would emit, WITHOUT executing it. */
|
|
66
|
+
dryRun(): Promise<Array<{
|
|
67
|
+
name: string;
|
|
68
|
+
sql: string[];
|
|
69
|
+
}>>;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=EonMigrationRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EonMigrationRunner.d.ts","sourceRoot":"","sources":["../../src/schema/EonMigrationRunner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAUpE,MAAM,WAAW,mBAAmB;IACnC,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AASD,qBAAa,kBAAkB;;gBAKlB,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,mBAAwB;IAYlE,gFAAgF;IAC1E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC3B,qEAAqE;IAC/D,MAAM,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAa1C,oEAAoE;IAC9D,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BlC,kFAAkF;IAC5E,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA2BnC,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAYhC,mEAAmE;IAC7D,OAAO,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAMlE,wDAAwD;IACxD,KAAK,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAI1D,+EAA+E;IACzE,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAyI/D"}
|