@jaypie/dynamodb 0.2.0 → 0.4.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/dist/cjs/constants.d.ts +2 -2
- package/dist/cjs/entities.d.ts +6 -6
- package/dist/cjs/index.cjs +112 -106
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/keyBuilders.d.ts +21 -21
- package/dist/cjs/mcp/admin/createTable.d.ts +5 -1
- package/dist/cjs/mcp/admin/dockerCompose.d.ts +9 -1
- package/dist/cjs/mcp/admin/status.d.ts +6 -1
- package/dist/cjs/mcp/index.cjs +143 -121
- package/dist/cjs/mcp/index.cjs.map +1 -1
- package/dist/cjs/queries.d.ts +15 -15
- package/dist/cjs/query.d.ts +7 -7
- package/dist/cjs/seedExport.d.ts +7 -7
- package/dist/cjs/types.d.ts +20 -20
- package/dist/esm/constants.d.ts +2 -2
- package/dist/esm/entities.d.ts +6 -6
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +91 -85
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/keyBuilders.d.ts +21 -21
- package/dist/esm/mcp/admin/createTable.d.ts +5 -1
- package/dist/esm/mcp/admin/dockerCompose.d.ts +9 -1
- package/dist/esm/mcp/admin/status.d.ts +6 -1
- package/dist/esm/mcp/index.js +133 -111
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/queries.d.ts +15 -15
- package/dist/esm/query.d.ts +7 -7
- package/dist/esm/seedExport.d.ts +7 -7
- package/dist/esm/types.d.ts +20 -20
- package/package.json +5 -5
package/dist/cjs/constants.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { APEX, ARCHIVED_SUFFIX, DELETED_SUFFIX, SEPARATOR } from "@jaypie/
|
|
1
|
+
export { APEX, ARCHIVED_SUFFIX, DELETED_SUFFIX, SEPARATOR, } from "@jaypie/fabric";
|
|
2
2
|
export declare const INDEX_ALIAS = "indexAlias";
|
|
3
3
|
export declare const INDEX_CLASS = "indexClass";
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const INDEX_SCOPE = "indexScope";
|
|
5
5
|
export declare const INDEX_TYPE = "indexType";
|
|
6
6
|
export declare const INDEX_XID = "indexXid";
|
package/dist/cjs/entities.d.ts
CHANGED
|
@@ -2,12 +2,12 @@ import type { StorableEntity } from "./types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Get a single entity by primary key
|
|
4
4
|
*/
|
|
5
|
-
export declare const getEntity: import("@jaypie/
|
|
5
|
+
export declare const getEntity: import("@jaypie/fabric").Service<Record<string, unknown>, StorableEntity | null, StorableEntity | null>;
|
|
6
6
|
/**
|
|
7
7
|
* Put (create or replace) an entity
|
|
8
8
|
* Auto-populates GSI index keys via indexEntity
|
|
9
9
|
*
|
|
10
|
-
* Note: This is a regular async function (not
|
|
10
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
11
11
|
* complex StorableEntity objects that can't be coerced by vocabulary's type system.
|
|
12
12
|
*/
|
|
13
13
|
export declare function putEntity({ entity, }: {
|
|
@@ -17,7 +17,7 @@ export declare function putEntity({ entity, }: {
|
|
|
17
17
|
* Update an existing entity
|
|
18
18
|
* Auto-populates GSI index keys and sets updatedAt
|
|
19
19
|
*
|
|
20
|
-
* Note: This is a regular async function (not
|
|
20
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
21
21
|
* complex StorableEntity objects that can't be coerced by vocabulary's type system.
|
|
22
22
|
*/
|
|
23
23
|
export declare function updateEntity({ entity, }: {
|
|
@@ -27,14 +27,14 @@ export declare function updateEntity({ entity, }: {
|
|
|
27
27
|
* Soft delete an entity by setting deletedAt timestamp
|
|
28
28
|
* Re-indexes with appropriate suffix based on archived/deleted state
|
|
29
29
|
*/
|
|
30
|
-
export declare const deleteEntity: import("@jaypie/
|
|
30
|
+
export declare const deleteEntity: import("@jaypie/fabric").Service<Record<string, unknown>, boolean, boolean>;
|
|
31
31
|
/**
|
|
32
32
|
* Archive an entity by setting archivedAt timestamp
|
|
33
33
|
* Re-indexes with appropriate suffix based on archived/deleted state
|
|
34
34
|
*/
|
|
35
|
-
export declare const archiveEntity: import("@jaypie/
|
|
35
|
+
export declare const archiveEntity: import("@jaypie/fabric").Service<Record<string, unknown>, boolean, boolean>;
|
|
36
36
|
/**
|
|
37
37
|
* Hard delete an entity (permanently removes from table)
|
|
38
38
|
* Use with caution - prefer deleteEntity for soft delete
|
|
39
39
|
*/
|
|
40
|
-
export declare const destroyEntity: import("@jaypie/
|
|
40
|
+
export declare const destroyEntity: import("@jaypie/fabric").Service<Record<string, unknown>, boolean, boolean>;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var clientDynamodb = require('@aws-sdk/client-dynamodb');
|
|
4
4
|
var libDynamodb = require('@aws-sdk/lib-dynamodb');
|
|
5
5
|
var errors = require('@jaypie/errors');
|
|
6
|
-
var
|
|
6
|
+
var fabric = require('@jaypie/fabric');
|
|
7
7
|
|
|
8
8
|
// Environment variable names
|
|
9
9
|
const ENV_AWS_REGION = "AWS_REGION";
|
|
@@ -83,11 +83,11 @@ function resetClient() {
|
|
|
83
83
|
tableName = null;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// Re-export shared constants from
|
|
86
|
+
// Re-export shared constants from fabric
|
|
87
87
|
// GSI names (derived from DEFAULT_INDEXES)
|
|
88
88
|
const INDEX_ALIAS = "indexAlias";
|
|
89
89
|
const INDEX_CLASS = "indexClass";
|
|
90
|
-
const
|
|
90
|
+
const INDEX_SCOPE = "indexScope";
|
|
91
91
|
const INDEX_TYPE = "indexType";
|
|
92
92
|
const INDEX_XID = "indexXid";
|
|
93
93
|
|
|
@@ -95,53 +95,53 @@ const INDEX_XID = "indexXid";
|
|
|
95
95
|
// Key Builders
|
|
96
96
|
// =============================================================================
|
|
97
97
|
/**
|
|
98
|
-
* Build the
|
|
99
|
-
* @param
|
|
98
|
+
* Build the indexScope key for hierarchical queries
|
|
99
|
+
* @param scope - The scope (APEX or "{parent.model}#{parent.id}")
|
|
100
100
|
* @param model - The entity model name
|
|
101
|
-
* @returns Composite key: "{
|
|
101
|
+
* @returns Composite key: "{scope}#{model}"
|
|
102
102
|
*/
|
|
103
|
-
function
|
|
104
|
-
return `${
|
|
103
|
+
function buildIndexScope(scope, model) {
|
|
104
|
+
return `${scope}${fabric.SEPARATOR}${model}`;
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
107
|
* Build the indexAlias key for human-friendly lookups
|
|
108
|
-
* @param
|
|
108
|
+
* @param scope - The scope
|
|
109
109
|
* @param model - The entity model name
|
|
110
110
|
* @param alias - The human-friendly alias
|
|
111
|
-
* @returns Composite key: "{
|
|
111
|
+
* @returns Composite key: "{scope}#{model}#{alias}"
|
|
112
112
|
*/
|
|
113
|
-
function buildIndexAlias(
|
|
114
|
-
return `${
|
|
113
|
+
function buildIndexAlias(scope, model, alias) {
|
|
114
|
+
return `${scope}${fabric.SEPARATOR}${model}${fabric.SEPARATOR}${alias}`;
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
117
|
* Build the indexClass key for category filtering
|
|
118
|
-
* @param
|
|
118
|
+
* @param scope - The scope
|
|
119
119
|
* @param model - The entity model name
|
|
120
120
|
* @param recordClass - The category classification
|
|
121
|
-
* @returns Composite key: "{
|
|
121
|
+
* @returns Composite key: "{scope}#{model}#{class}"
|
|
122
122
|
*/
|
|
123
|
-
function buildIndexClass(
|
|
124
|
-
return `${
|
|
123
|
+
function buildIndexClass(scope, model, recordClass) {
|
|
124
|
+
return `${scope}${fabric.SEPARATOR}${model}${fabric.SEPARATOR}${recordClass}`;
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
127
|
* Build the indexType key for type filtering
|
|
128
|
-
* @param
|
|
128
|
+
* @param scope - The scope
|
|
129
129
|
* @param model - The entity model name
|
|
130
130
|
* @param type - The type classification
|
|
131
|
-
* @returns Composite key: "{
|
|
131
|
+
* @returns Composite key: "{scope}#{model}#{type}"
|
|
132
132
|
*/
|
|
133
|
-
function buildIndexType(
|
|
134
|
-
return `${
|
|
133
|
+
function buildIndexType(scope, model, type) {
|
|
134
|
+
return `${scope}${fabric.SEPARATOR}${model}${fabric.SEPARATOR}${type}`;
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* Build the indexXid key for external ID lookups
|
|
138
|
-
* @param
|
|
138
|
+
* @param scope - The scope
|
|
139
139
|
* @param model - The entity model name
|
|
140
140
|
* @param xid - The external ID
|
|
141
|
-
* @returns Composite key: "{
|
|
141
|
+
* @returns Composite key: "{scope}#{model}#{xid}"
|
|
142
142
|
*/
|
|
143
|
-
function buildIndexXid(
|
|
144
|
-
return `${
|
|
143
|
+
function buildIndexXid(scope, model, xid) {
|
|
144
|
+
return `${scope}${fabric.SEPARATOR}${model}${fabric.SEPARATOR}${xid}`;
|
|
145
145
|
}
|
|
146
146
|
// =============================================================================
|
|
147
147
|
// New Vocabulary-Based Functions
|
|
@@ -155,18 +155,18 @@ function buildIndexXid(ou, model, xid) {
|
|
|
155
155
|
* @returns Composite key string
|
|
156
156
|
*/
|
|
157
157
|
function buildCompositeKey(entity, fields, suffix) {
|
|
158
|
-
return
|
|
158
|
+
return fabric.buildCompositeKey(entity, fields, suffix);
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
|
-
* Calculate the
|
|
161
|
+
* Calculate the scope from a parent reference
|
|
162
162
|
* @param parent - Optional parent entity reference
|
|
163
163
|
* @returns APEX ("@") if no parent, otherwise "{parent.model}#{parent.id}"
|
|
164
164
|
*/
|
|
165
|
-
function
|
|
165
|
+
function calculateScope(parent) {
|
|
166
166
|
if (!parent) {
|
|
167
|
-
return
|
|
167
|
+
return fabric.APEX;
|
|
168
168
|
}
|
|
169
|
-
return
|
|
169
|
+
return fabric.calculateScope(parent);
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
172
172
|
* Auto-populate GSI index keys on an entity
|
|
@@ -174,7 +174,7 @@ function calculateOu(parent) {
|
|
|
174
174
|
* Uses the model's registered indexes (from vocabulary registry) or
|
|
175
175
|
* DEFAULT_INDEXES if no custom indexes are registered.
|
|
176
176
|
*
|
|
177
|
-
* -
|
|
177
|
+
* - indexScope is always populated from scope + model
|
|
178
178
|
* - indexAlias is populated only when alias is present
|
|
179
179
|
* - indexClass is populated only when class is present
|
|
180
180
|
* - indexType is populated only when type is present
|
|
@@ -185,9 +185,9 @@ function calculateOu(parent) {
|
|
|
185
185
|
* @returns The entity with populated index keys
|
|
186
186
|
*/
|
|
187
187
|
function indexEntity(entity, suffix = "") {
|
|
188
|
-
const indexes =
|
|
189
|
-
// Cast through unknown to bridge the type gap between StorableEntity and
|
|
190
|
-
return
|
|
188
|
+
const indexes = fabric.getModelIndexes(entity.model);
|
|
189
|
+
// Cast through unknown to bridge the type gap between StorableEntity and IndexableModel
|
|
190
|
+
return fabric.populateIndexKeys(entity, indexes, suffix);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
/**
|
|
@@ -197,20 +197,20 @@ function calculateEntitySuffix(entity) {
|
|
|
197
197
|
const hasArchived = Boolean(entity.archivedAt);
|
|
198
198
|
const hasDeleted = Boolean(entity.deletedAt);
|
|
199
199
|
if (hasArchived && hasDeleted) {
|
|
200
|
-
return
|
|
200
|
+
return fabric.ARCHIVED_SUFFIX + fabric.DELETED_SUFFIX;
|
|
201
201
|
}
|
|
202
202
|
if (hasArchived) {
|
|
203
|
-
return
|
|
203
|
+
return fabric.ARCHIVED_SUFFIX;
|
|
204
204
|
}
|
|
205
205
|
if (hasDeleted) {
|
|
206
|
-
return
|
|
206
|
+
return fabric.DELETED_SUFFIX;
|
|
207
207
|
}
|
|
208
208
|
return "";
|
|
209
209
|
}
|
|
210
210
|
/**
|
|
211
211
|
* Get a single entity by primary key
|
|
212
212
|
*/
|
|
213
|
-
const getEntity =
|
|
213
|
+
const getEntity = fabric.fabricService({
|
|
214
214
|
alias: "getEntity",
|
|
215
215
|
description: "Get a single entity by primary key",
|
|
216
216
|
input: {
|
|
@@ -232,7 +232,7 @@ const getEntity = vocabulary.serviceHandler({
|
|
|
232
232
|
* Put (create or replace) an entity
|
|
233
233
|
* Auto-populates GSI index keys via indexEntity
|
|
234
234
|
*
|
|
235
|
-
* Note: This is a regular async function (not
|
|
235
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
236
236
|
* complex StorableEntity objects that can't be coerced by vocabulary's type system.
|
|
237
237
|
*/
|
|
238
238
|
async function putEntity({ entity, }) {
|
|
@@ -251,7 +251,7 @@ async function putEntity({ entity, }) {
|
|
|
251
251
|
* Update an existing entity
|
|
252
252
|
* Auto-populates GSI index keys and sets updatedAt
|
|
253
253
|
*
|
|
254
|
-
* Note: This is a regular async function (not
|
|
254
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
255
255
|
* complex StorableEntity objects that can't be coerced by vocabulary's type system.
|
|
256
256
|
*/
|
|
257
257
|
async function updateEntity({ entity, }) {
|
|
@@ -273,7 +273,7 @@ async function updateEntity({ entity, }) {
|
|
|
273
273
|
* Soft delete an entity by setting deletedAt timestamp
|
|
274
274
|
* Re-indexes with appropriate suffix based on archived/deleted state
|
|
275
275
|
*/
|
|
276
|
-
const deleteEntity =
|
|
276
|
+
const deleteEntity = fabric.fabricService({
|
|
277
277
|
alias: "deleteEntity",
|
|
278
278
|
description: "Soft delete an entity (sets deletedAt timestamp)",
|
|
279
279
|
input: {
|
|
@@ -310,7 +310,7 @@ const deleteEntity = vocabulary.serviceHandler({
|
|
|
310
310
|
* Archive an entity by setting archivedAt timestamp
|
|
311
311
|
* Re-indexes with appropriate suffix based on archived/deleted state
|
|
312
312
|
*/
|
|
313
|
-
const archiveEntity =
|
|
313
|
+
const archiveEntity = fabric.fabricService({
|
|
314
314
|
alias: "archiveEntity",
|
|
315
315
|
description: "Archive an entity (sets archivedAt timestamp)",
|
|
316
316
|
input: {
|
|
@@ -347,7 +347,7 @@ const archiveEntity = vocabulary.serviceHandler({
|
|
|
347
347
|
* Hard delete an entity (permanently removes from table)
|
|
348
348
|
* Use with caution - prefer deleteEntity for soft delete
|
|
349
349
|
*/
|
|
350
|
-
const destroyEntity =
|
|
350
|
+
const destroyEntity = fabric.fabricService({
|
|
351
351
|
alias: "destroyEntity",
|
|
352
352
|
description: "Hard delete an entity (permanently removes from table)",
|
|
353
353
|
input: {
|
|
@@ -372,13 +372,13 @@ const destroyEntity = vocabulary.serviceHandler({
|
|
|
372
372
|
*/
|
|
373
373
|
function calculateSuffix$1({ archived, deleted, }) {
|
|
374
374
|
if (archived && deleted) {
|
|
375
|
-
return
|
|
375
|
+
return fabric.ARCHIVED_SUFFIX + fabric.DELETED_SUFFIX;
|
|
376
376
|
}
|
|
377
377
|
if (archived) {
|
|
378
|
-
return
|
|
378
|
+
return fabric.ARCHIVED_SUFFIX;
|
|
379
379
|
}
|
|
380
380
|
if (deleted) {
|
|
381
|
-
return
|
|
381
|
+
return fabric.DELETED_SUFFIX;
|
|
382
382
|
}
|
|
383
383
|
return "";
|
|
384
384
|
}
|
|
@@ -410,16 +410,16 @@ async function executeQuery(indexName, keyValue, options = {}) {
|
|
|
410
410
|
};
|
|
411
411
|
}
|
|
412
412
|
/**
|
|
413
|
-
* Query entities by
|
|
414
|
-
* Uses
|
|
413
|
+
* Query entities by scope (parent hierarchy)
|
|
414
|
+
* Uses indexScope GSI
|
|
415
415
|
*
|
|
416
|
-
* Note: This is a regular async function (not
|
|
416
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
417
417
|
* complex startKey objects that can't be coerced by vocabulary's type system.
|
|
418
418
|
*/
|
|
419
|
-
async function
|
|
419
|
+
async function queryByScope({ archived = false, ascending = false, deleted = false, limit, model, scope, startKey, }) {
|
|
420
420
|
const suffix = calculateSuffix$1({ archived, deleted });
|
|
421
|
-
const keyValue =
|
|
422
|
-
return executeQuery(
|
|
421
|
+
const keyValue = buildIndexScope(scope, model) + suffix;
|
|
422
|
+
return executeQuery(INDEX_SCOPE, keyValue, {
|
|
423
423
|
ascending,
|
|
424
424
|
limit,
|
|
425
425
|
startKey,
|
|
@@ -429,7 +429,7 @@ async function queryByOu({ archived = false, ascending = false, deleted = false,
|
|
|
429
429
|
* Query a single entity by human-friendly alias
|
|
430
430
|
* Uses indexAlias GSI
|
|
431
431
|
*/
|
|
432
|
-
const queryByAlias =
|
|
432
|
+
const queryByAlias = fabric.fabricService({
|
|
433
433
|
alias: "queryByAlias",
|
|
434
434
|
description: "Query a single entity by human-friendly alias",
|
|
435
435
|
input: {
|
|
@@ -447,16 +447,19 @@ const queryByAlias = vocabulary.serviceHandler({
|
|
|
447
447
|
description: "Query deleted entities instead of active ones",
|
|
448
448
|
},
|
|
449
449
|
model: { type: String, description: "Entity model name" },
|
|
450
|
-
|
|
450
|
+
scope: { type: String, description: "Scope (@ for root)" },
|
|
451
451
|
},
|
|
452
|
-
service: async ({ alias, archived, deleted, model,
|
|
452
|
+
service: async ({ alias, archived, deleted, model, scope, }) => {
|
|
453
453
|
const aliasStr = alias;
|
|
454
454
|
const archivedBool = archived;
|
|
455
455
|
const deletedBool = deleted;
|
|
456
456
|
const modelStr = model;
|
|
457
|
-
const
|
|
458
|
-
const suffix = calculateSuffix$1({
|
|
459
|
-
|
|
457
|
+
const scopeStr = scope;
|
|
458
|
+
const suffix = calculateSuffix$1({
|
|
459
|
+
archived: archivedBool,
|
|
460
|
+
deleted: deletedBool,
|
|
461
|
+
});
|
|
462
|
+
const keyValue = buildIndexAlias(scopeStr, modelStr, aliasStr) + suffix;
|
|
460
463
|
const result = await executeQuery(INDEX_ALIAS, keyValue, {
|
|
461
464
|
limit: 1,
|
|
462
465
|
});
|
|
@@ -467,12 +470,12 @@ const queryByAlias = vocabulary.serviceHandler({
|
|
|
467
470
|
* Query entities by category classification
|
|
468
471
|
* Uses indexClass GSI
|
|
469
472
|
*
|
|
470
|
-
* Note: This is a regular async function (not
|
|
473
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
471
474
|
* complex startKey objects that can't be coerced by vocabulary's type system.
|
|
472
475
|
*/
|
|
473
|
-
async function queryByClass({ archived = false, ascending = false, deleted = false, limit, model,
|
|
476
|
+
async function queryByClass({ archived = false, ascending = false, deleted = false, limit, model, scope, recordClass, startKey, }) {
|
|
474
477
|
const suffix = calculateSuffix$1({ archived, deleted });
|
|
475
|
-
const keyValue = buildIndexClass(
|
|
478
|
+
const keyValue = buildIndexClass(scope, model, recordClass) + suffix;
|
|
476
479
|
return executeQuery(INDEX_CLASS, keyValue, {
|
|
477
480
|
ascending,
|
|
478
481
|
limit,
|
|
@@ -483,12 +486,12 @@ async function queryByClass({ archived = false, ascending = false, deleted = fal
|
|
|
483
486
|
* Query entities by type classification
|
|
484
487
|
* Uses indexType GSI
|
|
485
488
|
*
|
|
486
|
-
* Note: This is a regular async function (not
|
|
489
|
+
* Note: This is a regular async function (not fabricService) because it accepts
|
|
487
490
|
* complex startKey objects that can't be coerced by vocabulary's type system.
|
|
488
491
|
*/
|
|
489
|
-
async function queryByType({ archived = false, ascending = false, deleted = false, limit, model,
|
|
492
|
+
async function queryByType({ archived = false, ascending = false, deleted = false, limit, model, scope, startKey, type, }) {
|
|
490
493
|
const suffix = calculateSuffix$1({ archived, deleted });
|
|
491
|
-
const keyValue = buildIndexType(
|
|
494
|
+
const keyValue = buildIndexType(scope, model, type) + suffix;
|
|
492
495
|
return executeQuery(INDEX_TYPE, keyValue, {
|
|
493
496
|
ascending,
|
|
494
497
|
limit,
|
|
@@ -499,7 +502,7 @@ async function queryByType({ archived = false, ascending = false, deleted = fals
|
|
|
499
502
|
* Query a single entity by external ID
|
|
500
503
|
* Uses indexXid GSI
|
|
501
504
|
*/
|
|
502
|
-
const queryByXid =
|
|
505
|
+
const queryByXid = fabric.fabricService({
|
|
503
506
|
alias: "queryByXid",
|
|
504
507
|
description: "Query a single entity by external ID",
|
|
505
508
|
input: {
|
|
@@ -516,17 +519,20 @@ const queryByXid = vocabulary.serviceHandler({
|
|
|
516
519
|
description: "Query deleted entities instead of active ones",
|
|
517
520
|
},
|
|
518
521
|
model: { type: String, description: "Entity model name" },
|
|
519
|
-
|
|
522
|
+
scope: { type: String, description: "Scope (@ for root)" },
|
|
520
523
|
xid: { type: String, description: "External ID" },
|
|
521
524
|
},
|
|
522
|
-
service: async ({ archived, deleted, model,
|
|
525
|
+
service: async ({ archived, deleted, model, scope, xid, }) => {
|
|
523
526
|
const archivedBool = archived;
|
|
524
527
|
const deletedBool = deleted;
|
|
525
528
|
const modelStr = model;
|
|
526
|
-
const
|
|
529
|
+
const scopeStr = scope;
|
|
527
530
|
const xidStr = xid;
|
|
528
|
-
const suffix = calculateSuffix$1({
|
|
529
|
-
|
|
531
|
+
const suffix = calculateSuffix$1({
|
|
532
|
+
archived: archivedBool,
|
|
533
|
+
deleted: deletedBool,
|
|
534
|
+
});
|
|
535
|
+
const keyValue = buildIndexXid(scopeStr, modelStr, xidStr) + suffix;
|
|
530
536
|
const result = await executeQuery(INDEX_XID, keyValue, {
|
|
531
537
|
limit: 1,
|
|
532
538
|
});
|
|
@@ -549,13 +555,13 @@ const queryByXid = vocabulary.serviceHandler({
|
|
|
549
555
|
*/
|
|
550
556
|
function calculateSuffix(archived, deleted) {
|
|
551
557
|
if (archived && deleted) {
|
|
552
|
-
return
|
|
558
|
+
return fabric.ARCHIVED_SUFFIX + fabric.DELETED_SUFFIX;
|
|
553
559
|
}
|
|
554
560
|
if (archived) {
|
|
555
|
-
return
|
|
561
|
+
return fabric.ARCHIVED_SUFFIX;
|
|
556
562
|
}
|
|
557
563
|
if (deleted) {
|
|
558
|
-
return
|
|
564
|
+
return fabric.DELETED_SUFFIX;
|
|
559
565
|
}
|
|
560
566
|
return "";
|
|
561
567
|
}
|
|
@@ -566,8 +572,8 @@ function buildFilterObject(params) {
|
|
|
566
572
|
const result = {
|
|
567
573
|
model: params.model,
|
|
568
574
|
};
|
|
569
|
-
if (params.
|
|
570
|
-
result.
|
|
575
|
+
if (params.scope !== undefined) {
|
|
576
|
+
result.scope = params.scope;
|
|
571
577
|
}
|
|
572
578
|
if (params.filter) {
|
|
573
579
|
Object.assign(result, params.filter);
|
|
@@ -637,14 +643,14 @@ function selectBestIndex(indexes, filterFields) {
|
|
|
637
643
|
* specific query function (queryByOu, queryByAlias, etc.) to use.
|
|
638
644
|
*
|
|
639
645
|
* @example
|
|
640
|
-
* // Uses
|
|
641
|
-
* const allMessages = await query({ model: "message",
|
|
646
|
+
* // Uses indexScope (pk: ["scope", "model"])
|
|
647
|
+
* const allMessages = await query({ model: "message", scope: `chat#${chatId}` });
|
|
642
648
|
*
|
|
643
649
|
* @example
|
|
644
|
-
* // Uses indexAlias (pk: ["
|
|
650
|
+
* // Uses indexAlias (pk: ["scope", "model", "alias"])
|
|
645
651
|
* const byAlias = await query({
|
|
646
652
|
* model: "record",
|
|
647
|
-
*
|
|
653
|
+
* scope: "@",
|
|
648
654
|
* filter: { alias: "my-record" },
|
|
649
655
|
* });
|
|
650
656
|
*
|
|
@@ -660,7 +666,7 @@ async function query(params) {
|
|
|
660
666
|
// Build the combined filter object
|
|
661
667
|
const filterFields = buildFilterObject(params);
|
|
662
668
|
// Get indexes for this model (custom or DEFAULT_INDEXES)
|
|
663
|
-
const indexes =
|
|
669
|
+
const indexes = fabric.getModelIndexes(model);
|
|
664
670
|
// Select the best matching index
|
|
665
671
|
const selectedIndex = selectBestIndex(indexes, filterFields);
|
|
666
672
|
const indexName = selectedIndex.name ?? generateIndexName(selectedIndex.pk);
|
|
@@ -703,18 +709,18 @@ function generateIndexName(pk) {
|
|
|
703
709
|
/**
|
|
704
710
|
* Seed a single entity if it doesn't already exist
|
|
705
711
|
*
|
|
706
|
-
* @param entity - Partial entity with at least alias, model, and
|
|
712
|
+
* @param entity - Partial entity with at least alias, model, and scope
|
|
707
713
|
* @returns true if entity was created, false if it already exists
|
|
708
714
|
*/
|
|
709
715
|
async function seedEntityIfNotExists(entity) {
|
|
710
|
-
if (!entity.alias || !entity.model || !entity.
|
|
711
|
-
throw new Error("Entity must have alias, model, and
|
|
716
|
+
if (!entity.alias || !entity.model || !entity.scope) {
|
|
717
|
+
throw new Error("Entity must have alias, model, and scope to check existence");
|
|
712
718
|
}
|
|
713
719
|
// Check if entity already exists
|
|
714
720
|
const existing = await queryByAlias({
|
|
715
721
|
alias: entity.alias,
|
|
716
722
|
model: entity.model,
|
|
717
|
-
|
|
723
|
+
scope: entity.scope,
|
|
718
724
|
});
|
|
719
725
|
if (existing) {
|
|
720
726
|
return false;
|
|
@@ -726,7 +732,7 @@ async function seedEntityIfNotExists(entity) {
|
|
|
726
732
|
id: entity.id ?? crypto.randomUUID(),
|
|
727
733
|
model: entity.model,
|
|
728
734
|
name: entity.name ?? entity.alias,
|
|
729
|
-
|
|
735
|
+
scope: entity.scope,
|
|
730
736
|
sequence: entity.sequence ?? Date.now(),
|
|
731
737
|
updatedAt: entity.updatedAt ?? now,
|
|
732
738
|
...entity,
|
|
@@ -756,15 +762,15 @@ async function seedEntities(entities, options = {}) {
|
|
|
756
762
|
for (const entity of entities) {
|
|
757
763
|
const alias = entity.alias ?? entity.name ?? "unknown";
|
|
758
764
|
try {
|
|
759
|
-
if (!entity.model || !entity.
|
|
760
|
-
throw new Error("Entity must have model and
|
|
765
|
+
if (!entity.model || !entity.scope) {
|
|
766
|
+
throw new Error("Entity must have model and scope");
|
|
761
767
|
}
|
|
762
768
|
// For entities with alias, check existence
|
|
763
769
|
if (entity.alias) {
|
|
764
770
|
const existing = await queryByAlias({
|
|
765
771
|
alias: entity.alias,
|
|
766
772
|
model: entity.model,
|
|
767
|
-
|
|
773
|
+
scope: entity.scope,
|
|
768
774
|
});
|
|
769
775
|
if (existing && !replace) {
|
|
770
776
|
result.skipped.push(alias);
|
|
@@ -786,7 +792,7 @@ async function seedEntities(entities, options = {}) {
|
|
|
786
792
|
id: entity.id ?? crypto.randomUUID(),
|
|
787
793
|
model: entity.model,
|
|
788
794
|
name: entity.name ?? entity.alias ?? "Unnamed",
|
|
789
|
-
|
|
795
|
+
scope: entity.scope,
|
|
790
796
|
sequence: entity.sequence ?? Date.now(),
|
|
791
797
|
updatedAt: entity.updatedAt ?? now,
|
|
792
798
|
...entity,
|
|
@@ -802,27 +808,27 @@ async function seedEntities(entities, options = {}) {
|
|
|
802
808
|
return result;
|
|
803
809
|
}
|
|
804
810
|
/**
|
|
805
|
-
* Export entities by model and
|
|
811
|
+
* Export entities by model and scope
|
|
806
812
|
*
|
|
807
|
-
* - Paginates through all matching entities via
|
|
813
|
+
* - Paginates through all matching entities via queryByScope
|
|
808
814
|
* - Returns entities sorted by sequence (ascending)
|
|
809
815
|
*
|
|
810
816
|
* @param model - The entity model name
|
|
811
|
-
* @param
|
|
817
|
+
* @param scope - The scope (APEX or "{parent.model}#{parent.id}")
|
|
812
818
|
* @param limit - Optional maximum number of entities to export
|
|
813
819
|
* @returns Export result with entities and count
|
|
814
820
|
*/
|
|
815
|
-
async function exportEntities(model,
|
|
821
|
+
async function exportEntities(model, scope, limit) {
|
|
816
822
|
const entities = [];
|
|
817
823
|
let startKey;
|
|
818
824
|
let remaining = limit;
|
|
819
825
|
do {
|
|
820
826
|
const batchLimit = remaining !== undefined ? Math.min(remaining, 100) : undefined;
|
|
821
|
-
const { items, lastEvaluatedKey } = await
|
|
827
|
+
const { items, lastEvaluatedKey } = await queryByScope({
|
|
822
828
|
ascending: true,
|
|
823
829
|
limit: batchLimit,
|
|
824
830
|
model,
|
|
825
|
-
|
|
831
|
+
scope,
|
|
826
832
|
startKey,
|
|
827
833
|
});
|
|
828
834
|
entities.push(...items);
|
|
@@ -840,48 +846,48 @@ async function exportEntities(model, ou, limit) {
|
|
|
840
846
|
* Export entities as a JSON string
|
|
841
847
|
*
|
|
842
848
|
* @param model - The entity model name
|
|
843
|
-
* @param
|
|
849
|
+
* @param scope - The scope (APEX or "{parent.model}#{parent.id}")
|
|
844
850
|
* @param pretty - Format JSON with indentation (default: true)
|
|
845
851
|
* @returns JSON string of exported entities
|
|
846
852
|
*/
|
|
847
|
-
async function exportEntitiesToJson(model,
|
|
848
|
-
const { entities } = await exportEntities(model,
|
|
853
|
+
async function exportEntitiesToJson(model, scope, pretty = true) {
|
|
854
|
+
const { entities } = await exportEntities(model, scope);
|
|
849
855
|
return pretty ? JSON.stringify(entities, null, 2) : JSON.stringify(entities);
|
|
850
856
|
}
|
|
851
857
|
|
|
852
858
|
Object.defineProperty(exports, "APEX", {
|
|
853
859
|
enumerable: true,
|
|
854
|
-
get: function () { return
|
|
860
|
+
get: function () { return fabric.APEX; }
|
|
855
861
|
});
|
|
856
862
|
Object.defineProperty(exports, "ARCHIVED_SUFFIX", {
|
|
857
863
|
enumerable: true,
|
|
858
|
-
get: function () { return
|
|
864
|
+
get: function () { return fabric.ARCHIVED_SUFFIX; }
|
|
859
865
|
});
|
|
860
866
|
Object.defineProperty(exports, "DEFAULT_INDEXES", {
|
|
861
867
|
enumerable: true,
|
|
862
|
-
get: function () { return
|
|
868
|
+
get: function () { return fabric.DEFAULT_INDEXES; }
|
|
863
869
|
});
|
|
864
870
|
Object.defineProperty(exports, "DELETED_SUFFIX", {
|
|
865
871
|
enumerable: true,
|
|
866
|
-
get: function () { return
|
|
872
|
+
get: function () { return fabric.DELETED_SUFFIX; }
|
|
867
873
|
});
|
|
868
874
|
Object.defineProperty(exports, "SEPARATOR", {
|
|
869
875
|
enumerable: true,
|
|
870
|
-
get: function () { return
|
|
876
|
+
get: function () { return fabric.SEPARATOR; }
|
|
871
877
|
});
|
|
872
878
|
exports.INDEX_ALIAS = INDEX_ALIAS;
|
|
873
879
|
exports.INDEX_CLASS = INDEX_CLASS;
|
|
874
|
-
exports.
|
|
880
|
+
exports.INDEX_SCOPE = INDEX_SCOPE;
|
|
875
881
|
exports.INDEX_TYPE = INDEX_TYPE;
|
|
876
882
|
exports.INDEX_XID = INDEX_XID;
|
|
877
883
|
exports.archiveEntity = archiveEntity;
|
|
878
884
|
exports.buildCompositeKey = buildCompositeKey;
|
|
879
885
|
exports.buildIndexAlias = buildIndexAlias;
|
|
880
886
|
exports.buildIndexClass = buildIndexClass;
|
|
881
|
-
exports.
|
|
887
|
+
exports.buildIndexScope = buildIndexScope;
|
|
882
888
|
exports.buildIndexType = buildIndexType;
|
|
883
889
|
exports.buildIndexXid = buildIndexXid;
|
|
884
|
-
exports.
|
|
890
|
+
exports.calculateScope = calculateScope;
|
|
885
891
|
exports.deleteEntity = deleteEntity;
|
|
886
892
|
exports.destroyEntity = destroyEntity;
|
|
887
893
|
exports.exportEntities = exportEntities;
|
|
@@ -896,7 +902,7 @@ exports.putEntity = putEntity;
|
|
|
896
902
|
exports.query = query;
|
|
897
903
|
exports.queryByAlias = queryByAlias;
|
|
898
904
|
exports.queryByClass = queryByClass;
|
|
899
|
-
exports.
|
|
905
|
+
exports.queryByScope = queryByScope;
|
|
900
906
|
exports.queryByType = queryByType;
|
|
901
907
|
exports.queryByXid = queryByXid;
|
|
902
908
|
exports.resetClient = resetClient;
|