@ronin/compiler 0.10.2 → 0.10.3-leo-ron-1083-experimental-215
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/README.md +38 -2
- package/dist/index.d.ts +16 -5
- package/dist/index.js +69 -41
- package/package.json +1 -1
package/README.md
CHANGED
@@ -70,6 +70,24 @@ executed and their results can be formatted by the compiler as well:
|
|
70
70
|
const results: Array<Result> = transaction.prepareResults(rows);
|
71
71
|
```
|
72
72
|
|
73
|
+
#### Root Model
|
74
|
+
|
75
|
+
Before you can run any statements generated by the compiler that are altering the database schema, you need to create the table of the so-called "root model", which is used to store metadata for all other models.
|
76
|
+
|
77
|
+
This table is called `ronin_schema`, which mimics the default `sqlite_schema` table provided by SQLite. You can generate its respective SQL statements like so:
|
78
|
+
|
79
|
+
```typescript
|
80
|
+
import { Transaction, ROOT_MODEL } from '@ronin/compiler';
|
81
|
+
|
82
|
+
const transaction = new Transaction([
|
83
|
+
{
|
84
|
+
create: { model: ROOT_MODEL }
|
85
|
+
}
|
86
|
+
]);
|
87
|
+
```
|
88
|
+
|
89
|
+
Afterward, run the statements located in `transaction.statements` to create the table for the root model. Once that is done, your database is prepared to run any statements generated by the compiler.
|
90
|
+
|
73
91
|
#### Types
|
74
92
|
|
75
93
|
In total, the following types are being exported:
|
@@ -103,7 +121,25 @@ new Transaction(queries, {
|
|
103
121
|
// Instead of returning an array of parameters for every statement (which allows for
|
104
122
|
// preventing SQL injections), all parameters are inlined directly into the SQL strings.
|
105
123
|
// This option should only be used if the generated SQL will be manually verified.
|
106
|
-
inlineParams: true
|
124
|
+
inlineParams: true,
|
125
|
+
|
126
|
+
// By default, in the generated SQL statements, the compiler does not alias columns if
|
127
|
+
// multiple different tables with the same column names are being joined. Only the table
|
128
|
+
// names themselves are aliased.
|
129
|
+
//
|
130
|
+
// This ensures the cleanest possible SQL statements in conjunction with the default
|
131
|
+
// behavior of SQL databases, where the result of a statement is a list (array) of
|
132
|
+
// values, which are inherently not prone to conflicts.
|
133
|
+
//
|
134
|
+
// If the driver being used instead returns an object for every row, the driver must
|
135
|
+
// ensure the uniqueness of every key in that object, which means prefixing duplicated
|
136
|
+
// column names with the name of the respective table, if multiple tables are joined.
|
137
|
+
//
|
138
|
+
// Drivers that return objects for rows offer this behavior as an option that is
|
139
|
+
// usually called "expand columns". If the driver being used does not offer such an
|
140
|
+
// option, you can instead activate the option in the compiler, which results in longer
|
141
|
+
// SQL statements because any duplicated column name is aliased.
|
142
|
+
expandColumns: true
|
107
143
|
});
|
108
144
|
```
|
109
145
|
|
@@ -117,7 +153,7 @@ bun run dev
|
|
117
153
|
|
118
154
|
Whenever you make a change to the source code, it will then automatically be transpiled again.
|
119
155
|
|
120
|
-
###
|
156
|
+
### Architecture
|
121
157
|
|
122
158
|
The interface of creating new `Transaction` instances (thereby creating new transactions) was chosen in order to define the smallest workload unit that the compiler can operate on.
|
123
159
|
|
package/dist/index.d.ts
CHANGED
@@ -5979,7 +5979,7 @@ interface Model<T extends Array<ModelField> = Array<ModelField>> {
|
|
5979
5979
|
triggers?: Array<ModelTrigger<T>>;
|
5980
5980
|
presets?: Array<ModelPreset>;
|
5981
5981
|
}
|
5982
|
-
type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'system' | '
|
5982
|
+
type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'system' | 'tableAlias'> & {
|
5983
5983
|
slug: Required<Model['slug']>;
|
5984
5984
|
identifiers?: Partial<Model['identifiers']>;
|
5985
5985
|
};
|
@@ -6005,13 +6005,22 @@ type AmountResult = {
|
|
6005
6005
|
};
|
6006
6006
|
type Result = SingleRecordResult | MultipleRecordResult | AmountResult;
|
6007
6007
|
|
6008
|
+
interface TransactionOptions {
|
6009
|
+
/** A list of models that already exist in the database. */
|
6010
|
+
models?: Array<PublicModel>;
|
6011
|
+
/**
|
6012
|
+
* Place statement parameters directly inside the statement strings instead of
|
6013
|
+
* separating them out into a dedicated `params` array.
|
6014
|
+
*/
|
6015
|
+
inlineParams?: boolean;
|
6016
|
+
/** Alias column names that are duplicated when joining multiple tables. */
|
6017
|
+
expandColumns?: boolean;
|
6018
|
+
}
|
6008
6019
|
declare class Transaction {
|
6009
6020
|
statements: Array<Statement>;
|
6010
6021
|
models: Array<Model>;
|
6011
6022
|
private queries;
|
6012
|
-
constructor(queries: Array<Query>, options?:
|
6013
|
-
models?: Array<PublicModel>;
|
6014
|
-
});
|
6023
|
+
constructor(queries: Array<Query>, options?: TransactionOptions);
|
6015
6024
|
/**
|
6016
6025
|
* Composes SQL statements for the provided RONIN queries.
|
6017
6026
|
*
|
@@ -6026,4 +6035,6 @@ declare class Transaction {
|
|
6026
6035
|
prepareResults(results: Array<Array<Row>>): Array<Result>;
|
6027
6036
|
}
|
6028
6037
|
|
6029
|
-
|
6038
|
+
declare const CLEAN_ROOT_MODEL: PublicModel;
|
6039
|
+
|
6040
|
+
export { type PublicModel as Model, type ModelField, type ModelIndex, type ModelPreset, type ModelTrigger, type Query, CLEAN_ROOT_MODEL as ROOT_MODEL, type Result, type Statement, Transaction };
|
package/dist/index.js
CHANGED
@@ -65,6 +65,23 @@ var convertToCamelCase = (str) => {
|
|
65
65
|
return sanitize(str).split(SPLIT_REGEX).map((part, index) => index === 0 ? part.toLowerCase() : capitalize(part)).join("");
|
66
66
|
};
|
67
67
|
var isObject = (value) => value != null && typeof value === "object" && Array.isArray(value) === false;
|
68
|
+
var getSymbol = (value) => {
|
69
|
+
if (!isObject(value)) return null;
|
70
|
+
const objectValue = value;
|
71
|
+
if (RONIN_MODEL_SYMBOLS.QUERY in objectValue) {
|
72
|
+
return {
|
73
|
+
type: "query",
|
74
|
+
value: objectValue[RONIN_MODEL_SYMBOLS.QUERY]
|
75
|
+
};
|
76
|
+
}
|
77
|
+
if (RONIN_MODEL_SYMBOLS.EXPRESSION in objectValue) {
|
78
|
+
return {
|
79
|
+
type: "expression",
|
80
|
+
value: objectValue[RONIN_MODEL_SYMBOLS.EXPRESSION]
|
81
|
+
};
|
82
|
+
}
|
83
|
+
return null;
|
84
|
+
};
|
68
85
|
var findInObject = (obj, pattern, replacer) => {
|
69
86
|
let found = false;
|
70
87
|
for (const key in obj) {
|
@@ -85,14 +102,18 @@ var findInObject = (obj, pattern, replacer) => {
|
|
85
102
|
var flatten = (obj, prefix = "", res = {}) => {
|
86
103
|
for (const key in obj) {
|
87
104
|
const path = prefix ? `${prefix}.${key}` : key;
|
88
|
-
|
89
|
-
|
105
|
+
const value = obj[key];
|
106
|
+
if (typeof value === "object" && value !== null && !getSymbol(value)) {
|
107
|
+
flatten(value, path, res);
|
90
108
|
} else {
|
91
|
-
res[path] =
|
109
|
+
res[path] = value;
|
92
110
|
}
|
93
111
|
}
|
94
112
|
return res;
|
95
113
|
};
|
114
|
+
var omit = (obj, properties) => Object.fromEntries(
|
115
|
+
Object.entries(obj).filter(([key]) => !properties.includes(key))
|
116
|
+
);
|
96
117
|
var expand = (obj) => {
|
97
118
|
return Object.entries(obj).reduce((res, [key, val]) => {
|
98
119
|
key.split(".").reduce((acc, part, i, arr) => {
|
@@ -275,23 +296,6 @@ var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
|
275
296
|
[type]: newNestedInstructions
|
276
297
|
};
|
277
298
|
};
|
278
|
-
var getSymbol = (value) => {
|
279
|
-
if (!isObject(value)) return null;
|
280
|
-
const objectValue = value;
|
281
|
-
if (RONIN_MODEL_SYMBOLS.QUERY in objectValue) {
|
282
|
-
return {
|
283
|
-
type: "query",
|
284
|
-
value: objectValue[RONIN_MODEL_SYMBOLS.QUERY]
|
285
|
-
};
|
286
|
-
}
|
287
|
-
if (RONIN_MODEL_SYMBOLS.EXPRESSION in objectValue) {
|
288
|
-
return {
|
289
|
-
type: "expression",
|
290
|
-
value: objectValue[RONIN_MODEL_SYMBOLS.EXPRESSION]
|
291
|
-
};
|
292
|
-
}
|
293
|
-
return null;
|
294
|
-
};
|
295
299
|
|
296
300
|
// src/instructions/with.ts
|
297
301
|
var getMatcher = (value, negative) => {
|
@@ -693,7 +697,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
693
697
|
if (!(modelSlug && slug)) return query;
|
694
698
|
const model = action === "create" && entity === "model" ? null : getModelBySlug(models, modelSlug);
|
695
699
|
if (entity === "model") {
|
696
|
-
let queryTypeDetails;
|
700
|
+
let queryTypeDetails = {};
|
697
701
|
if (action === "create") {
|
698
702
|
const newModel = jsonValue;
|
699
703
|
const modelWithFields = addDefaultModelFields(newModel, true);
|
@@ -749,6 +753,8 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
|
|
749
753
|
return handleSystemModel(models, dependencyStatements, "drop", systemModel);
|
750
754
|
});
|
751
755
|
}
|
756
|
+
const modelSlug2 = "to" in queryTypeDetails ? queryTypeDetails?.to?.slug : "with" in queryTypeDetails ? queryTypeDetails?.with?.slug : void 0;
|
757
|
+
if (modelSlug2 === "model") return null;
|
752
758
|
const queryTypeAction = action === "create" ? "add" : action === "alter" ? "set" : "remove";
|
753
759
|
return {
|
754
760
|
[queryTypeAction]: {
|
@@ -1178,32 +1184,44 @@ var handleOrderedBy = (model, instruction) => {
|
|
1178
1184
|
};
|
1179
1185
|
|
1180
1186
|
// src/instructions/selecting.ts
|
1181
|
-
var handleSelecting = (model, statementParams, instructions) => {
|
1187
|
+
var handleSelecting = (models, model, statementParams, instructions, options) => {
|
1182
1188
|
let isJoining = false;
|
1183
1189
|
let statement = instructions.selecting ? instructions.selecting.map((slug) => {
|
1184
1190
|
return getFieldFromModel(model, slug, "selecting").fieldSelector;
|
1185
1191
|
}).join(", ") : "*";
|
1186
1192
|
if (instructions.including) {
|
1187
|
-
const filteredObject = Object.entries(instructions.including).
|
1193
|
+
const filteredObject = Object.entries(instructions.including).flatMap(([key, value]) => {
|
1188
1194
|
const symbol = getSymbol(value);
|
1189
|
-
if (symbol) {
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1195
|
+
if (symbol?.type === "query") {
|
1196
|
+
isJoining = true;
|
1197
|
+
if (!options?.expandColumns) return null;
|
1198
|
+
const { queryModel: queryModelSlug } = splitQuery(symbol.value);
|
1199
|
+
const queryModel = getModelBySlug(models, queryModelSlug);
|
1200
|
+
const tableName = `including_${key}`;
|
1201
|
+
const duplicatedFields = queryModel.fields.filter((field) => {
|
1202
|
+
if (field.type === "group") return null;
|
1203
|
+
return model.fields.some((modelField) => modelField.slug === field.slug);
|
1204
|
+
}).filter((item) => item !== null);
|
1205
|
+
return duplicatedFields.map((field) => ({
|
1206
|
+
key: `${tableName}.${field.slug}`,
|
1207
|
+
value: {
|
1208
|
+
[RONIN_MODEL_SYMBOLS.EXPRESSION]: `${RONIN_MODEL_SYMBOLS.FIELD}${field.slug}`
|
1209
|
+
}
|
1210
|
+
}));
|
1197
1211
|
}
|
1198
|
-
return
|
1199
|
-
}).filter((entry) => entry !== null);
|
1212
|
+
return { key, value };
|
1213
|
+
}).filter((entry) => entry !== null).map((entry) => [entry.key, entry.value]);
|
1200
1214
|
const newObjectEntries = Object.entries(flatten(Object.fromEntries(filteredObject)));
|
1201
1215
|
if (newObjectEntries.length > 0) {
|
1202
1216
|
statement += ", ";
|
1203
1217
|
statement += newObjectEntries.map(([key, value]) => {
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1218
|
+
const symbol = getSymbol(value);
|
1219
|
+
if (symbol?.type === "expression") {
|
1220
|
+
value = `(${parseFieldExpression(model, "including", symbol.value)})`;
|
1221
|
+
} else {
|
1222
|
+
value = prepareStatementValue(statementParams, value);
|
1223
|
+
}
|
1224
|
+
return `${value} as "${key}"`;
|
1207
1225
|
}).join(", ");
|
1208
1226
|
}
|
1209
1227
|
}
|
@@ -1340,6 +1358,7 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
|
|
1340
1358
|
statementParams,
|
1341
1359
|
defaultQuery
|
1342
1360
|
);
|
1361
|
+
if (query === null) return { dependencies: [], main: dependencyStatements[0] };
|
1343
1362
|
const parsedQuery = splitQuery(query);
|
1344
1363
|
const { queryType, queryModel, queryInstructions } = parsedQuery;
|
1345
1364
|
const model = getModelBySlug(models, queryModel);
|
@@ -1349,10 +1368,16 @@ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
|
|
1349
1368
|
if (instructions && Object.hasOwn(instructions, "for")) {
|
1350
1369
|
instructions = handleFor(model, instructions);
|
1351
1370
|
}
|
1352
|
-
const { columns, isJoining } = handleSelecting(
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1371
|
+
const { columns, isJoining } = handleSelecting(
|
1372
|
+
models,
|
1373
|
+
model,
|
1374
|
+
statementParams,
|
1375
|
+
{
|
1376
|
+
selecting: instructions?.selecting,
|
1377
|
+
including: instructions?.including
|
1378
|
+
},
|
1379
|
+
options
|
1380
|
+
);
|
1356
1381
|
let statement = "";
|
1357
1382
|
switch (queryType) {
|
1358
1383
|
case "get":
|
@@ -1512,7 +1537,8 @@ var Transaction = class {
|
|
1512
1537
|
const result = compileQueryInput(
|
1513
1538
|
query,
|
1514
1539
|
modelListWithPresets,
|
1515
|
-
options?.inlineParams ? null : []
|
1540
|
+
options?.inlineParams ? null : [],
|
1541
|
+
{ expandColumns: options?.expandColumns }
|
1516
1542
|
);
|
1517
1543
|
dependencyStatements.push(...result.dependencies);
|
1518
1544
|
mainStatements.push(result.main);
|
@@ -1577,6 +1603,8 @@ var Transaction = class {
|
|
1577
1603
|
});
|
1578
1604
|
}
|
1579
1605
|
};
|
1606
|
+
var CLEAN_ROOT_MODEL = omit(ROOT_MODEL, ["system"]);
|
1580
1607
|
export {
|
1608
|
+
CLEAN_ROOT_MODEL as ROOT_MODEL,
|
1581
1609
|
Transaction
|
1582
1610
|
};
|