@ronin/compiler 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.d.ts +14264 -2
- package/dist/index.js +300 -205
- package/package.json +1 -1
package/dist/index.js
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
// src/utils/index.ts
|
2
2
|
import { init as cuid } from "@paralleldrive/cuid2";
|
3
3
|
var RONIN_SCHEMA_SYMBOLS = {
|
4
|
+
// Represents a sub query.
|
4
5
|
QUERY: "__RONIN_QUERY",
|
6
|
+
// Represents the value of a field in a schema.
|
5
7
|
FIELD: "__RONIN_FIELD_",
|
8
|
+
// Represents the old value of a field in a schema. Used for triggers.
|
9
|
+
FIELD_OLD: "__RONIN_FIELD_OLD_",
|
10
|
+
// Represents the new value of a field in a schema. Used for triggers.
|
11
|
+
FIELD_NEW: "__RONIN_FIELD_NEW_",
|
12
|
+
// Represents a value provided to a query preset.
|
6
13
|
VALUE: "__RONIN_VALUE"
|
7
14
|
};
|
8
15
|
var RoninError = class extends Error {
|
@@ -44,15 +51,22 @@ var convertToCamelCase = (str) => {
|
|
44
51
|
return sanitize(str).split(SPLIT_REGEX).map((part, index) => index === 0 ? part.toLowerCase() : capitalize(part)).join("");
|
45
52
|
};
|
46
53
|
var isObject = (value) => value != null && typeof value === "object" && Array.isArray(value) === false;
|
47
|
-
var
|
54
|
+
var findInObject = (obj, pattern, replacer) => {
|
55
|
+
let found = false;
|
48
56
|
for (const key in obj) {
|
49
57
|
const value = obj[key];
|
50
58
|
if (isObject(value)) {
|
51
|
-
|
59
|
+
found = findInObject(value, pattern, replacer);
|
52
60
|
} else if (typeof value === "string" && value.startsWith(pattern)) {
|
53
|
-
|
61
|
+
found = true;
|
62
|
+
if (replacer) {
|
63
|
+
obj[key] = value.replace(pattern, replacer);
|
64
|
+
} else {
|
65
|
+
return found;
|
66
|
+
}
|
54
67
|
}
|
55
68
|
}
|
69
|
+
return found;
|
56
70
|
};
|
57
71
|
var flatten = (obj, prefix = "", res = {}) => {
|
58
72
|
for (const key in obj) {
|
@@ -81,6 +95,209 @@ var splitQuery = (query) => {
|
|
81
95
|
return { queryType, querySchema, queryInstructions };
|
82
96
|
};
|
83
97
|
|
98
|
+
// src/utils/statement.ts
|
99
|
+
var prepareStatementValue = (statementValues, value, bindNull = false) => {
|
100
|
+
if (!bindNull && value === null) return "NULL";
|
101
|
+
let formattedValue = value;
|
102
|
+
if (Array.isArray(value) || isObject(value)) {
|
103
|
+
formattedValue = JSON.stringify(value);
|
104
|
+
} else if (typeof value === "boolean") {
|
105
|
+
formattedValue = value ? 1 : 0;
|
106
|
+
}
|
107
|
+
const index = statementValues.push(formattedValue);
|
108
|
+
return `?${index}`;
|
109
|
+
};
|
110
|
+
var composeFieldValues = (schemas, schema, statementValues, instructionName, value, options) => {
|
111
|
+
const { field: schemaField, fieldSelector: selector } = getFieldFromSchema(
|
112
|
+
schema,
|
113
|
+
options.fieldSlug,
|
114
|
+
instructionName,
|
115
|
+
options.rootTable
|
116
|
+
);
|
117
|
+
const isSubQuery = isObject(value) && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY);
|
118
|
+
const collectStatementValue = options.type !== "fields";
|
119
|
+
let conditionSelector = selector;
|
120
|
+
let conditionValue = value;
|
121
|
+
if (isSubQuery && collectStatementValue) {
|
122
|
+
conditionValue = `(${compileQueryInput(
|
123
|
+
value[RONIN_SCHEMA_SYMBOLS.QUERY],
|
124
|
+
schemas,
|
125
|
+
{ statementValues }
|
126
|
+
).readStatement})`;
|
127
|
+
} else if (typeof value === "string" && value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD)) {
|
128
|
+
let targetTable = `"${options.rootTable}"`;
|
129
|
+
let toReplace = RONIN_SCHEMA_SYMBOLS.FIELD;
|
130
|
+
if (value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD_OLD)) {
|
131
|
+
targetTable = "OLD";
|
132
|
+
toReplace = RONIN_SCHEMA_SYMBOLS.FIELD_OLD;
|
133
|
+
} else if (value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD_NEW)) {
|
134
|
+
targetTable = "NEW";
|
135
|
+
toReplace = RONIN_SCHEMA_SYMBOLS.FIELD_NEW;
|
136
|
+
}
|
137
|
+
conditionSelector = `${options.customTable ? `"${options.customTable}".` : ""}"${schemaField.slug}"`;
|
138
|
+
conditionValue = `${targetTable}."${value.replace(toReplace, "")}"`;
|
139
|
+
} else if (schemaField.type === "json" && instructionName === "to") {
|
140
|
+
conditionSelector = `"${schemaField.slug}"`;
|
141
|
+
if (collectStatementValue) {
|
142
|
+
const preparedValue = prepareStatementValue(statementValues, value, false);
|
143
|
+
conditionValue = `IIF(${conditionSelector} IS NULL, ${preparedValue}, json_patch(${conditionSelector}, ${preparedValue}))`;
|
144
|
+
}
|
145
|
+
} else if (collectStatementValue) {
|
146
|
+
conditionValue = prepareStatementValue(statementValues, value, false);
|
147
|
+
}
|
148
|
+
if (options.type === "fields") return conditionSelector;
|
149
|
+
if (options.type === "values") return conditionValue;
|
150
|
+
return `${conditionSelector} ${WITH_CONDITIONS[options.condition || "being"](conditionValue, value)}`;
|
151
|
+
};
|
152
|
+
var composeConditions = (schemas, schema, statementValues, instructionName, value, options) => {
|
153
|
+
const isNested = isObject(value) && Object.keys(value).length > 0;
|
154
|
+
if (isNested && Object.keys(value).every((key) => key in WITH_CONDITIONS)) {
|
155
|
+
const conditions = Object.entries(value).map(
|
156
|
+
([conditionType, checkValue]) => composeConditions(schemas, schema, statementValues, instructionName, checkValue, {
|
157
|
+
...options,
|
158
|
+
condition: conditionType
|
159
|
+
})
|
160
|
+
);
|
161
|
+
return conditions.join(" AND ");
|
162
|
+
}
|
163
|
+
if (options.fieldSlug) {
|
164
|
+
const fieldDetails = getFieldFromSchema(
|
165
|
+
schema,
|
166
|
+
options.fieldSlug,
|
167
|
+
instructionName,
|
168
|
+
options.rootTable
|
169
|
+
);
|
170
|
+
const { field: schemaField } = fieldDetails;
|
171
|
+
const consumeJSON = schemaField.type === "json" && instructionName === "to";
|
172
|
+
const isSubQuery = isNested && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY);
|
173
|
+
if (!(isObject(value) || Array.isArray(value)) || isSubQuery || consumeJSON) {
|
174
|
+
return composeFieldValues(
|
175
|
+
schemas,
|
176
|
+
schema,
|
177
|
+
statementValues,
|
178
|
+
instructionName,
|
179
|
+
value,
|
180
|
+
{ ...options, fieldSlug: options.fieldSlug }
|
181
|
+
);
|
182
|
+
}
|
183
|
+
if (schemaField.type === "reference" && isNested) {
|
184
|
+
const keys = Object.keys(value);
|
185
|
+
const values = Object.values(value);
|
186
|
+
let recordTarget;
|
187
|
+
if (keys.length === 1 && keys[0] === "id") {
|
188
|
+
recordTarget = values[0];
|
189
|
+
} else {
|
190
|
+
const relatedSchema = getSchemaBySlug(schemas, schemaField.target.slug);
|
191
|
+
const subQuery = {
|
192
|
+
get: {
|
193
|
+
[relatedSchema.slug]: {
|
194
|
+
with: value,
|
195
|
+
selecting: ["id"]
|
196
|
+
}
|
197
|
+
}
|
198
|
+
};
|
199
|
+
recordTarget = {
|
200
|
+
[RONIN_SCHEMA_SYMBOLS.QUERY]: subQuery
|
201
|
+
};
|
202
|
+
}
|
203
|
+
return composeConditions(
|
204
|
+
schemas,
|
205
|
+
schema,
|
206
|
+
statementValues,
|
207
|
+
instructionName,
|
208
|
+
recordTarget,
|
209
|
+
options
|
210
|
+
);
|
211
|
+
}
|
212
|
+
}
|
213
|
+
if (isNested) {
|
214
|
+
const conditions = Object.entries(value).map(([field, value2]) => {
|
215
|
+
const nestedFieldSlug = options.fieldSlug ? `${options.fieldSlug}.${field}` : field;
|
216
|
+
return composeConditions(schemas, schema, statementValues, instructionName, value2, {
|
217
|
+
...options,
|
218
|
+
fieldSlug: nestedFieldSlug
|
219
|
+
});
|
220
|
+
});
|
221
|
+
const joiner = instructionName === "to" ? ", " : " AND ";
|
222
|
+
if (instructionName === "to") return `${conditions.join(joiner)}`;
|
223
|
+
return conditions.length === 1 ? conditions[0] : options.fieldSlug ? `(${conditions.join(joiner)})` : conditions.join(joiner);
|
224
|
+
}
|
225
|
+
if (Array.isArray(value)) {
|
226
|
+
const conditions = value.map(
|
227
|
+
(filter) => composeConditions(
|
228
|
+
schemas,
|
229
|
+
schema,
|
230
|
+
statementValues,
|
231
|
+
instructionName,
|
232
|
+
filter,
|
233
|
+
options
|
234
|
+
)
|
235
|
+
);
|
236
|
+
return conditions.join(" OR ");
|
237
|
+
}
|
238
|
+
throw new RoninError({
|
239
|
+
message: `The \`with\` instruction must not contain an empty field. The following fields are empty: \`${options.fieldSlug}\`. If you meant to query by an empty field, try using \`null\` instead.`,
|
240
|
+
code: "INVALID_WITH_VALUE",
|
241
|
+
queries: null
|
242
|
+
});
|
243
|
+
};
|
244
|
+
var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
245
|
+
if (!queryInstructions) return queryInstructions;
|
246
|
+
const type = "with" in queryInstructions ? "with" : null;
|
247
|
+
if (!type) return queryInstructions;
|
248
|
+
const nestedInstructions = queryInstructions[type];
|
249
|
+
if (!nestedInstructions || Array.isArray(nestedInstructions))
|
250
|
+
return queryInstructions;
|
251
|
+
const newNestedInstructions = { ...nestedInstructions };
|
252
|
+
for (const oldKey of Object.keys(newNestedInstructions)) {
|
253
|
+
if (oldKey !== "titleIdentifier" && oldKey !== "slugIdentifier") continue;
|
254
|
+
const identifierName = oldKey === "titleIdentifier" ? "title" : "slug";
|
255
|
+
const value = newNestedInstructions[oldKey];
|
256
|
+
const newKey = identifiers?.[identifierName] || "id";
|
257
|
+
newNestedInstructions[newKey] = value;
|
258
|
+
delete newNestedInstructions[oldKey];
|
259
|
+
}
|
260
|
+
return {
|
261
|
+
...queryInstructions,
|
262
|
+
[type]: newNestedInstructions
|
263
|
+
};
|
264
|
+
};
|
265
|
+
|
266
|
+
// src/instructions/with.ts
|
267
|
+
var getMatcher = (value, negative) => {
|
268
|
+
if (negative) {
|
269
|
+
if (value === null) return "IS NOT";
|
270
|
+
return "!=";
|
271
|
+
}
|
272
|
+
if (value === null) return "IS";
|
273
|
+
return "=";
|
274
|
+
};
|
275
|
+
var WITH_CONDITIONS = {
|
276
|
+
being: (value, baseValue) => `${getMatcher(baseValue, false)} ${value}`,
|
277
|
+
notBeing: (value, baseValue) => `${getMatcher(baseValue, true)} ${value}`,
|
278
|
+
startingWith: (value) => `LIKE ${value}%`,
|
279
|
+
notStartingWith: (value) => `NOT LIKE ${value}%`,
|
280
|
+
endingWith: (value) => `LIKE %${value}`,
|
281
|
+
notEndingWith: (value) => `NOT LIKE %${value}`,
|
282
|
+
containing: (value) => `LIKE %${value}%`,
|
283
|
+
notContaining: (value) => `NOT LIKE %${value}%`,
|
284
|
+
greaterThan: (value) => `> ${value}`,
|
285
|
+
greaterOrEqual: (value) => `>= ${value}`,
|
286
|
+
lessThan: (value) => `< ${value}`,
|
287
|
+
lessOrEqual: (value) => `<= ${value}`
|
288
|
+
};
|
289
|
+
var handleWith = (schemas, schema, statementValues, instruction, rootTable) => {
|
290
|
+
const subStatement = composeConditions(
|
291
|
+
schemas,
|
292
|
+
schema,
|
293
|
+
statementValues,
|
294
|
+
"with",
|
295
|
+
instruction,
|
296
|
+
{ rootTable }
|
297
|
+
);
|
298
|
+
return `(${subStatement})`;
|
299
|
+
};
|
300
|
+
|
84
301
|
// src/utils/schema.ts
|
85
302
|
import title from "title";
|
86
303
|
var getSchemaBySlug = (schemas, slug) => {
|
@@ -101,7 +318,8 @@ var getTableForSchema = (schema) => {
|
|
101
318
|
var composeMetaSchemaSlug = (suffix) => convertToCamelCase(`ronin_${suffix}`);
|
102
319
|
var composeAssociationSchemaSlug = (schema, field) => composeMetaSchemaSlug(`${schema.pluralSlug}_${field.slug}`);
|
103
320
|
var getFieldSelector = (field, fieldPath, rootTable) => {
|
104
|
-
const
|
321
|
+
const symbol = rootTable?.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD) ? `${rootTable.replace(RONIN_SCHEMA_SYMBOLS.FIELD, "").slice(0, -1)}.` : "";
|
322
|
+
const tablePrefix = symbol || (rootTable ? `"${rootTable}".` : "");
|
105
323
|
if (field.type === "json") {
|
106
324
|
const dotParts = fieldPath.split(".");
|
107
325
|
const columnName = tablePrefix + dotParts.shift();
|
@@ -186,7 +404,10 @@ var SYSTEM_SCHEMAS = [
|
|
186
404
|
{ slug: "idPrefix", type: "string" },
|
187
405
|
{ slug: "identifiers", type: "group" },
|
188
406
|
{ slug: "identifiers.title", type: "string" },
|
189
|
-
{ slug: "identifiers.slug", type: "string" }
|
407
|
+
{ slug: "identifiers.slug", type: "string" },
|
408
|
+
{ slug: "fields", type: "json" },
|
409
|
+
{ slug: "indexes", type: "json" },
|
410
|
+
{ slug: "triggers", type: "json" }
|
190
411
|
]
|
191
412
|
},
|
192
413
|
{
|
@@ -234,6 +455,20 @@ var SYSTEM_SCHEMAS = [
|
|
234
455
|
{ slug: "unique", type: "boolean" },
|
235
456
|
{ slug: "filter", type: "json" }
|
236
457
|
]
|
458
|
+
},
|
459
|
+
{
|
460
|
+
name: "Trigger",
|
461
|
+
pluralName: "Triggers",
|
462
|
+
slug: "trigger",
|
463
|
+
pluralSlug: "triggers",
|
464
|
+
fields: [
|
465
|
+
...SYSTEM_FIELDS,
|
466
|
+
{ slug: "slug", type: "string", required: true },
|
467
|
+
{ slug: "schema", type: "reference", target: { slug: "schema" }, required: true },
|
468
|
+
{ slug: "cause", type: "string", required: true },
|
469
|
+
{ slug: "filter", type: "json" },
|
470
|
+
{ slug: "effects", type: "json", required: true }
|
471
|
+
]
|
237
472
|
}
|
238
473
|
];
|
239
474
|
var SYSTEM_SCHEMA_SLUGS = SYSTEM_SCHEMAS.flatMap(({ slug, pluralSlug }) => [
|
@@ -341,7 +576,7 @@ var getFieldStatement = (field) => {
|
|
341
576
|
}
|
342
577
|
return statement;
|
343
578
|
};
|
344
|
-
var addSchemaQueries = (queryDetails, writeStatements) => {
|
579
|
+
var addSchemaQueries = (schemas, statementValues, queryDetails, writeStatements) => {
|
345
580
|
const { queryType, querySchema, queryInstructions } = queryDetails;
|
346
581
|
if (!["create", "set", "drop"].includes(queryType)) return;
|
347
582
|
if (!SYSTEM_SCHEMA_SLUGS.includes(querySchema)) return;
|
@@ -352,7 +587,9 @@ var addSchemaQueries = (queryDetails, writeStatements) => {
|
|
352
587
|
let queryTypeReadable = null;
|
353
588
|
switch (queryType) {
|
354
589
|
case "create": {
|
355
|
-
if (kind === "schemas" || kind === "indexes"
|
590
|
+
if (kind === "schemas" || kind === "indexes" || kind === "triggers") {
|
591
|
+
tableAction = "CREATE";
|
592
|
+
}
|
356
593
|
queryTypeReadable = "creating";
|
357
594
|
break;
|
358
595
|
}
|
@@ -362,7 +599,9 @@ var addSchemaQueries = (queryDetails, writeStatements) => {
|
|
362
599
|
break;
|
363
600
|
}
|
364
601
|
case "drop": {
|
365
|
-
if (kind === "schemas" || kind === "indexes"
|
602
|
+
if (kind === "schemas" || kind === "indexes" || kind === "triggers") {
|
603
|
+
tableAction = "DROP";
|
604
|
+
}
|
366
605
|
queryTypeReadable = "deleting";
|
367
606
|
break;
|
368
607
|
}
|
@@ -388,8 +627,58 @@ var addSchemaQueries = (queryDetails, writeStatements) => {
|
|
388
627
|
if (kind === "indexes") {
|
389
628
|
const indexName = convertToSnakeCase(slug);
|
390
629
|
const unique = instructionList?.unique;
|
630
|
+
const filterQuery = instructionList?.filter;
|
391
631
|
let statement2 = `${tableAction}${unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
|
392
|
-
if (queryType === "create")
|
632
|
+
if (queryType === "create") {
|
633
|
+
statement2 += ` ON "${tableName}"`;
|
634
|
+
if (filterQuery) {
|
635
|
+
const targetSchema = getSchemaBySlug(schemas, schemaSlug);
|
636
|
+
const withStatement = handleWith(
|
637
|
+
schemas,
|
638
|
+
targetSchema,
|
639
|
+
statementValues,
|
640
|
+
filterQuery
|
641
|
+
);
|
642
|
+
statement2 += ` WHERE (${withStatement})`;
|
643
|
+
}
|
644
|
+
}
|
645
|
+
writeStatements.push(statement2);
|
646
|
+
return;
|
647
|
+
}
|
648
|
+
if (kind === "triggers") {
|
649
|
+
const triggerName = convertToSnakeCase(slug);
|
650
|
+
let statement2 = `${tableAction} TRIGGER "${triggerName}"`;
|
651
|
+
if (queryType === "create") {
|
652
|
+
const cause = slugToName(instructionList?.cause).toUpperCase();
|
653
|
+
const statementParts = [cause, "ON", `"${tableName}"`];
|
654
|
+
const effectQueries = instructionList?.effects;
|
655
|
+
const filterQuery = instructionList?.filter;
|
656
|
+
if (filterQuery || effectQueries.some((query) => findInObject(query, RONIN_SCHEMA_SYMBOLS.FIELD))) {
|
657
|
+
statementParts.push("FOR EACH ROW");
|
658
|
+
}
|
659
|
+
if (filterQuery) {
|
660
|
+
const targetSchema = getSchemaBySlug(schemas, schemaSlug);
|
661
|
+
const tablePlaceholder = cause.endsWith("DELETE") ? RONIN_SCHEMA_SYMBOLS.FIELD_OLD : RONIN_SCHEMA_SYMBOLS.FIELD_NEW;
|
662
|
+
const withStatement = handleWith(
|
663
|
+
schemas,
|
664
|
+
targetSchema,
|
665
|
+
statementValues,
|
666
|
+
filterQuery,
|
667
|
+
tablePlaceholder
|
668
|
+
);
|
669
|
+
statementParts.push("WHEN", `(${withStatement})`);
|
670
|
+
}
|
671
|
+
const effectStatements = effectQueries.map((effectQuery) => {
|
672
|
+
return compileQueryInput(effectQuery, schemas, {
|
673
|
+
statementValues,
|
674
|
+
disableReturning: true
|
675
|
+
}).readStatement;
|
676
|
+
});
|
677
|
+
if (effectStatements.length > 1) statementParts.push("BEGIN");
|
678
|
+
statementParts.push(effectStatements.join("; "));
|
679
|
+
if (effectStatements.length > 1) statementParts.push("END");
|
680
|
+
statement2 += ` ${statementParts.join(" ")}`;
|
681
|
+
}
|
393
682
|
writeStatements.push(statement2);
|
394
683
|
return;
|
395
684
|
}
|
@@ -447,200 +736,6 @@ var pluralize = (word) => {
|
|
447
736
|
return `${word}s`;
|
448
737
|
};
|
449
738
|
|
450
|
-
// src/instructions/with.ts
|
451
|
-
var getMatcher = (value, negative) => {
|
452
|
-
if (negative) {
|
453
|
-
if (value === null) return "IS NOT";
|
454
|
-
return "!=";
|
455
|
-
}
|
456
|
-
if (value === null) return "IS";
|
457
|
-
return "=";
|
458
|
-
};
|
459
|
-
var WITH_CONDITIONS = {
|
460
|
-
being: (value, baseValue) => `${getMatcher(baseValue, false)} ${value}`,
|
461
|
-
notBeing: (value, baseValue) => `${getMatcher(baseValue, true)} ${value}`,
|
462
|
-
startingWith: (value) => `LIKE ${value}%`,
|
463
|
-
notStartingWith: (value) => `NOT LIKE ${value}%`,
|
464
|
-
endingWith: (value) => `LIKE %${value}`,
|
465
|
-
notEndingWith: (value) => `NOT LIKE %${value}`,
|
466
|
-
containing: (value) => `LIKE %${value}%`,
|
467
|
-
notContaining: (value) => `NOT LIKE %${value}%`,
|
468
|
-
greaterThan: (value) => `> ${value}`,
|
469
|
-
greaterOrEqual: (value) => `>= ${value}`,
|
470
|
-
lessThan: (value) => `< ${value}`,
|
471
|
-
lessOrEqual: (value) => `<= ${value}`
|
472
|
-
};
|
473
|
-
var handleWith = (schemas, schema, statementValues, instruction, rootTable) => {
|
474
|
-
const subStatement = composeConditions(
|
475
|
-
schemas,
|
476
|
-
schema,
|
477
|
-
statementValues,
|
478
|
-
"with",
|
479
|
-
instruction,
|
480
|
-
{ rootTable }
|
481
|
-
);
|
482
|
-
return `(${subStatement})`;
|
483
|
-
};
|
484
|
-
|
485
|
-
// src/utils/statement.ts
|
486
|
-
var prepareStatementValue = (statementValues, value, bindNull = false) => {
|
487
|
-
if (!bindNull && value === null) return "NULL";
|
488
|
-
let formattedValue = value;
|
489
|
-
if (Array.isArray(value) || isObject(value)) {
|
490
|
-
formattedValue = JSON.stringify(value);
|
491
|
-
} else if (typeof value === "boolean") {
|
492
|
-
formattedValue = value ? 1 : 0;
|
493
|
-
}
|
494
|
-
const index = statementValues.push(formattedValue);
|
495
|
-
return `?${index}`;
|
496
|
-
};
|
497
|
-
var composeFieldValues = (schemas, schema, statementValues, instructionName, value, options) => {
|
498
|
-
const { field: schemaField, fieldSelector: selector } = getFieldFromSchema(
|
499
|
-
schema,
|
500
|
-
options.fieldSlug,
|
501
|
-
instructionName,
|
502
|
-
options.rootTable
|
503
|
-
);
|
504
|
-
const isSubQuery = isObject(value) && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY);
|
505
|
-
const collectStatementValue = options.type !== "fields";
|
506
|
-
let conditionSelector = selector;
|
507
|
-
let conditionValue = value;
|
508
|
-
if (isSubQuery && collectStatementValue) {
|
509
|
-
conditionValue = `(${compileQueryInput(
|
510
|
-
value[RONIN_SCHEMA_SYMBOLS.QUERY],
|
511
|
-
schemas,
|
512
|
-
{ statementValues }
|
513
|
-
).readStatement})`;
|
514
|
-
} else if (typeof value === "string" && value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD)) {
|
515
|
-
conditionSelector = `"${options.customTable}"."${schemaField.slug}"`;
|
516
|
-
conditionValue = `"${options.rootTable}"."${value.replace(RONIN_SCHEMA_SYMBOLS.FIELD, "")}"`;
|
517
|
-
} else if (schemaField.type === "json" && instructionName === "to") {
|
518
|
-
conditionSelector = `"${schemaField.slug}"`;
|
519
|
-
if (collectStatementValue) {
|
520
|
-
const preparedValue = prepareStatementValue(statementValues, value, false);
|
521
|
-
conditionValue = `IIF(${conditionSelector} IS NULL, ${preparedValue}, json_patch(${conditionSelector}, ${preparedValue}))`;
|
522
|
-
}
|
523
|
-
} else if (collectStatementValue) {
|
524
|
-
conditionValue = prepareStatementValue(statementValues, value, false);
|
525
|
-
}
|
526
|
-
if (options.type === "fields") return conditionSelector;
|
527
|
-
if (options.type === "values") return conditionValue;
|
528
|
-
return `${conditionSelector} ${WITH_CONDITIONS[options.condition || "being"](conditionValue, value)}`;
|
529
|
-
};
|
530
|
-
var composeConditions = (schemas, schema, statementValues, instructionName, value, options) => {
|
531
|
-
const isNested = isObject(value) && Object.keys(value).length > 0;
|
532
|
-
if (isNested && Object.keys(value).every((key) => key in WITH_CONDITIONS)) {
|
533
|
-
const conditions = Object.entries(value).map(
|
534
|
-
([conditionType, checkValue]) => composeConditions(schemas, schema, statementValues, instructionName, checkValue, {
|
535
|
-
...options,
|
536
|
-
condition: conditionType
|
537
|
-
})
|
538
|
-
);
|
539
|
-
return conditions.join(" AND ");
|
540
|
-
}
|
541
|
-
if (options.fieldSlug) {
|
542
|
-
const fieldDetails = getFieldFromSchema(
|
543
|
-
schema,
|
544
|
-
options.fieldSlug,
|
545
|
-
instructionName,
|
546
|
-
options.rootTable
|
547
|
-
);
|
548
|
-
const { field: schemaField } = fieldDetails;
|
549
|
-
const consumeJSON = schemaField.type === "json" && instructionName === "to";
|
550
|
-
const isSubQuery = isNested && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY);
|
551
|
-
if (!(isObject(value) || Array.isArray(value)) || isSubQuery || consumeJSON) {
|
552
|
-
return composeFieldValues(
|
553
|
-
schemas,
|
554
|
-
schema,
|
555
|
-
statementValues,
|
556
|
-
instructionName,
|
557
|
-
value,
|
558
|
-
{ ...options, fieldSlug: options.fieldSlug }
|
559
|
-
);
|
560
|
-
}
|
561
|
-
if (schemaField.type === "reference" && isNested) {
|
562
|
-
const keys = Object.keys(value);
|
563
|
-
const values = Object.values(value);
|
564
|
-
let recordTarget;
|
565
|
-
if (keys.length === 1 && keys[0] === "id") {
|
566
|
-
recordTarget = values[0];
|
567
|
-
} else {
|
568
|
-
const relatedSchema = getSchemaBySlug(schemas, schemaField.target.slug);
|
569
|
-
const subQuery = {
|
570
|
-
get: {
|
571
|
-
[relatedSchema.slug]: {
|
572
|
-
with: value,
|
573
|
-
selecting: ["id"]
|
574
|
-
}
|
575
|
-
}
|
576
|
-
};
|
577
|
-
recordTarget = {
|
578
|
-
[RONIN_SCHEMA_SYMBOLS.QUERY]: subQuery
|
579
|
-
};
|
580
|
-
}
|
581
|
-
return composeConditions(
|
582
|
-
schemas,
|
583
|
-
schema,
|
584
|
-
statementValues,
|
585
|
-
instructionName,
|
586
|
-
recordTarget,
|
587
|
-
options
|
588
|
-
);
|
589
|
-
}
|
590
|
-
}
|
591
|
-
if (isNested) {
|
592
|
-
const conditions = Object.entries(value).map(([field, value2]) => {
|
593
|
-
const nestedFieldSlug = options.fieldSlug ? `${options.fieldSlug}.${field}` : field;
|
594
|
-
return composeConditions(schemas, schema, statementValues, instructionName, value2, {
|
595
|
-
...options,
|
596
|
-
fieldSlug: nestedFieldSlug
|
597
|
-
});
|
598
|
-
});
|
599
|
-
const joiner = instructionName === "to" ? ", " : " AND ";
|
600
|
-
if (instructionName === "to") return `${conditions.join(joiner)}`;
|
601
|
-
return conditions.length === 1 ? conditions[0] : options.fieldSlug ? `(${conditions.join(joiner)})` : conditions.join(joiner);
|
602
|
-
}
|
603
|
-
if (Array.isArray(value)) {
|
604
|
-
const conditions = value.map(
|
605
|
-
(filter) => composeConditions(
|
606
|
-
schemas,
|
607
|
-
schema,
|
608
|
-
statementValues,
|
609
|
-
instructionName,
|
610
|
-
filter,
|
611
|
-
options
|
612
|
-
)
|
613
|
-
);
|
614
|
-
return conditions.join(" OR ");
|
615
|
-
}
|
616
|
-
throw new RoninError({
|
617
|
-
message: `The \`with\` instruction must not contain an empty field. The following fields are empty: \`${options.fieldSlug}\`. If you meant to query by an empty field, try using \`null\` instead.`,
|
618
|
-
code: "INVALID_WITH_VALUE",
|
619
|
-
queries: null
|
620
|
-
});
|
621
|
-
};
|
622
|
-
var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
623
|
-
if (!queryInstructions) return queryInstructions;
|
624
|
-
const type = "with" in queryInstructions ? "with" : null;
|
625
|
-
if (!type) return queryInstructions;
|
626
|
-
const nestedInstructions = queryInstructions[type];
|
627
|
-
if (!nestedInstructions || Array.isArray(nestedInstructions))
|
628
|
-
return queryInstructions;
|
629
|
-
const newNestedInstructions = { ...nestedInstructions };
|
630
|
-
for (const oldKey of Object.keys(newNestedInstructions)) {
|
631
|
-
if (oldKey !== "titleIdentifier" && oldKey !== "slugIdentifier") continue;
|
632
|
-
const identifierName = oldKey === "titleIdentifier" ? "title" : "slug";
|
633
|
-
const value = newNestedInstructions[oldKey];
|
634
|
-
const newKey = identifiers?.[identifierName] || "id";
|
635
|
-
newNestedInstructions[newKey] = value;
|
636
|
-
delete newNestedInstructions[oldKey];
|
637
|
-
}
|
638
|
-
return {
|
639
|
-
...queryInstructions,
|
640
|
-
[type]: newNestedInstructions
|
641
|
-
};
|
642
|
-
};
|
643
|
-
|
644
739
|
// src/instructions/before-after.ts
|
645
740
|
var CURSOR_SEPARATOR = ",";
|
646
741
|
var CURSOR_NULL_PLACEHOLDER = "RONIN_NULL";
|
@@ -734,7 +829,7 @@ var handleFor = (schemas, schema, statementValues, instruction, rootTable) => {
|
|
734
829
|
});
|
735
830
|
}
|
736
831
|
const replacedForFilter = structuredClone(forFilter);
|
737
|
-
|
832
|
+
findInObject(
|
738
833
|
replacedForFilter,
|
739
834
|
RONIN_SCHEMA_SYMBOLS.VALUE,
|
740
835
|
(match) => match.replace(RONIN_SCHEMA_SYMBOLS.VALUE, args)
|
@@ -999,7 +1094,7 @@ var compileQueryInput = (query, defaultSchemas, options) => {
|
|
999
1094
|
let table = getTableForSchema(schema);
|
1000
1095
|
const statementValues = options?.statementValues || [];
|
1001
1096
|
const writeStatements = [];
|
1002
|
-
addSchemaQueries(parsedQuery, writeStatements);
|
1097
|
+
addSchemaQueries(schemas, statementValues, parsedQuery, writeStatements);
|
1003
1098
|
const columns = handleSelecting(schema, statementValues, {
|
1004
1099
|
selecting: instructions?.selecting,
|
1005
1100
|
including: instructions?.including
|