@ronin/compiler 0.2.2 → 0.3.1
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/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
|