@snowtop/ent 0.2.13 → 0.2.14
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/action/orchestrator.d.ts +6 -1
- package/action/orchestrator.js +151 -19
- package/action/privacy.d.ts +1 -0
- package/action/privacy.js +1 -0
- package/core/clause.js +1 -1
- package/core/query_impl.d.ts +2 -2
- package/core/query_impl.js +7 -5
- package/package.json +2 -2
- package/schema/struct_field.js +7 -5
- package/schema/union_field.js +3 -2
- package/scripts/custom_graphql.js +4 -1
- package/testutils/db/temp_db.js +2 -1
package/action/orchestrator.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface OrchestratorOptions<TEnt extends Ent<TViewer>, TInput extends D
|
|
|
17
17
|
action?: Action<TEnt, Builder<TEnt, TViewer>, TViewer, TInput>;
|
|
18
18
|
schema: SchemaInputType;
|
|
19
19
|
editedFields(): Map<string, any> | Promise<Map<string, any>>;
|
|
20
|
-
updateInput?: (data: TInput) => void;
|
|
20
|
+
updateInput?: (data: TInput, operation?: WriteOperation) => void;
|
|
21
21
|
expressions?: Map<string, clause.Clause>;
|
|
22
22
|
fieldInfo: FieldInfoMap;
|
|
23
23
|
}
|
|
@@ -38,6 +38,8 @@ export declare class Orchestrator<TEnt extends Ent<TViewer>, TInput extends Data
|
|
|
38
38
|
private options;
|
|
39
39
|
private edgeSet;
|
|
40
40
|
private edges;
|
|
41
|
+
private fieldEdgeSources;
|
|
42
|
+
private fieldEdgeInputs;
|
|
41
43
|
private conditionalEdges;
|
|
42
44
|
private validatedFields;
|
|
43
45
|
private logValues;
|
|
@@ -59,6 +61,9 @@ export declare class Orchestrator<TEnt extends Ent<TViewer>, TInput extends Data
|
|
|
59
61
|
setDisableTransformations(val: boolean): void;
|
|
60
62
|
setOnConflictOptions(onConflict: CreateRowOptions["onConflict"]): void;
|
|
61
63
|
addInboundEdge<T2 extends Ent>(id1: ID | Builder<T2, any>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
|
|
64
|
+
__setFieldEdges<T2 extends Ent>(fieldName: string, ids: readonly (ID | Builder<T2, any>)[] | undefined, edgeType: string, nodeType: string, stored: {
|
|
65
|
+
existingIDs?: readonly ID[];
|
|
66
|
+
}): void;
|
|
62
67
|
addOutboundEdge<T2 extends Ent>(id2: ID | Builder<T2, any>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
|
|
63
68
|
removeInboundEdge(id1: ID, edgeType: string, options?: AssocEdgeOptions): void;
|
|
64
69
|
removeOutboundEdge(id2: ID, edgeType: string, options?: AssocEdgeOptions): void;
|
package/action/orchestrator.js
CHANGED
|
@@ -39,12 +39,25 @@ const schema_1 = require("../schema/schema");
|
|
|
39
39
|
const operations_1 = require("./operations");
|
|
40
40
|
const action_1 = require("../action");
|
|
41
41
|
const privacy_1 = require("../core/privacy");
|
|
42
|
+
const privacy_2 = require("./privacy");
|
|
42
43
|
const executor_1 = require("./executor");
|
|
43
44
|
const memoize_1 = require("../core/memoize");
|
|
44
45
|
const logger_1 = require("../core/logger");
|
|
45
46
|
const clause = __importStar(require("../core/clause"));
|
|
46
47
|
const types_1 = require("util/types");
|
|
47
48
|
const operations_2 = require("./operations");
|
|
49
|
+
function invalidFieldError(fieldName, value) {
|
|
50
|
+
let description;
|
|
51
|
+
try {
|
|
52
|
+
description = `${value}`;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Null-prototype records (and lists containing them) cannot be coerced
|
|
56
|
+
// to strings. Diagnostic formatting must not hide a validation failure.
|
|
57
|
+
description = "[unprintable value]";
|
|
58
|
+
}
|
|
59
|
+
return new Error(`invalid field ${fieldName} with value ${description}`);
|
|
60
|
+
}
|
|
48
61
|
var edgeDirection;
|
|
49
62
|
(function (edgeDirection) {
|
|
50
63
|
edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
|
|
@@ -99,6 +112,8 @@ class Orchestrator {
|
|
|
99
112
|
this.options = options;
|
|
100
113
|
this.edgeSet = new Set();
|
|
101
114
|
this.edges = new Map();
|
|
115
|
+
this.fieldEdgeSources = new WeakMap();
|
|
116
|
+
this.fieldEdgeInputs = new Map();
|
|
102
117
|
this.conditionalEdges = new Map();
|
|
103
118
|
this.validatedFields = null;
|
|
104
119
|
this.changesets = [];
|
|
@@ -155,6 +170,105 @@ class Orchestrator {
|
|
|
155
170
|
direction: edgeDirection.inboundEdge,
|
|
156
171
|
}), action_1.WriteOperation.Insert, options?.conditional);
|
|
157
172
|
}
|
|
173
|
+
// Update inverse edges for generated fields while preserving explicit edge
|
|
174
|
+
// operations. This method is internal. For edits and deletions, use stored IDs.
|
|
175
|
+
// If `stored` omits `existingIDs`, reuse the IDs captured before synchronous
|
|
176
|
+
// default updates.
|
|
177
|
+
__setFieldEdges(fieldName, ids, edgeType, nodeType, stored) {
|
|
178
|
+
// Existing builders and literal IDs can refer to the same database row.
|
|
179
|
+
// Use placeholder IDs as queue keys so unsaved builders remain dependencies.
|
|
180
|
+
const endpointID = (id) => (0, privacy_2.isBuilder)(id) ? (id.existingEnt?.id ?? id.placeholderID) : id;
|
|
181
|
+
this.fieldEdgeInputs.set(fieldName, {
|
|
182
|
+
edgeType,
|
|
183
|
+
nodeType,
|
|
184
|
+
ids,
|
|
185
|
+
existingIDs: stored.existingIDs ??
|
|
186
|
+
this.fieldEdgeInputs.get(fieldName)?.existingIDs ??
|
|
187
|
+
[],
|
|
188
|
+
});
|
|
189
|
+
const inserts = new Map();
|
|
190
|
+
const removals = new Map();
|
|
191
|
+
const retained = new Set();
|
|
192
|
+
const contribute = (map, id, source) => {
|
|
193
|
+
const key = (0, privacy_2.isBuilder)(id) ? id.placeholderID : id;
|
|
194
|
+
let entry = map.get(key);
|
|
195
|
+
if (!entry) {
|
|
196
|
+
map.set(key, (entry = { id, sources: new Set() }));
|
|
197
|
+
}
|
|
198
|
+
entry.sources.add(source);
|
|
199
|
+
};
|
|
200
|
+
for (const [source, field] of this.fieldEdgeInputs) {
|
|
201
|
+
if (field.edgeType !== edgeType) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const existing = this.actualOperation === action_1.WriteOperation.Insert ? [] : field.existingIDs;
|
|
205
|
+
const current = this.actualOperation === action_1.WriteOperation.Delete ? [] : field.ids;
|
|
206
|
+
for (const id of current ?? existing) {
|
|
207
|
+
retained.add(endpointID(id));
|
|
208
|
+
}
|
|
209
|
+
if (current !== undefined) {
|
|
210
|
+
for (const id of current) {
|
|
211
|
+
contribute(inserts, id, source);
|
|
212
|
+
}
|
|
213
|
+
for (const id of existing) {
|
|
214
|
+
contribute(removals, id, source);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const manualEndpoints = (op) => {
|
|
219
|
+
const endpoints = new Set();
|
|
220
|
+
for (const edge of this.edges.get(edgeType)?.get(op)?.values() ?? []) {
|
|
221
|
+
if (!this.fieldEdgeSources.has(edge)) {
|
|
222
|
+
endpoints.add(endpointID(edge.id));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return endpoints;
|
|
226
|
+
};
|
|
227
|
+
const manualInserts = manualEndpoints(action_1.WriteOperation.Insert);
|
|
228
|
+
const manualRemovals = manualEndpoints(action_1.WriteOperation.Delete);
|
|
229
|
+
for (const id of removals.keys()) {
|
|
230
|
+
if (retained.has(id) || manualInserts.has(id)) {
|
|
231
|
+
removals.delete(id);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (const [key, contribution] of inserts) {
|
|
235
|
+
const id = endpointID(contribution.id);
|
|
236
|
+
if (manualInserts.has(id) || manualRemovals.has(id)) {
|
|
237
|
+
inserts.delete(key);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
for (const [op, desired] of [
|
|
241
|
+
[action_1.WriteOperation.Insert, inserts],
|
|
242
|
+
[action_1.WriteOperation.Delete, removals],
|
|
243
|
+
]) {
|
|
244
|
+
const queued = this.edges.get(edgeType)?.get(op);
|
|
245
|
+
for (const [id, edge] of queued ?? []) {
|
|
246
|
+
if (!this.fieldEdgeSources.has(edge)) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const contribution = desired.get(id);
|
|
250
|
+
if (contribution) {
|
|
251
|
+
this.fieldEdgeSources.set(edge, contribution.sources);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
queued.delete(id);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const [key, contribution] of desired) {
|
|
258
|
+
if (this.edges.get(edgeType)?.get(op)?.has(key)) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const edge = new edgeInputData({
|
|
262
|
+
id: contribution.id,
|
|
263
|
+
edgeType,
|
|
264
|
+
nodeType,
|
|
265
|
+
direction: edgeDirection.inboundEdge,
|
|
266
|
+
});
|
|
267
|
+
this.fieldEdgeSources.set(edge, contribution.sources);
|
|
268
|
+
this.addEdge(edge, op);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
158
272
|
addOutboundEdge(id2, edgeType, nodeType, options) {
|
|
159
273
|
this.addEdge(new edgeInputData({
|
|
160
274
|
id: id2,
|
|
@@ -532,27 +646,22 @@ class Orchestrator {
|
|
|
532
646
|
}
|
|
533
647
|
async triggers(action, builder, triggers) {
|
|
534
648
|
const groups = [];
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
// @ts-ignore
|
|
542
|
-
groups.push(triggers.slice(lastArray, i));
|
|
649
|
+
let group = [];
|
|
650
|
+
for (const trigger of triggers) {
|
|
651
|
+
if (Array.isArray(trigger)) {
|
|
652
|
+
if (group.length) {
|
|
653
|
+
groups.push(group);
|
|
654
|
+
group = [];
|
|
543
655
|
}
|
|
544
|
-
groups.push(
|
|
545
|
-
prevWasArray = true;
|
|
546
|
-
lastArray++;
|
|
656
|
+
groups.push(trigger);
|
|
547
657
|
}
|
|
548
658
|
else {
|
|
549
|
-
|
|
550
|
-
// @ts-ignore
|
|
551
|
-
groups.push(triggers.slice(lastArray, i + 1));
|
|
552
|
-
}
|
|
553
|
-
prevWasArray = false;
|
|
659
|
+
group.push(trigger);
|
|
554
660
|
}
|
|
555
661
|
}
|
|
662
|
+
if (group.length) {
|
|
663
|
+
groups.push(group);
|
|
664
|
+
}
|
|
556
665
|
for (const triggers of groups) {
|
|
557
666
|
await Promise.all(triggers.map(async (trigger) => {
|
|
558
667
|
let ret = await trigger.changeset(builder, action.getInput());
|
|
@@ -606,6 +715,8 @@ class Orchestrator {
|
|
|
606
715
|
// if disable transformations set, don't do schema transform and just do the right thing
|
|
607
716
|
// else apply schema tranformation if it exists
|
|
608
717
|
let transformed = null;
|
|
718
|
+
const initialOperation = this.actualOperation;
|
|
719
|
+
const initialEnt = this.existingEnt;
|
|
609
720
|
const sqlOp = this.getSQLStatementOperation();
|
|
610
721
|
// why is transform write technically different from upsert?
|
|
611
722
|
// it's create -> update just at the db level...
|
|
@@ -668,6 +779,13 @@ class Orchestrator {
|
|
|
668
779
|
builder.existingEnt = transformed.existingEnt;
|
|
669
780
|
}
|
|
670
781
|
}
|
|
782
|
+
if (this.fieldEdgeInputs.size > 0 &&
|
|
783
|
+
(initialOperation !== this.actualOperation ||
|
|
784
|
+
initialEnt !== this.existingEnt)) {
|
|
785
|
+
// Refresh inverse edges for the transformed operation and row before
|
|
786
|
+
// applying defaults or running triggers.
|
|
787
|
+
editedFields = await this.options.editedFields();
|
|
788
|
+
}
|
|
671
789
|
// transforming before doing default fields so that we don't create a new id
|
|
672
790
|
// and anything that depends on the type of operations knows what it is
|
|
673
791
|
const userDefinedKeys = new Set();
|
|
@@ -728,7 +846,7 @@ class Orchestrator {
|
|
|
728
846
|
};
|
|
729
847
|
if (updateInput && this.options.updateInput) {
|
|
730
848
|
// this basically fixes #605. just needs to be exposed correctly
|
|
731
|
-
this.options.updateInput(this.defaultFieldsByTSName);
|
|
849
|
+
this.options.updateInput(this.defaultFieldsByTSName, this.actualOperation);
|
|
732
850
|
}
|
|
733
851
|
}
|
|
734
852
|
return { data, userDefinedKeys };
|
|
@@ -764,7 +882,7 @@ class Orchestrator {
|
|
|
764
882
|
valid = await valid;
|
|
765
883
|
}
|
|
766
884
|
if (!valid) {
|
|
767
|
-
return
|
|
885
|
+
return invalidFieldError(fieldName, value);
|
|
768
886
|
}
|
|
769
887
|
}
|
|
770
888
|
// keep track of dependencies to resolve
|
|
@@ -779,7 +897,7 @@ class Orchestrator {
|
|
|
779
897
|
valid = await valid;
|
|
780
898
|
}
|
|
781
899
|
if (!valid) {
|
|
782
|
-
return
|
|
900
|
+
return invalidFieldError(fieldName, value);
|
|
783
901
|
}
|
|
784
902
|
}
|
|
785
903
|
if (field.format) {
|
|
@@ -855,6 +973,20 @@ class Orchestrator {
|
|
|
855
973
|
}
|
|
856
974
|
}
|
|
857
975
|
}
|
|
976
|
+
// If a trigger clears an input, the SQL write can still use its computed
|
|
977
|
+
// default. Update inverse edges for defaults included in `data`; apply edit
|
|
978
|
+
// defaults only when the edit has data to save.
|
|
979
|
+
for (const [fieldName, field] of this.fieldEdgeInputs) {
|
|
980
|
+
if (field.ids !== undefined ||
|
|
981
|
+
data[this.getStorageKey(fieldName)] === undefined) {
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
const value = this.defaultFieldsByFieldName[fieldName];
|
|
985
|
+
if (value === undefined) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
this.__setFieldEdges(fieldName, value === null ? [] : Array.isArray(value) ? value : [value], field.edgeType, field.nodeType, {});
|
|
989
|
+
}
|
|
858
990
|
this.validatedFields = data;
|
|
859
991
|
this.logValues = logValues;
|
|
860
992
|
return errors;
|
package/action/privacy.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Builder } from "./action";
|
|
2
2
|
import { Viewer, ID, Ent, PrivacyResult, PrivacyPolicyRule } from "../core/base";
|
|
3
|
+
export declare function isBuilder(node: ID | Builder<Ent, any>): node is Builder<Ent, any>;
|
|
3
4
|
export declare class DenyIfBuilder implements PrivacyPolicyRule {
|
|
4
5
|
private id?;
|
|
5
6
|
constructor(id?: (ID | Builder<Ent, any>) | undefined);
|
package/action/privacy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AllowIfBuilder = exports.DenyIfBuilder = void 0;
|
|
4
|
+
exports.isBuilder = isBuilder;
|
|
4
5
|
const base_1 = require("../core/base");
|
|
5
6
|
function isBuilder(node) {
|
|
6
7
|
return node.placeholderID !== undefined;
|
package/core/clause.js
CHANGED
|
@@ -185,7 +185,7 @@ class queryClause {
|
|
|
185
185
|
this.prefix = prefix;
|
|
186
186
|
}
|
|
187
187
|
clause(idx, alias) {
|
|
188
|
-
const q = (0, query_impl_1.buildQuery)(this.dependentQueryOptions);
|
|
188
|
+
const q = (0, query_impl_1.buildQuery)(this.dependentQueryOptions, idx);
|
|
189
189
|
return `${this.prefix} (${q})`;
|
|
190
190
|
}
|
|
191
191
|
columns() {
|
package/core/query_impl.d.ts
CHANGED
|
@@ -28,6 +28,6 @@ export declare function reverseOrderBy(orderby: OrderBy): OrderBy;
|
|
|
28
28
|
interface JoinInfo extends QueryFragmentInfo {
|
|
29
29
|
}
|
|
30
30
|
export declare function getJoinInfo(join: NonNullable<QueryableDataOptions["join"]>, clauseIdx?: number): JoinInfo;
|
|
31
|
-
export declare function buildQueryData(options: QueryableDataOptions): BuiltQueryData;
|
|
32
|
-
export declare function buildQuery(options: QueryableDataOptions): string;
|
|
31
|
+
export declare function buildQueryData(options: QueryableDataOptions, startIdx?: number): BuiltQueryData;
|
|
32
|
+
export declare function buildQuery(options: QueryableDataOptions, startIdx?: number): string;
|
|
33
33
|
export {};
|
package/core/query_impl.js
CHANGED
|
@@ -171,12 +171,14 @@ function getJoinInfo(join, clauseIdx = 1) {
|
|
|
171
171
|
logValues,
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
-
|
|
174
|
+
// startIdx is the first placeholder number when embedding this query in another.
|
|
175
|
+
// The returned values contain only this query's parameters.
|
|
176
|
+
function buildQueryData(options, startIdx = 1) {
|
|
175
177
|
const fieldsAlias = options.fieldsAlias ?? options.alias;
|
|
176
|
-
const fieldInfo = getFieldsInfo(options.fields, fieldsAlias, options.disableFieldsAlias,
|
|
178
|
+
const fieldInfo = getFieldsInfo(options.fields, fieldsAlias, options.disableFieldsAlias, startIdx);
|
|
177
179
|
const values = [...fieldInfo.values];
|
|
178
180
|
const logValues = [...fieldInfo.logValues];
|
|
179
|
-
let clauseIdx =
|
|
181
|
+
let clauseIdx = startIdx + fieldInfo.valuesUsed;
|
|
180
182
|
const parts = [];
|
|
181
183
|
const tableName = options.alias
|
|
182
184
|
? `${options.tableName} AS ${options.alias}`
|
|
@@ -220,6 +222,6 @@ function buildQueryData(options) {
|
|
|
220
222
|
logValues,
|
|
221
223
|
};
|
|
222
224
|
}
|
|
223
|
-
function buildQuery(options) {
|
|
224
|
-
return buildQueryData(options).query;
|
|
225
|
+
function buildQuery(options, startIdx = 1) {
|
|
226
|
+
return buildQueryData(options, startIdx).query;
|
|
225
227
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowtop/ent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"description": "snowtop ent framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"tsconfig-paths": "4.2.0",
|
|
19
19
|
"tslib": "2.8.1",
|
|
20
20
|
"typescript": "5.9.3",
|
|
21
|
-
"uuid": "
|
|
21
|
+
"uuid": "11.1.1"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@swc-node/register": "1.6.8",
|
package/schema/struct_field.js
CHANGED
|
@@ -35,7 +35,7 @@ class StructField extends field_1.BaseField {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
formatImpl(obj, nested) {
|
|
38
|
-
if (
|
|
38
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
39
39
|
throw new Error("valid was not called");
|
|
40
40
|
}
|
|
41
41
|
let ret = {};
|
|
@@ -122,7 +122,12 @@ class StructField extends field_1.BaseField {
|
|
|
122
122
|
return this.formatImpl(obj, nested);
|
|
123
123
|
}
|
|
124
124
|
async validImpl(obj) {
|
|
125
|
-
|
|
125
|
+
// GraphQL.js valueFromAST turns inline objects such as prefs: {enabled: true}
|
|
126
|
+
// into null-prototype records, which fail instanceof Object. Objects supplied
|
|
127
|
+
// in the JSON variables payload use ordinary prototypes instead. A struct
|
|
128
|
+
// can still be inline when other values use variables; object-valued variable
|
|
129
|
+
// defaults also go through valueFromAST when the variable is omitted.
|
|
130
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
126
131
|
return false;
|
|
127
132
|
}
|
|
128
133
|
let promises = [];
|
|
@@ -241,9 +246,6 @@ class StructField extends field_1.BaseField {
|
|
|
241
246
|
}));
|
|
242
247
|
return valid.every((b) => b);
|
|
243
248
|
}
|
|
244
|
-
if (!(obj instanceof Object)) {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
249
|
return this.validImpl(obj);
|
|
248
250
|
}
|
|
249
251
|
}
|
package/schema/union_field.js
CHANGED
|
@@ -25,7 +25,7 @@ class UnionField extends field_1.BaseField {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
format(obj) {
|
|
28
|
-
if (
|
|
28
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
29
29
|
throw new Error("valid was not called");
|
|
30
30
|
}
|
|
31
31
|
const k = obj[KEY];
|
|
@@ -46,7 +46,8 @@ class UnionField extends field_1.BaseField {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
async valid(obj) {
|
|
49
|
-
|
|
49
|
+
// Accept the same record shapes as the member struct fields.
|
|
50
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
50
51
|
return false;
|
|
51
52
|
}
|
|
52
53
|
let promises = [];
|
|
@@ -533,7 +533,10 @@ async function main() {
|
|
|
533
533
|
});
|
|
534
534
|
}
|
|
535
535
|
main()
|
|
536
|
-
.
|
|
536
|
+
// Imported application modules can leave database pools or other handles open.
|
|
537
|
+
// The awaited stdout write above is the script's final required work, so exit
|
|
538
|
+
// explicitly instead of waiting for the event loop to drain.
|
|
539
|
+
.then(() => (0, process_1.exit)(0))
|
|
537
540
|
.catch((err) => {
|
|
538
541
|
console.error(err);
|
|
539
542
|
(0, process_1.exit)(1);
|
package/testutils/db/temp_db.js
CHANGED
|
@@ -627,13 +627,14 @@ function setupSqlite(connString, tables, opts) {
|
|
|
627
627
|
const client = await db_1.default.getInstance().getNewClient();
|
|
628
628
|
for (const [key, _] of tdb.__getTables()) {
|
|
629
629
|
const query = `delete from ${key}`;
|
|
630
|
-
if (isSyncClient(client))
|
|
630
|
+
if (isSyncClient(client)) {
|
|
631
631
|
if (client.execSync) {
|
|
632
632
|
client.execSync(query);
|
|
633
633
|
}
|
|
634
634
|
else {
|
|
635
635
|
await client.exec(query);
|
|
636
636
|
}
|
|
637
|
+
}
|
|
637
638
|
}
|
|
638
639
|
});
|
|
639
640
|
}
|