@ronin/compiler 0.13.9 → 0.13.10
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 +1 -1
- package/dist/index.js +64 -28
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
@@ -15,7 +15,7 @@ declare const QUERY_SYMBOLS: {
|
|
15
15
|
readonly FIELD_PARENT_NEW: "__RONIN_FIELD_PARENT_NEW_";
|
16
16
|
readonly VALUE: "__RONIN_VALUE";
|
17
17
|
};
|
18
|
-
type RoninErrorCode = 'MODEL_NOT_FOUND' | 'FIELD_NOT_FOUND' | 'INDEX_NOT_FOUND' | 'TRIGGER_NOT_FOUND' | 'PRESET_NOT_FOUND' | 'INVALID_WITH_VALUE' | 'INVALID_TO_VALUE' | 'INVALID_INCLUDING_VALUE' | 'INVALID_FOR_VALUE' | 'INVALID_BEFORE_OR_AFTER_INSTRUCTION' | 'INVALID_MODEL_VALUE' | 'MUTUALLY_EXCLUSIVE_INSTRUCTIONS' | 'MISSING_INSTRUCTION' | 'MISSING_FIELD';
|
18
|
+
type RoninErrorCode = 'MODEL_NOT_FOUND' | 'FIELD_NOT_FOUND' | 'INDEX_NOT_FOUND' | 'TRIGGER_NOT_FOUND' | 'PRESET_NOT_FOUND' | 'INVALID_WITH_VALUE' | 'INVALID_TO_VALUE' | 'INVALID_INCLUDING_VALUE' | 'INVALID_FOR_VALUE' | 'INVALID_BEFORE_OR_AFTER_INSTRUCTION' | 'INVALID_MODEL_VALUE' | 'EXISTING_MODEL_ENTITY' | 'REQUIRED_MODEL_ENTITY' | 'MUTUALLY_EXCLUSIVE_INSTRUCTIONS' | 'MISSING_INSTRUCTION' | 'MISSING_FIELD';
|
19
19
|
interface Issue {
|
20
20
|
message: string;
|
21
21
|
path: Array<string | number>;
|
package/dist/index.js
CHANGED
@@ -184,7 +184,7 @@ var parseFieldExpression = (model, instructionName, expression, parentModel) =>
|
|
184
184
|
}
|
185
185
|
}
|
186
186
|
const fieldSlug = match.replace(toReplace, "");
|
187
|
-
const field = getFieldFromModel(rootModel, fieldSlug, instructionName);
|
187
|
+
const field = getFieldFromModel(rootModel, fieldSlug, { instructionName });
|
188
188
|
return field.fieldSelector;
|
189
189
|
});
|
190
190
|
};
|
@@ -192,7 +192,7 @@ var composeFieldValues = (models, model, statementParams, instructionName, value
|
|
192
192
|
const { fieldSelector: conditionSelector } = getFieldFromModel(
|
193
193
|
model,
|
194
194
|
options.fieldSlug,
|
195
|
-
instructionName
|
195
|
+
{ instructionName }
|
196
196
|
);
|
197
197
|
const collectStatementValue = options.type !== "fields";
|
198
198
|
const symbol = getSymbol(value);
|
@@ -233,7 +233,9 @@ var composeConditions = (models, model, statementParams, instructionName, value,
|
|
233
233
|
return slug.includes(".") && slug.split(".")[0] === options.fieldSlug;
|
234
234
|
});
|
235
235
|
if (!childField) {
|
236
|
-
const fieldDetails = getFieldFromModel(model, options.fieldSlug,
|
236
|
+
const fieldDetails = getFieldFromModel(model, options.fieldSlug, {
|
237
|
+
instructionName
|
238
|
+
});
|
237
239
|
const { field: modelField } = fieldDetails || {};
|
238
240
|
const consumeJSON = modelField?.type === "json" && instructionName === "to";
|
239
241
|
if (modelField && !(isObject(value) || Array.isArray(value)) || getSymbol(value) || consumeJSON) {
|
@@ -565,10 +567,10 @@ var getModelBySlug = (models, slug) => {
|
|
565
567
|
return model;
|
566
568
|
};
|
567
569
|
var composeAssociationModelSlug = (model, field) => convertToCamelCase(`ronin_link_${model.slug}_${field.slug}`);
|
568
|
-
var getFieldSelector = (model, field, fieldPath,
|
570
|
+
var getFieldSelector = (model, field, fieldPath, writing) => {
|
569
571
|
const symbol = model.tableAlias?.startsWith(QUERY_SYMBOLS.FIELD_PARENT) ? `${model.tableAlias.replace(QUERY_SYMBOLS.FIELD_PARENT, "").slice(0, -1)}.` : "";
|
570
572
|
const tablePrefix = symbol || (model.tableAlias ? `"${model.tableAlias}".` : "");
|
571
|
-
if (field.type === "json" &&
|
573
|
+
if (field.type === "json" && !writing) {
|
572
574
|
const dotParts = fieldPath.split(".");
|
573
575
|
const columnName = tablePrefix + dotParts.shift();
|
574
576
|
const jsonField = dotParts.join(".");
|
@@ -576,19 +578,16 @@ var getFieldSelector = (model, field, fieldPath, instructionName) => {
|
|
576
578
|
}
|
577
579
|
return `${tablePrefix}"${fieldPath}"`;
|
578
580
|
};
|
579
|
-
function getFieldFromModel(model, fieldPath,
|
580
|
-
const
|
581
|
+
function getFieldFromModel(model, fieldPath, source, shouldThrow = true) {
|
582
|
+
const writingField = "instructionName" in source ? source.instructionName === "to" : true;
|
583
|
+
const errorTarget = "instructionName" in source ? `\`${source.instructionName}\`` : `${source.modelEntityType} "${source.modelEntityName}"`;
|
584
|
+
const errorPrefix = `Field "${fieldPath}" defined for ${errorTarget}`;
|
581
585
|
const modelFields = model.fields || [];
|
582
586
|
let modelField;
|
583
587
|
if (fieldPath.includes(".")) {
|
584
588
|
modelField = modelFields.find((field) => field.slug === fieldPath.split(".")[0]);
|
585
589
|
if (modelField?.type === "json") {
|
586
|
-
const fieldSelector2 = getFieldSelector(
|
587
|
-
model,
|
588
|
-
modelField,
|
589
|
-
fieldPath,
|
590
|
-
instructionName
|
591
|
-
);
|
590
|
+
const fieldSelector2 = getFieldSelector(model, modelField, fieldPath, writingField);
|
592
591
|
return { field: modelField, fieldSelector: fieldSelector2 };
|
593
592
|
}
|
594
593
|
}
|
@@ -604,7 +603,7 @@ function getFieldFromModel(model, fieldPath, instructionName, shouldThrow = true
|
|
604
603
|
}
|
605
604
|
return null;
|
606
605
|
}
|
607
|
-
const fieldSelector = getFieldSelector(model, modelField, fieldPath,
|
606
|
+
const fieldSelector = getFieldSelector(model, modelField, fieldPath, writingField);
|
608
607
|
return { field: modelField, fieldSelector };
|
609
608
|
}
|
610
609
|
var slugToName = (slug) => {
|
@@ -1015,6 +1014,13 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
1015
1014
|
});
|
1016
1015
|
}
|
1017
1016
|
const existingEntity = existingModel[pluralType]?.[targetEntityIndex];
|
1017
|
+
if (action === "create" && existingEntity) {
|
1018
|
+
throw new RoninError({
|
1019
|
+
message: `A ${entity} with the slug "${slug}" already exists.`,
|
1020
|
+
code: "EXISTING_MODEL_ENTITY",
|
1021
|
+
fields: ["slug"]
|
1022
|
+
});
|
1023
|
+
}
|
1018
1024
|
if (entity === "field") {
|
1019
1025
|
const statement = `ALTER TABLE "${existingModel.table}"`;
|
1020
1026
|
const existingField = existingEntity;
|
@@ -1043,6 +1049,14 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
1043
1049
|
}
|
1044
1050
|
}
|
1045
1051
|
} else if (action === "drop" && !existingLinkField) {
|
1052
|
+
const systemFields = getSystemFields(existingModel.idPrefix);
|
1053
|
+
const isSystemField = systemFields.some((field2) => field2.slug === slug);
|
1054
|
+
if (isSystemField) {
|
1055
|
+
throw new RoninError({
|
1056
|
+
message: `The ${entity} "${slug}" is a system ${entity} and cannot be removed.`,
|
1057
|
+
code: "REQUIRED_MODEL_ENTITY"
|
1058
|
+
});
|
1059
|
+
}
|
1046
1060
|
dependencyStatements.push({
|
1047
1061
|
statement: `${statement} DROP COLUMN "${slug}"`,
|
1048
1062
|
params: []
|
@@ -1055,10 +1069,20 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
1055
1069
|
const indexName = convertToSnakeCase(slug);
|
1056
1070
|
let statement = `${statementAction}${index?.unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
|
1057
1071
|
if (action === "create") {
|
1072
|
+
if (!Array.isArray(index.fields) || index.fields.length === 0) {
|
1073
|
+
throw new RoninError({
|
1074
|
+
message: `When ${actionReadable} ${PLURAL_MODEL_ENTITIES[entity]}, at least one field must be provided.`,
|
1075
|
+
code: "INVALID_MODEL_VALUE",
|
1076
|
+
fields: ["fields"]
|
1077
|
+
});
|
1078
|
+
}
|
1058
1079
|
const columns = index.fields.map((field2) => {
|
1059
1080
|
let fieldSelector = "";
|
1060
1081
|
if ("slug" in field2) {
|
1061
|
-
({ fieldSelector } = getFieldFromModel(existingModel, field2.slug,
|
1082
|
+
({ fieldSelector } = getFieldFromModel(existingModel, field2.slug, {
|
1083
|
+
modelEntityType: "index",
|
1084
|
+
modelEntityName: indexName
|
1085
|
+
}));
|
1062
1086
|
} else if ("expression" in field2) {
|
1063
1087
|
fieldSelector = parseFieldExpression(existingModel, "to", field2.expression);
|
1064
1088
|
}
|
@@ -1089,7 +1113,10 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
1089
1113
|
});
|
1090
1114
|
}
|
1091
1115
|
const fieldSelectors = trigger.fields.map((field2) => {
|
1092
|
-
return getFieldFromModel(existingModel, field2.slug,
|
1116
|
+
return getFieldFromModel(existingModel, field2.slug, {
|
1117
|
+
modelEntityType: "trigger",
|
1118
|
+
modelEntityName: triggerName
|
1119
|
+
}).fieldSelector;
|
1093
1120
|
});
|
1094
1121
|
statementParts.push(`OF (${fieldSelectors.join(", ")})`);
|
1095
1122
|
}
|
@@ -1201,7 +1228,9 @@ var generatePaginationCursor = (model, orderedBy, record) => {
|
|
1201
1228
|
const cursors = keys.map((fieldSlug) => {
|
1202
1229
|
const property = getProperty(record, fieldSlug);
|
1203
1230
|
if (property === null || property === void 0) return CURSOR_NULL_PLACEHOLDER;
|
1204
|
-
const { field } = getFieldFromModel(model, fieldSlug,
|
1231
|
+
const { field } = getFieldFromModel(model, fieldSlug, {
|
1232
|
+
instructionName: "orderedBy"
|
1233
|
+
});
|
1205
1234
|
if (field.type === "date") return new Date(property).getTime();
|
1206
1235
|
return property;
|
1207
1236
|
});
|
@@ -1240,7 +1269,9 @@ var handleBeforeOrAfter = (model, statementParams, instructions) => {
|
|
1240
1269
|
if (value === CURSOR_NULL_PLACEHOLDER) {
|
1241
1270
|
return "NULL";
|
1242
1271
|
}
|
1243
|
-
const { field } = getFieldFromModel(model, key,
|
1272
|
+
const { field } = getFieldFromModel(model, key, {
|
1273
|
+
instructionName: "orderedBy"
|
1274
|
+
});
|
1244
1275
|
if (field.type === "boolean") {
|
1245
1276
|
return prepareStatementValue(statementParams, value === "true");
|
1246
1277
|
}
|
@@ -1266,7 +1297,9 @@ var handleBeforeOrAfter = (model, statementParams, instructions) => {
|
|
1266
1297
|
for (let j = 0; j <= i; j++) {
|
1267
1298
|
const key = keys[j];
|
1268
1299
|
const value = values[j];
|
1269
|
-
let { field, fieldSelector } = getFieldFromModel(model, key,
|
1300
|
+
let { field, fieldSelector } = getFieldFromModel(model, key, {
|
1301
|
+
instructionName: "orderedBy"
|
1302
|
+
});
|
1270
1303
|
if (j === i) {
|
1271
1304
|
const closingParentheses = ")".repeat(condition.length);
|
1272
1305
|
const operator = value === "NULL" ? "IS NOT" : compareOperators[j];
|
@@ -1418,7 +1451,7 @@ var handleOrderedBy = (model, instruction) => {
|
|
1418
1451
|
const { field: modelField, fieldSelector } = getFieldFromModel(
|
1419
1452
|
model,
|
1420
1453
|
item.value,
|
1421
|
-
instructionName
|
1454
|
+
{ instructionName }
|
1422
1455
|
);
|
1423
1456
|
const caseInsensitiveStatement = modelField.type === "string" ? " COLLATE NOCASE" : "";
|
1424
1457
|
statement += `${fieldSelector}${caseInsensitiveStatement} ${item.order}`;
|
@@ -1488,11 +1521,9 @@ var handleSelecting = (models, model, statementParams, instructions, options) =>
|
|
1488
1521
|
const usableModel = expandColumns ? { ...model, tableAlias: model.tableAlias || model.table } : model;
|
1489
1522
|
const selectedFields = [];
|
1490
1523
|
statement = instructions.selecting.map((slug) => {
|
1491
|
-
const { field, fieldSelector } = getFieldFromModel(
|
1492
|
-
|
1493
|
-
|
1494
|
-
"selecting"
|
1495
|
-
);
|
1524
|
+
const { field, fieldSelector } = getFieldFromModel(usableModel, slug, {
|
1525
|
+
instructionName: "selecting"
|
1526
|
+
});
|
1496
1527
|
selectedFields.push(field);
|
1497
1528
|
return fieldSelector;
|
1498
1529
|
}).join(", ");
|
@@ -1542,12 +1573,12 @@ var handleTo = (models, model, statementParams, queryType, dependencyStatements,
|
|
1542
1573
|
) : []
|
1543
1574
|
];
|
1544
1575
|
for (const field of subQueryFields || []) {
|
1545
|
-
getFieldFromModel(model, field, "to");
|
1576
|
+
getFieldFromModel(model, field, { instructionName: "to" });
|
1546
1577
|
}
|
1547
1578
|
let statement2 = "";
|
1548
1579
|
if (subQuerySelectedFields) {
|
1549
1580
|
const columns = subQueryFields.map((field) => {
|
1550
|
-
return getFieldFromModel(model, field, "to").fieldSelector;
|
1581
|
+
return getFieldFromModel(model, field, { instructionName: "to" }).fieldSelector;
|
1551
1582
|
});
|
1552
1583
|
statement2 = `(${columns.join(", ")}) `;
|
1553
1584
|
}
|
@@ -1558,7 +1589,12 @@ var handleTo = (models, model, statementParams, queryType, dependencyStatements,
|
|
1558
1589
|
for (const fieldSlug in toInstruction) {
|
1559
1590
|
if (!Object.hasOwn(toInstruction, fieldSlug)) continue;
|
1560
1591
|
const fieldValue = toInstruction[fieldSlug];
|
1561
|
-
const fieldDetails = getFieldFromModel(
|
1592
|
+
const fieldDetails = getFieldFromModel(
|
1593
|
+
model,
|
1594
|
+
fieldSlug,
|
1595
|
+
{ instructionName: "to" },
|
1596
|
+
false
|
1597
|
+
);
|
1562
1598
|
if (fieldDetails?.field.type === "link" && fieldDetails.field.kind === "many") {
|
1563
1599
|
delete toInstruction[fieldSlug];
|
1564
1600
|
const associativeModelSlug = composeAssociationModelSlug(model, fieldDetails.field);
|