@stonyx/orm 0.3.2-beta.9 → 0.3.2-beta.91
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 +35 -2
- package/config/environment.js +99 -12
- package/dist/commands.js +34 -0
- package/dist/dynamodb/connection.d.ts +31 -0
- package/dist/dynamodb/connection.js +28 -0
- package/dist/dynamodb/dynamodb-db.d.ts +142 -0
- package/dist/dynamodb/dynamodb-db.js +596 -0
- package/dist/dynamodb/operation-builder.d.ts +76 -0
- package/dist/dynamodb/operation-builder.js +116 -0
- package/dist/dynamodb/type-map.d.ts +31 -0
- package/dist/dynamodb/type-map.js +48 -0
- package/dist/main.js +10 -0
- package/dist/manage-record.js +34 -3
- package/dist/mysql/mysql-db.d.ts +8 -0
- package/dist/mysql/mysql-db.js +22 -8
- package/dist/orm-request.js +7 -6
- package/dist/postgres/connection.d.ts +1 -0
- package/dist/postgres/connection.js +8 -6
- package/dist/postgres/postgres-db.d.ts +8 -0
- package/dist/postgres/postgres-db.js +22 -8
- package/dist/relationships.js +1 -1
- package/dist/serializer.js +38 -2
- package/dist/store.d.ts +13 -1
- package/dist/store.js +65 -5
- package/dist/types/orm-types.d.ts +9 -0
- package/package.json +16 -7
- package/src/commands.ts +43 -0
- package/src/dynamodb/connection.ts +50 -0
- package/src/dynamodb/dynamodb-db.ts +811 -0
- package/src/dynamodb/operation-builder.ts +202 -0
- package/src/dynamodb/type-map.ts +54 -0
- package/src/main.ts +10 -0
- package/src/manage-record.ts +41 -9
- package/src/mysql/mysql-db.ts +24 -8
- package/src/orm-request.ts +8 -5
- package/src/postgres/connection.ts +10 -6
- package/src/postgres/postgres-db.ts +24 -8
- package/src/relationships.ts +1 -1
- package/src/serializer.ts +39 -2
- package/src/store.ts +68 -5
- package/src/types/orm-types.ts +10 -0
- package/src/types/stonyx.d.ts +7 -1
- package/config/environment.ts +0 -91
package/src/store.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Orm, { relationships } from '@stonyx/orm';
|
|
2
|
-
import { TYPES, getHasManyRegistry, getBelongsToRegistry, getPendingRegistry } from './relationships.js';
|
|
2
|
+
import { TYPES, getHasManyRegistry, getBelongsToRegistry, getPendingRegistry, getPendingBelongsToRegistry } from './relationships.js';
|
|
3
3
|
import ViewResolver from './view-resolver.js';
|
|
4
4
|
|
|
5
5
|
interface UnloadOptions {
|
|
@@ -64,7 +64,7 @@ export default class Store {
|
|
|
64
64
|
get(key: string): Map<number | string, unknown> | undefined;
|
|
65
65
|
get(key: string, id: number | string): unknown;
|
|
66
66
|
get(key: string, id?: number | string): Map<number | string, unknown> | unknown | undefined {
|
|
67
|
-
if (
|
|
67
|
+
if (id === undefined) return this.data.get(key);
|
|
68
68
|
|
|
69
69
|
return this.data.get(key)?.get(id);
|
|
70
70
|
}
|
|
@@ -170,14 +170,15 @@ export default class Store {
|
|
|
170
170
|
this.data.set(key, value);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
remove(key: string, id?: number | string): void {
|
|
173
|
+
remove(key: string, id?: number | string, options?: { _skipAutoPersist?: boolean }): void {
|
|
174
174
|
// Guard: read-only views cannot have records removed
|
|
175
175
|
if (Orm.instance?.isView?.(key)) {
|
|
176
176
|
throw new Error(`Cannot remove records from read-only view '${key}'`);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
// Auto-persist delete to SQL
|
|
180
|
-
|
|
179
|
+
// Auto-persist delete to SQL (fire-and-forget) — skipped when the
|
|
180
|
+
// request path handles persist itself to avoid double-delete.
|
|
181
|
+
if (id && Orm.instance?.sqlDb && !options?._skipAutoPersist) {
|
|
181
182
|
Orm.instance.sqlDb.persist('delete', key, { recordId: id }, {}).catch((err: unknown) => {
|
|
182
183
|
Orm.instance.emitPersistError({
|
|
183
184
|
operation: 'delete',
|
|
@@ -193,6 +194,44 @@ export default class Store {
|
|
|
193
194
|
this.unloadAllRecords(key);
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Evict a record from the store with full relationship registry cleanup,
|
|
199
|
+
* WITHOUT calling record.clean(). This preserves the caller's reference
|
|
200
|
+
* to the returned record (used by memory:false post-persist eviction).
|
|
201
|
+
*
|
|
202
|
+
* @param registryId - The ID used when the record's relationships were
|
|
203
|
+
* registered. For SQL models with pending IDs, this is the original
|
|
204
|
+
* negative pending ID (before the adapter re-keyed to the real DB ID).
|
|
205
|
+
*/
|
|
206
|
+
evictRecord(modelName: string, id: unknown, registryId?: unknown): void {
|
|
207
|
+
const modelStore = this.data.get(modelName);
|
|
208
|
+
if (!modelStore) return;
|
|
209
|
+
|
|
210
|
+
if (typeof id !== 'string' && typeof id !== 'number') return;
|
|
211
|
+
const raw = modelStore.get(id);
|
|
212
|
+
if (!raw || !isStoreRecord(raw)) return;
|
|
213
|
+
|
|
214
|
+
const visited = new Set([`${modelName}:${id}`]);
|
|
215
|
+
|
|
216
|
+
// Remove from hasMany arrays and nullify belongsTo references using current ID
|
|
217
|
+
// (the adapter updates record.id, so value-based matches need the current ID)
|
|
218
|
+
this._removeFromHasManyArrays(modelName, id, visited);
|
|
219
|
+
this._nullifyBelongsToReferences(modelName, id, visited);
|
|
220
|
+
|
|
221
|
+
// Clean up relationship registry entries using the registry key
|
|
222
|
+
// (belongsTo/hasMany registries were keyed by the ID at registration time,
|
|
223
|
+
// which may differ from the current ID if SQL persist re-keyed the record)
|
|
224
|
+
const cleanupId = registryId ?? id;
|
|
225
|
+
this._cleanupRelationshipRegistries(modelName, cleanupId);
|
|
226
|
+
|
|
227
|
+
// If registryId differs from id, also clean with current id as safety net
|
|
228
|
+
if (registryId !== undefined && registryId !== id) {
|
|
229
|
+
this._cleanupRelationshipRegistries(modelName, id);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
modelStore.delete(id);
|
|
233
|
+
}
|
|
234
|
+
|
|
196
235
|
unloadRecord(model: string, id: unknown, options: UnloadOptions = {}): void {
|
|
197
236
|
const modelStore = this.data.get(model);
|
|
198
237
|
|
|
@@ -308,6 +347,30 @@ export default class Store {
|
|
|
308
347
|
|
|
309
348
|
const pendingMap = getPendingRegistry().get(modelName);
|
|
310
349
|
if (pendingMap) pendingMap.delete(recordId);
|
|
350
|
+
|
|
351
|
+
// Clean pendingBelongsTo entries in both directions
|
|
352
|
+
const pendingBelongsToMap = getPendingBelongsToRegistry();
|
|
353
|
+
if (pendingBelongsToMap) {
|
|
354
|
+
// Direction 1: evicted record was the TARGET others were waiting for
|
|
355
|
+
const targetEntries = pendingBelongsToMap.get(modelName);
|
|
356
|
+
if (targetEntries) targetEntries.delete(recordId);
|
|
357
|
+
|
|
358
|
+
// Direction 2: evicted record was the SOURCE with unresolved forward-references
|
|
359
|
+
for (const [, targetIdMap] of pendingBelongsToMap) {
|
|
360
|
+
for (const [targetId, entries] of targetIdMap) {
|
|
361
|
+
if (!Array.isArray(entries)) continue;
|
|
362
|
+
const filtered = entries.filter((e: unknown) => {
|
|
363
|
+
const entry = e as { sourceModelName?: string; relationshipId?: unknown };
|
|
364
|
+
return !(entry.sourceModelName === modelName && entry.relationshipId === recordId);
|
|
365
|
+
});
|
|
366
|
+
if (filtered.length === 0) {
|
|
367
|
+
targetIdMap.delete(targetId);
|
|
368
|
+
} else if (filtered.length < entries.length) {
|
|
369
|
+
targetIdMap.set(targetId, filtered);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
311
374
|
}
|
|
312
375
|
|
|
313
376
|
/**
|
package/src/types/orm-types.ts
CHANGED
|
@@ -48,6 +48,13 @@ export interface OrmRestServerConfig {
|
|
|
48
48
|
metaRoute: boolean;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
export interface OrmDynamoDBConfig {
|
|
52
|
+
region?: string;
|
|
53
|
+
endpoint?: string;
|
|
54
|
+
tablePrefix?: string;
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
export interface OrmSection {
|
|
52
59
|
db: OrmDbConfig;
|
|
53
60
|
paths: OrmPaths;
|
|
@@ -55,6 +62,9 @@ export interface OrmSection {
|
|
|
55
62
|
mysql?: OrmMysqlConfig;
|
|
56
63
|
postgres?: OrmPostgresConfig;
|
|
57
64
|
timescale?: OrmPostgresConfig;
|
|
65
|
+
dynamodb?: OrmDynamoDBConfig;
|
|
66
|
+
logColor?: string;
|
|
67
|
+
logMethod?: string;
|
|
58
68
|
[key: string]: unknown;
|
|
59
69
|
}
|
|
60
70
|
|
package/src/types/stonyx.d.ts
CHANGED
|
@@ -5,7 +5,13 @@ declare module 'stonyx/config' {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
declare module 'stonyx/log' {
|
|
8
|
-
|
|
8
|
+
interface Log {
|
|
9
|
+
db(message: string): void;
|
|
10
|
+
error(message: string, ...args: unknown[]): void;
|
|
11
|
+
defineType(type: string, setting: string, options?: Record<string, unknown> | null): void;
|
|
12
|
+
[key: string]: ((...args: unknown[]) => void) | undefined;
|
|
13
|
+
}
|
|
14
|
+
const log: Log;
|
|
9
15
|
export default log;
|
|
10
16
|
}
|
|
11
17
|
|
package/config/environment.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
ORM_ACCESS_PATH,
|
|
3
|
-
ORM_MODEL_PATH,
|
|
4
|
-
ORM_REST_ROUTE,
|
|
5
|
-
ORM_SERIALIZER_PATH,
|
|
6
|
-
ORM_TRANSFORM_PATH,
|
|
7
|
-
ORM_VIEW_PATH,
|
|
8
|
-
ORM_USE_REST_SERVER,
|
|
9
|
-
DB_AUTO_SAVE,
|
|
10
|
-
DB_FILE,
|
|
11
|
-
DB_MODE,
|
|
12
|
-
DB_DIRECTORY,
|
|
13
|
-
DB_SCHEMA_PATH,
|
|
14
|
-
DB_SAVE_INTERVAL,
|
|
15
|
-
MYSQL_HOST,
|
|
16
|
-
MYSQL_PORT,
|
|
17
|
-
MYSQL_USER,
|
|
18
|
-
MYSQL_PASSWORD,
|
|
19
|
-
MYSQL_DATABASE,
|
|
20
|
-
MYSQL_CONNECTION_LIMIT,
|
|
21
|
-
MYSQL_MIGRATIONS_DIR,
|
|
22
|
-
PG_HOST,
|
|
23
|
-
PG_PORT,
|
|
24
|
-
PG_USER,
|
|
25
|
-
PG_PASSWORD,
|
|
26
|
-
PG_DATABASE,
|
|
27
|
-
PG_CONNECTION_LIMIT,
|
|
28
|
-
PG_MIGRATIONS_DIR,
|
|
29
|
-
TIMESCALE_HOST,
|
|
30
|
-
TIMESCALE_PORT,
|
|
31
|
-
TIMESCALE_USER,
|
|
32
|
-
TIMESCALE_PASSWORD,
|
|
33
|
-
TIMESCALE_DATABASE,
|
|
34
|
-
TIMESCALE_CONNECTION_LIMIT,
|
|
35
|
-
TIMESCALE_MIGRATIONS_DIR,
|
|
36
|
-
} = process.env;
|
|
37
|
-
|
|
38
|
-
export default {
|
|
39
|
-
logColor: 'white',
|
|
40
|
-
logMethod: 'db',
|
|
41
|
-
|
|
42
|
-
db: {
|
|
43
|
-
autosave: DB_AUTO_SAVE ?? 'false', // 'true' (cron interval), 'false' (disabled), 'onUpdate' (save after each write op)
|
|
44
|
-
file: DB_FILE ?? 'db.json',
|
|
45
|
-
mode: DB_MODE ?? 'file', // 'file' (single db.json) or 'directory' (one file per collection)
|
|
46
|
-
directory: DB_DIRECTORY ?? 'db', // directory name for collection files when mode is 'directory'
|
|
47
|
-
saveInterval: DB_SAVE_INTERVAL ?? 60 * 60, // 1 hour
|
|
48
|
-
schema: DB_SCHEMA_PATH ?? './config/db-schema.js'
|
|
49
|
-
},
|
|
50
|
-
paths: {
|
|
51
|
-
access: ORM_ACCESS_PATH ?? './access', // Optional for restServer access hooks
|
|
52
|
-
model: ORM_MODEL_PATH ?? './models',
|
|
53
|
-
serializer: ORM_SERIALIZER_PATH ?? './serializers',
|
|
54
|
-
transform: ORM_TRANSFORM_PATH ?? './transforms',
|
|
55
|
-
view: ORM_VIEW_PATH ?? './views'
|
|
56
|
-
},
|
|
57
|
-
mysql: MYSQL_HOST ? {
|
|
58
|
-
host: MYSQL_HOST ?? 'localhost',
|
|
59
|
-
port: parseInt(MYSQL_PORT ?? '3306'),
|
|
60
|
-
user: MYSQL_USER ?? 'root',
|
|
61
|
-
password: MYSQL_PASSWORD ?? '',
|
|
62
|
-
database: MYSQL_DATABASE ?? 'stonyx',
|
|
63
|
-
connectionLimit: parseInt(MYSQL_CONNECTION_LIMIT ?? '10'),
|
|
64
|
-
migrationsDir: MYSQL_MIGRATIONS_DIR ?? 'migrations',
|
|
65
|
-
migrationsTable: '__migrations',
|
|
66
|
-
} : undefined,
|
|
67
|
-
postgres: PG_HOST ? {
|
|
68
|
-
host: PG_HOST ?? 'localhost',
|
|
69
|
-
port: parseInt(PG_PORT ?? '5432'),
|
|
70
|
-
user: PG_USER ?? 'postgres',
|
|
71
|
-
password: PG_PASSWORD ?? '',
|
|
72
|
-
database: PG_DATABASE ?? 'stonyx',
|
|
73
|
-
connectionLimit: parseInt(PG_CONNECTION_LIMIT ?? '10'),
|
|
74
|
-
migrationsDir: PG_MIGRATIONS_DIR ?? 'migrations',
|
|
75
|
-
migrationsTable: '__migrations',
|
|
76
|
-
} : undefined,
|
|
77
|
-
timescale: TIMESCALE_HOST ? {
|
|
78
|
-
host: TIMESCALE_HOST ?? 'localhost',
|
|
79
|
-
port: parseInt(TIMESCALE_PORT ?? '5432'),
|
|
80
|
-
user: TIMESCALE_USER ?? 'postgres',
|
|
81
|
-
password: TIMESCALE_PASSWORD ?? '',
|
|
82
|
-
database: TIMESCALE_DATABASE ?? 'stonyx',
|
|
83
|
-
connectionLimit: parseInt(TIMESCALE_CONNECTION_LIMIT ?? '10'),
|
|
84
|
-
migrationsDir: TIMESCALE_MIGRATIONS_DIR ?? 'migrations',
|
|
85
|
-
migrationsTable: '__migrations',
|
|
86
|
-
} : undefined,
|
|
87
|
-
restServer: {
|
|
88
|
-
enabled: ORM_USE_REST_SERVER ?? 'true', // Whether to load restServer for automatic route setup or
|
|
89
|
-
route: ORM_REST_ROUTE ?? '/',
|
|
90
|
-
}
|
|
91
|
-
}
|