@spine-event-engine/storage-mysql 2.0.0-snapshot.21 → 2.0.0-snapshot.23

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.
@@ -0,0 +1,104 @@
1
+ import type { AgentHistoryEntry } from "@spine-event-engine/proto/agent";
2
+ import type { AgentHistoryRecord } from "@spine-event-engine/proto/generated/spine/server/agent/history_record_pb.js";
3
+ import { type AgentHistoryPage, type AgentHistoryRead, type AgentHistoryStorage, type AgentHistoryStorageInput } from "@spine-event-engine/storage/provider";
4
+ import { MysqlRecordStorage } from "./record-storage.js";
5
+ /**
6
+ * Hashes complete identities into bounded physical IDs and indexed scope keys.
7
+ */
8
+ export declare const AgentHistoryHash: {
9
+ /**
10
+ * Calculates the fixed-width digest of a complete logical identity.
11
+ *
12
+ * @param value Complete logical identity.
13
+ * @returns Full SHA-256 hex digest.
14
+ */
15
+ value(value: string): string;
16
+ };
17
+ /**
18
+ * Stores Agent history in the shared MySQL immutable record family.
19
+ *
20
+ * @typeParam Id Typed Agent identifier.
21
+ */
22
+ export declare class MysqlAgentHistory<Id> implements AgentHistoryStorage<Id> {
23
+ #private;
24
+ private readonly input;
25
+ private readonly records;
26
+ /**
27
+ * Binds one typed Agent repository to the provider record handle.
28
+ *
29
+ * @param input Repository and complete tenant scope.
30
+ * @param records Native record-family storage.
31
+ */
32
+ constructor(input: AgentHistoryStorageInput<Id>, records: MysqlRecordStorage<string, AgentHistoryRecord>);
33
+ /**
34
+ * Writes one original entry or confirms byte-identical content.
35
+ *
36
+ * @param entityId Typed Agent identifier.
37
+ * @param entry Complete history entry.
38
+ * @returns Completion after immutable storage confirms the entry.
39
+ */
40
+ append(entityId: Id, entry: AgentHistoryEntry): Promise<void>;
41
+ /**
42
+ * Reads a bounded indexed page in full Timestamp/category/UTF-8 order.
43
+ *
44
+ * @param request Typed Agent, view, continuation, and response limits.
45
+ * @returns Original entries and continuation status.
46
+ */
47
+ read(request: AgentHistoryRead<Id>): Promise<AgentHistoryPage>;
48
+ /**
49
+
50
+ * Closes this handle without deleting stored records.
51
+
52
+ */
53
+ close(): void;
54
+ /**
55
+ * Prepares fixed composite indexes after canonical table initialization.
56
+ *
57
+ * @returns Shared preparation promise for this handle.
58
+ */
59
+ private ready;
60
+ /**
61
+ * Creates each native index idempotently on the shared record family.
62
+ *
63
+ * @returns Completion after all three definitions are validated.
64
+ */
65
+ private initialize;
66
+ /**
67
+ * Reads a bounded native page with complete scope and view predicates.
68
+ *
69
+ * @param agentKey Canonical Agent key.
70
+ * @param request View and response bounds.
71
+ * @param after Exclusive encoded order key.
72
+ * @param limit Maximum native rows for this chunk.
73
+ * @returns Complete original stored records.
74
+ */
75
+ private fetch;
76
+ /**
77
+ * Adds the indexed view predicate and exact original selector.
78
+ *
79
+ * @param request Selected history view.
80
+ * @param clauses Mutable bound SQL predicates.
81
+ * @param values Mutable bound SQL values.
82
+ */
83
+ private viewPredicate;
84
+ /**
85
+ * Validates view identity and complete-key continuation category.
86
+ *
87
+ * @param request View and continuation key.
88
+ */
89
+ private validateView;
90
+ /**
91
+ * Converts a typed Agent ID to its canonical key.
92
+ *
93
+ * @param id Typed Agent identifier.
94
+ * @returns Canonical storage key.
95
+ */
96
+ private key;
97
+ /**
98
+
99
+ * Rejects calls after this handle closes.
100
+
101
+ */
102
+ private requireOpen;
103
+ }
104
+ //# sourceMappingURL=agent-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-history.d.ts","sourceRoot":"","sources":["../../src/mysql/agent-history.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6EAA6E,CAAC;AACtH,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD;;GAEG;AACH,eAAO,MAAM,gBAAgB;IAC3B;;;;;OAKG;iBACU,MAAM,GAAG,MAAM;CAG7B,CAAC;AAEF;;;;GAIG;AACH,qBAAa,iBAAiB,CAAC,EAAE,CAAE,YAAW,mBAAmB,CAAC,EAAE,CAAC;;IAoBjE,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B;;;;;OAKG;gBAEgB,KAAK,EAAE,wBAAwB,CAAC,EAAE,CAAC,EACnC,OAAO,EAAE,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,CAAC;IAG1E;;;;;;OAMG;IACG,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAYpE;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAMb;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAIb;;;;OAIG;YACW,UAAU;IAexB;;;;;;;;OAQG;YACW,KAAK;IA4BnB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;OAKG;IACH,OAAO,CAAC,GAAG;IAMX;;;;OAIG;IACH,OAAO,CAAC,WAAW;CAGpB"}
@@ -0,0 +1,212 @@
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 { createHash } from "node:crypto";
15
+ import { AgentHistoryKeys, AgentHistoryPages, } from "@spine-event-engine/storage/provider";
16
+ import { AgentHistoryRecords } from "@spine-event-engine/storage/provider";
17
+ import { MysqlRecordStorage } from "./record-storage.js";
18
+ import { AgentHistoryIndexBytes } from "./table-spec.js";
19
+ /**
20
+ * Hashes complete identities into bounded physical IDs and indexed scope keys.
21
+ */
22
+ export const AgentHistoryHash = {
23
+ /**
24
+ * Calculates the fixed-width digest of a complete logical identity.
25
+ *
26
+ * @param value Complete logical identity.
27
+ * @returns Full SHA-256 hex digest.
28
+ */
29
+ value(value) {
30
+ return createHash("sha256").update(value, "utf8").digest("hex");
31
+ },
32
+ };
33
+ /**
34
+ * Stores Agent history in the shared MySQL immutable record family.
35
+ *
36
+ * @typeParam Id Typed Agent identifier.
37
+ */
38
+ export class MysqlAgentHistory {
39
+ input;
40
+ records;
41
+ /**
42
+ * Records whether this handle accepts calls.
43
+ */
44
+ #open = true;
45
+ /**
46
+
47
+ * Reuses one table and index preparation promise.
48
+
49
+ */
50
+ #ready;
51
+ /**
52
+ * Binds one typed Agent repository to the provider record handle.
53
+ *
54
+ * @param input Repository and complete tenant scope.
55
+ * @param records Native record-family storage.
56
+ */
57
+ constructor(input, records) {
58
+ this.input = input;
59
+ this.records = records;
60
+ }
61
+ /**
62
+ * Writes one original entry or confirms byte-identical content.
63
+ *
64
+ * @param entityId Typed Agent identifier.
65
+ * @param entry Complete history entry.
66
+ * @returns Completion after immutable storage confirms the entry.
67
+ */
68
+ async append(entityId, entry) {
69
+ this.requireOpen();
70
+ if (AgentHistoryKeys.indexValue(AgentHistoryKeys.fromEntry(entry)).length > AgentHistoryIndexBytes)
71
+ throw new RangeError("Agent history ordering key exceeds the MySQL index capacity.");
72
+ await this.ready();
73
+ await this.records.writeImmutable(AgentHistoryRecords.record(this.input.stateType, this.key(entityId), entry));
74
+ }
75
+ /**
76
+ * Reads a bounded indexed page in full Timestamp/category/UTF-8 order.
77
+ *
78
+ * @param request Typed Agent, view, continuation, and response limits.
79
+ * @returns Original entries and continuation status.
80
+ */
81
+ async read(request) {
82
+ this.requireOpen();
83
+ await this.ready();
84
+ const agentKey = this.key(request.entityId);
85
+ const after = request.after === undefined ? undefined : AgentHistoryKeys.indexValue(request.after);
86
+ this.validateView(request);
87
+ return AgentHistoryPages.read(request, after, (cursor, limit) => this.fetch(agentKey, request, cursor, limit));
88
+ }
89
+ /**
90
+
91
+ * Closes this handle without deleting stored records.
92
+
93
+ */
94
+ close() {
95
+ if (!this.#open)
96
+ return;
97
+ this.#open = false;
98
+ this.records.close();
99
+ }
100
+ /**
101
+ * Prepares fixed composite indexes after canonical table initialization.
102
+ *
103
+ * @returns Shared preparation promise for this handle.
104
+ */
105
+ ready() {
106
+ return (this.#ready ??= this.initialize());
107
+ }
108
+ /**
109
+ * Creates each native index idempotently on the shared record family.
110
+ *
111
+ * @returns Completion after all three definitions are validated.
112
+ */
113
+ async initialize() {
114
+ await this.records.prepare();
115
+ await this.records.ensureHistoryIndex("agent_history_full", ["scope_digest", "order_key"]);
116
+ await this.records.ensureHistoryIndex("agent_history_category", [
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
+ ]);
126
+ }
127
+ /**
128
+ * Reads a bounded native page with complete scope and view predicates.
129
+ *
130
+ * @param agentKey Canonical Agent key.
131
+ * @param request View and response bounds.
132
+ * @param after Exclusive encoded order key.
133
+ * @param limit Maximum native rows for this chunk.
134
+ * @returns Complete original stored records.
135
+ */
136
+ async fetch(agentKey, request, after, limit) {
137
+ const values = [
138
+ AgentHistoryHash.value(AgentHistoryRecords.scope(this.input.stateType, agentKey)),
139
+ this.input.stateType,
140
+ agentKey,
141
+ ];
142
+ const clauses = [
143
+ "`scope_digest` = ?",
144
+ "BINARY `state_type` = BINARY ?",
145
+ "BINARY `agent_key` = BINARY ?",
146
+ ];
147
+ this.viewPredicate(request, clauses, values);
148
+ if (after !== undefined) {
149
+ values.push(after);
150
+ clauses.push("`order_key` > ?");
151
+ }
152
+ values.push(limit);
153
+ const sql = `SELECT \`bytes\` FROM \`${this.records.tableName}\` WHERE ${clauses.join(" AND ")} ` +
154
+ "ORDER BY `order_key` ASC LIMIT ?";
155
+ return this.records.historyPage(sql, values);
156
+ }
157
+ /**
158
+ * Adds the indexed view predicate and exact original selector.
159
+ *
160
+ * @param request Selected history view.
161
+ * @param clauses Mutable bound SQL predicates.
162
+ * @param values Mutable bound SQL values.
163
+ */
164
+ viewPredicate(request, clauses, values) {
165
+ if (request.view.kind === "full")
166
+ return;
167
+ if (request.view.kind === "conversation") {
168
+ const conversation = request.view.conversation.value;
169
+ values.push(AgentHistoryHash.value(conversation));
170
+ clauses.push("`conversation_digest` = ?");
171
+ values.push(conversation);
172
+ clauses.push("BINARY `conversation_key` = BINARY ?");
173
+ return;
174
+ }
175
+ values.push(request.view.kind);
176
+ clauses.push("`category` = ?");
177
+ }
178
+ /**
179
+ * Validates view identity and complete-key continuation category.
180
+ *
181
+ * @param request View and continuation key.
182
+ */
183
+ validateView(request) {
184
+ const kind = request.view.kind;
185
+ if (kind === "conversation" && request.view.conversation.value.length === 0)
186
+ throw new TypeError("Conversation history requires a ConversationId.");
187
+ if (request.after !== undefined && kind !== "full" && request.after.category !== kind)
188
+ throw new TypeError("Agent history boundary category does not match the view.");
189
+ }
190
+ /**
191
+ * Converts a typed Agent ID to its canonical key.
192
+ *
193
+ * @param id Typed Agent identifier.
194
+ * @returns Canonical storage key.
195
+ */
196
+ key(id) {
197
+ const key = this.input.id.key(id);
198
+ if (key.length === 0)
199
+ throw new TypeError("Agent history Entity ID is required.");
200
+ return key;
201
+ }
202
+ /**
203
+
204
+ * Rejects calls after this handle closes.
205
+
206
+ */
207
+ requireOpen() {
208
+ if (!this.#open)
209
+ throw new Error("Agent history storage handle is closed.");
210
+ }
211
+ }
212
+ //# sourceMappingURL=agent-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-history.js","sourceRoot":"","sources":["../../src/mysql/agent-history.ts"],"names":[],"mappings":"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"}
@@ -52,6 +52,37 @@ export declare class MysqlRecordStorage<I, R extends Message> extends RecordStor
52
52
  * @returns Resolves after table creation and inspection complete.
53
53
  */
54
54
  prepare(): Promise<void>;
55
+ /**
56
+ * Reads a bounded package-local native history page.
57
+ *
58
+ * @param sql Parameterized SQL assembled by the Agent history provider.
59
+ * @param values Bound scope, view, boundary, and limit values.
60
+ * @returns Decoded original record envelopes.
61
+ */
62
+ historyPage(sql: string, values: readonly unknown[]): Promise<readonly R[]>;
63
+ /**
64
+ * Creates one required complete Agent history index when absent.
65
+ *
66
+ * @param name Fixed provider index name.
67
+ * @param columns Fixed complete index columns.
68
+ * @returns Completion after the native index definition is checked.
69
+ */
70
+ ensureHistoryIndex(name: string, columns: readonly string[]): Promise<void>;
71
+ /**
72
+ * Reads complete physical index metadata for one fixed history index.
73
+ *
74
+ * @param connection Active MySQL connection.
75
+ * @param name Fixed required index name.
76
+ * @returns Ordered native index components.
77
+ */
78
+ private historyIndex;
79
+ /**
80
+ * Rejects prefixes, wrong order, or a different native index.
81
+ *
82
+ * @param rows Ordered native index components.
83
+ * @param columns Required complete index columns.
84
+ */
85
+ private assertHistoryIndex;
55
86
  /**
56
87
  * Creates a MySQL record-family storage handle.
57
88
  *
@@ -160,11 +191,13 @@ export declare class MysqlRecordStorage<I, R extends Message> extends RecordStor
160
191
  protected queryRecordEntries(query: RecordQuery<I>): Promise<readonly RecordEntry<I, R>[]>;
161
192
  /**
162
193
  * Returns the normalized query features genuinely executed by MySQL.
194
+ *
163
195
  * @returns The normalized features executed by this provider.
164
196
  */
165
197
  protected queryCapabilities(): StorageQueryCapabilities;
166
198
  /**
167
199
  * Executes the complete candidate selection in contained, bound MySQL SQL.
200
+ *
168
201
  * @param plan The validated normalized query plan.
169
202
  * @returns The selected record entries.
170
203
  */
@@ -1 +1 @@
1
- {"version":3,"file":"record-storage.d.ts","sourceRoot":"","sources":["../../src/mysql/record-storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"record-storage.d.ts","sourceRoot":"","sources":["../../src/mysql/record-storage.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@
12
12
  * the License.
13
13
  */
14
14
  import { fromBinary, ScalarType, toBinary } from "@bufbuild/protobuf";
15
+ import { AsyncLocalStorage } from "node:async_hooks";
15
16
  import { createHash } from "node:crypto";
16
17
  import { StringifierRegistry } from "@spine-event-engine/core";
17
18
  import { ColumnMappings, defaultQueryCandidateLimit, RecordStorage, } from "@spine-event-engine/storage";
@@ -37,7 +38,10 @@ export class MysqlRecordStorage extends RecordStorage {
37
38
  */
38
39
  atomicCompareAndSet = true;
39
40
  #ready;
40
- #bound;
41
+ /**
42
+ * Keeps transaction connections separate across concurrent asynchronous calls.
43
+ */
44
+ #bound = new AsyncLocalStorage();
41
45
  #idColumn;
42
46
  #columnMapping;
43
47
  /**
@@ -56,6 +60,82 @@ export class MysqlRecordStorage extends RecordStorage {
56
60
  async prepare() {
57
61
  await this.using(() => Promise.resolve());
58
62
  }
63
+ /**
64
+ * Reads a bounded package-local native history page.
65
+ *
66
+ * @param sql Parameterized SQL assembled by the Agent history provider.
67
+ * @param values Bound scope, view, boundary, and limit values.
68
+ * @returns Decoded original record envelopes.
69
+ */
70
+ async historyPage(sql, values) {
71
+ return this.using(async (connection) => {
72
+ const [rows] = await connection.query(sql, [...values]);
73
+ try {
74
+ return rows.map((row) => fromBinary(this.recordSpec.recordType, row.bytes));
75
+ }
76
+ catch (error) {
77
+ throw mysqlError(MysqlStorageDataError, "Stored MySQL history data is invalid.", error);
78
+ }
79
+ });
80
+ }
81
+ /**
82
+ * Creates one required complete Agent history index when absent.
83
+ *
84
+ * @param name Fixed provider index name.
85
+ * @param columns Fixed complete index columns.
86
+ * @returns Completion after the native index definition is checked.
87
+ */
88
+ async ensureHistoryIndex(name, columns) {
89
+ await this.using(async (connection) => {
90
+ const existing = await this.historyIndex(connection, name);
91
+ if (existing.length !== 0) {
92
+ this.assertHistoryIndex(existing, columns);
93
+ return;
94
+ }
95
+ if (!/^[a-z_]+$/u.test(name) || columns.some((column) => !/^[a-z_]+$/u.test(column)))
96
+ throw new MysqlStorageSchemaError("MySQL Agent history index name is invalid.");
97
+ const quoted = columns.map((column) => `\`${column}\``).join(", ");
98
+ try {
99
+ await connection.query(`CREATE INDEX \`${name}\` ON \`${this.tableName}\` (${quoted})`);
100
+ }
101
+ catch (error) {
102
+ if (error.code !== "ER_DUP_KEYNAME")
103
+ throw error;
104
+ }
105
+ this.assertHistoryIndex(await this.historyIndex(connection, name), columns);
106
+ });
107
+ }
108
+ /**
109
+ * Reads complete physical index metadata for one fixed history index.
110
+ *
111
+ * @param connection Active MySQL connection.
112
+ * @param name Fixed required index name.
113
+ * @returns Ordered native index components.
114
+ */
115
+ async historyIndex(connection, name) {
116
+ const [rows] = await connection.query("SELECT index_name AS index_name, non_unique AS non_unique, " +
117
+ "column_name AS column_name, seq_in_index AS seq_in_index, " +
118
+ "sub_part AS sub_part, collation AS collation, index_type AS index_type " +
119
+ "FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name=? " +
120
+ "AND index_name=? ORDER BY seq_in_index", [this.tableName, name]);
121
+ return rows;
122
+ }
123
+ /**
124
+ * Rejects prefixes, wrong order, or a different native index.
125
+ *
126
+ * @param rows Ordered native index components.
127
+ * @param columns Required complete index columns.
128
+ */
129
+ assertHistoryIndex(rows, columns) {
130
+ if (rows.length !== columns.length ||
131
+ rows.some((row, index) => row.column_name !== columns[index] ||
132
+ Number(row.seq_in_index) !== index + 1 ||
133
+ row.sub_part !== null ||
134
+ Number(row.non_unique) !== 1 ||
135
+ row.collation !== "A" ||
136
+ row.index_type !== "BTREE"))
137
+ throw new MysqlStorageSchemaError("MySQL Agent history index is incompatible.");
138
+ }
59
139
  /**
60
140
  * Creates a MySQL record-family storage handle.
61
141
  *
@@ -189,17 +269,16 @@ export class MysqlRecordStorage extends RecordStorage {
189
269
  * @returns Returns the work result.
190
270
  */
191
271
  async withConnection(connection, work) {
192
- if (this.#bound !== undefined) {
272
+ const bound = this.#bound.getStore();
273
+ if (bound !== undefined) {
274
+ if (bound !== connection)
275
+ throw new Error("MySQL record handle is bound to another transaction.");
193
276
  return work();
194
277
  }
195
- this.#bound = connection;
196
- try {
278
+ return this.#bound.run(connection, async () => {
197
279
  await this.ready(connection);
198
280
  return await work();
199
- }
200
- finally {
201
- this.#bound = undefined;
202
- }
281
+ });
203
282
  }
204
283
  /**
205
284
  * Reads one record with the coordinator transaction's row/gap lock.
@@ -257,6 +336,7 @@ export class MysqlRecordStorage extends RecordStorage {
257
336
  }
258
337
  /**
259
338
  * Returns the normalized query features genuinely executed by MySQL.
339
+ *
260
340
  * @returns The normalized features executed by this provider.
261
341
  */
262
342
  queryCapabilities() {
@@ -267,6 +347,7 @@ export class MysqlRecordStorage extends RecordStorage {
267
347
  }
268
348
  /**
269
349
  * Executes the complete candidate selection in contained, bound MySQL SQL.
350
+ *
270
351
  * @param plan The validated normalized query plan.
271
352
  * @returns The selected record entries.
272
353
  */
@@ -419,7 +500,7 @@ export class MysqlRecordStorage extends RecordStorage {
419
500
  * @returns Resolves to the operation result.
420
501
  */
421
502
  async using(work) {
422
- const bound = this.#bound;
503
+ const bound = this.#bound.getStore();
423
504
  if (bound !== undefined) {
424
505
  return work(bound);
425
506
  }
@@ -1034,10 +1115,10 @@ function groupedIndexes(indexes) {
1034
1115
  for (const index of indexes) {
1035
1116
  const existing = grouped.get(index.index_name) ?? {
1036
1117
  name: index.index_name,
1037
- nonUnique: index.non_unique,
1118
+ nonUnique: Number(index.non_unique),
1038
1119
  columns: [],
1039
1120
  };
1040
- existing.columns[index.seq_in_index - 1] = index.column_name;
1121
+ existing.columns[Number(index.seq_in_index) - 1] = index.column_name;
1041
1122
  grouped.set(index.index_name, existing);
1042
1123
  }
1043
1124
  return [...grouped.values()];