@ronin/compiler 0.6.0 → 0.7.0

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.js CHANGED
@@ -1,19 +1,23 @@
1
1
  // src/utils/helpers.ts
2
2
  import { init as cuid } from "@paralleldrive/cuid2";
3
- var RONIN_SCHEMA_SYMBOLS = {
3
+ var RONIN_MODEL_SYMBOLS = {
4
4
  // Represents a sub query.
5
5
  QUERY: "__RONIN_QUERY",
6
- // Represents the value of a field in a schema.
6
+ // Represents an expression that should be evaluated.
7
+ EXPRESSION: "__RONIN_EXPRESSION",
8
+ // Represents the value of a field in the model.
7
9
  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_",
10
+ // Represents the value of a field in the model of a parent query.
11
+ FIELD_PARENT: "__RONIN_FIELD_PARENT_",
12
+ // Represents the old value of a field in the parent model. Used for triggers.
13
+ FIELD_PARENT_OLD: "__RONIN_FIELD_PARENT_OLD_",
14
+ // Represents the new value of a field in the parent model. Used for triggers.
15
+ FIELD_PARENT_NEW: "__RONIN_FIELD_PARENT_NEW_",
12
16
  // Represents a value provided to a query preset.
13
17
  VALUE: "__RONIN_VALUE"
14
18
  };
15
- var RONIN_SCHEMA_FIELD_REGEX = new RegExp(
16
- `${RONIN_SCHEMA_SYMBOLS.FIELD}[a-zA-Z0-9]+`,
19
+ var RONIN_MODEL_FIELD_REGEX = new RegExp(
20
+ `${RONIN_MODEL_SYMBOLS.FIELD}[_a-zA-Z0-9]+`,
17
21
  "g"
18
22
  );
19
23
  var RoninError = class extends Error {
@@ -94,9 +98,9 @@ var expand = (obj) => {
94
98
  };
95
99
  var splitQuery = (query) => {
96
100
  const queryType = Object.keys(query)[0];
97
- const querySchema = Object.keys(query[queryType])[0];
98
- const queryInstructions = query[queryType][querySchema];
99
- return { queryType, querySchema, queryInstructions };
101
+ const queryModel = Object.keys(query[queryType])[0];
102
+ const queryInstructions = query[queryType][queryModel];
103
+ return { queryType, queryModel, queryInstructions };
100
104
  };
101
105
 
102
106
  // src/utils/statement.ts
@@ -112,40 +116,44 @@ var prepareStatementValue = (statementParams, value) => {
112
116
  const index = statementParams.push(formattedValue);
113
117
  return `?${index}`;
114
118
  };
115
- var composeFieldValues = (schemas, schema, statementParams, instructionName, value, options) => {
116
- const { field: schemaField, fieldSelector: selector } = getFieldFromSchema(
117
- schema,
119
+ var parseFieldExpression = (model, instructionName, expression, parentModel) => {
120
+ return expression.replace(RONIN_MODEL_FIELD_REGEX, (match) => {
121
+ let toReplace = RONIN_MODEL_SYMBOLS.FIELD;
122
+ let rootModel = model;
123
+ if (match.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT)) {
124
+ rootModel = parentModel;
125
+ toReplace = RONIN_MODEL_SYMBOLS.FIELD_PARENT;
126
+ if (match.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT_OLD)) {
127
+ rootModel.tableAlias = toReplace = RONIN_MODEL_SYMBOLS.FIELD_PARENT_OLD;
128
+ } else if (match.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT_NEW)) {
129
+ rootModel.tableAlias = toReplace = RONIN_MODEL_SYMBOLS.FIELD_PARENT_NEW;
130
+ }
131
+ }
132
+ const fieldSlug = match.replace(toReplace, "");
133
+ const field = getFieldFromModel(rootModel, fieldSlug, instructionName);
134
+ return field.fieldSelector;
135
+ });
136
+ };
137
+ var composeFieldValues = (models, model, statementParams, instructionName, value, options) => {
138
+ const { fieldSelector: conditionSelector } = getFieldFromModel(
139
+ model,
118
140
  options.fieldSlug,
119
- instructionName,
120
- options.rootTable
141
+ instructionName
121
142
  );
122
- const isSubQuery = isObject(value) && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY);
123
143
  const collectStatementValue = options.type !== "fields";
124
- let conditionSelector = selector;
144
+ const symbol = getSymbol(value);
125
145
  let conditionValue = value;
126
- if (isSubQuery && collectStatementValue) {
127
- conditionValue = `(${compileQueryInput(
128
- value[RONIN_SCHEMA_SYMBOLS.QUERY],
129
- schemas,
130
- statementParams
131
- ).main.statement})`;
132
- } else if (typeof value === "string" && value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD)) {
133
- let targetTable = `"${options.rootTable}"`;
134
- let toReplace = RONIN_SCHEMA_SYMBOLS.FIELD;
135
- if (value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD_OLD)) {
136
- targetTable = "OLD";
137
- toReplace = RONIN_SCHEMA_SYMBOLS.FIELD_OLD;
138
- } else if (value.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD_NEW)) {
139
- targetTable = "NEW";
140
- toReplace = RONIN_SCHEMA_SYMBOLS.FIELD_NEW;
146
+ if (symbol) {
147
+ if (symbol?.type === "expression") {
148
+ conditionValue = parseFieldExpression(
149
+ model,
150
+ instructionName,
151
+ symbol.value,
152
+ options.parentModel
153
+ );
141
154
  }
142
- conditionSelector = `${options.customTable ? `"${options.customTable}".` : ""}"${schemaField.slug}"`;
143
- conditionValue = `${targetTable}."${value.replace(toReplace, "")}"`;
144
- } else if (schemaField.type === "json" && instructionName === "to") {
145
- conditionSelector = `"${schemaField.slug}"`;
146
- if (collectStatementValue) {
147
- const preparedValue = prepareStatementValue(statementParams, value);
148
- conditionValue = `IIF(${conditionSelector} IS NULL, ${preparedValue}, json_patch(${conditionSelector}, ${preparedValue}))`;
155
+ if (symbol.type === "query" && collectStatementValue) {
156
+ conditionValue = `(${compileQueryInput(symbol.value, models, statementParams).main.statement})`;
149
157
  }
150
158
  } else if (collectStatementValue) {
151
159
  conditionValue = prepareStatementValue(statementParams, value);
@@ -154,11 +162,11 @@ var composeFieldValues = (schemas, schema, statementParams, instructionName, val
154
162
  if (options.type === "values") return conditionValue;
155
163
  return `${conditionSelector} ${WITH_CONDITIONS[options.condition || "being"](conditionValue, value)}`;
156
164
  };
157
- var composeConditions = (schemas, schema, statementParams, instructionName, value, options) => {
165
+ var composeConditions = (models, model, statementParams, instructionName, value, options) => {
158
166
  const isNested = isObject(value) && Object.keys(value).length > 0;
159
167
  if (isNested && Object.keys(value).every((key) => key in WITH_CONDITIONS)) {
160
168
  const conditions = Object.entries(value).map(
161
- ([conditionType, checkValue]) => composeConditions(schemas, schema, statementParams, instructionName, checkValue, {
169
+ ([conditionType, checkValue]) => composeConditions(models, model, statementParams, instructionName, checkValue, {
162
170
  ...options,
163
171
  condition: conditionType
164
172
  })
@@ -166,48 +174,42 @@ var composeConditions = (schemas, schema, statementParams, instructionName, valu
166
174
  return conditions.join(" AND ");
167
175
  }
168
176
  if (options.fieldSlug) {
169
- const fieldDetails = getFieldFromSchema(
170
- schema,
171
- options.fieldSlug,
172
- instructionName,
173
- options.rootTable
174
- );
175
- const { field: schemaField } = fieldDetails;
176
- const consumeJSON = schemaField.type === "json" && instructionName === "to";
177
- const isSubQuery = isNested && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY);
178
- if (!(isObject(value) || Array.isArray(value)) || isSubQuery || consumeJSON) {
177
+ const fieldDetails = getFieldFromModel(model, options.fieldSlug, instructionName);
178
+ const { field: modelField } = fieldDetails;
179
+ const consumeJSON = modelField.type === "json" && instructionName === "to";
180
+ if (!(isObject(value) || Array.isArray(value)) || getSymbol(value) || consumeJSON) {
179
181
  return composeFieldValues(
180
- schemas,
181
- schema,
182
+ models,
183
+ model,
182
184
  statementParams,
183
185
  instructionName,
184
186
  value,
185
187
  { ...options, fieldSlug: options.fieldSlug }
186
188
  );
187
189
  }
188
- if (schemaField.type === "reference" && isNested) {
190
+ if (modelField.type === "reference" && isNested) {
189
191
  const keys = Object.keys(value);
190
192
  const values = Object.values(value);
191
193
  let recordTarget;
192
194
  if (keys.length === 1 && keys[0] === "id") {
193
195
  recordTarget = values[0];
194
196
  } else {
195
- const relatedSchema = getSchemaBySlug(schemas, schemaField.target.slug);
197
+ const relatedModel = getModelBySlug(models, modelField.target.slug);
196
198
  const subQuery = {
197
199
  get: {
198
- [relatedSchema.slug]: {
200
+ [relatedModel.slug]: {
199
201
  with: value,
200
202
  selecting: ["id"]
201
203
  }
202
204
  }
203
205
  };
204
206
  recordTarget = {
205
- [RONIN_SCHEMA_SYMBOLS.QUERY]: subQuery
207
+ [RONIN_MODEL_SYMBOLS.QUERY]: subQuery
206
208
  };
207
209
  }
208
210
  return composeConditions(
209
- schemas,
210
- schema,
211
+ models,
212
+ model,
211
213
  statementParams,
212
214
  instructionName,
213
215
  recordTarget,
@@ -218,7 +220,7 @@ var composeConditions = (schemas, schema, statementParams, instructionName, valu
218
220
  if (isNested) {
219
221
  const conditions = Object.entries(value).map(([field, value2]) => {
220
222
  const nestedFieldSlug = options.fieldSlug ? `${options.fieldSlug}.${field}` : field;
221
- return composeConditions(schemas, schema, statementParams, instructionName, value2, {
223
+ return composeConditions(models, model, statementParams, instructionName, value2, {
222
224
  ...options,
223
225
  fieldSlug: nestedFieldSlug
224
226
  });
@@ -229,14 +231,7 @@ var composeConditions = (schemas, schema, statementParams, instructionName, valu
229
231
  }
230
232
  if (Array.isArray(value)) {
231
233
  const conditions = value.map(
232
- (filter) => composeConditions(
233
- schemas,
234
- schema,
235
- statementParams,
236
- instructionName,
237
- filter,
238
- options
239
- )
234
+ (filter) => composeConditions(models, model, statementParams, instructionName, filter, options)
240
235
  );
241
236
  return conditions.join(" OR ");
242
237
  }
@@ -267,6 +262,23 @@ var formatIdentifiers = ({ identifiers }, queryInstructions) => {
267
262
  [type]: newNestedInstructions
268
263
  };
269
264
  };
265
+ var getSymbol = (value) => {
266
+ if (!isObject(value)) return null;
267
+ const objectValue = value;
268
+ if (RONIN_MODEL_SYMBOLS.QUERY in objectValue) {
269
+ return {
270
+ type: "query",
271
+ value: objectValue[RONIN_MODEL_SYMBOLS.QUERY]
272
+ };
273
+ }
274
+ if (RONIN_MODEL_SYMBOLS.EXPRESSION in objectValue) {
275
+ return {
276
+ type: "expression",
277
+ value: objectValue[RONIN_MODEL_SYMBOLS.EXPRESSION]
278
+ };
279
+ }
280
+ return null;
281
+ };
270
282
 
271
283
  // src/instructions/with.ts
272
284
  var getMatcher = (value, negative) => {
@@ -291,41 +303,37 @@ var WITH_CONDITIONS = {
291
303
  lessThan: (value) => `< ${value}`,
292
304
  lessOrEqual: (value) => `<= ${value}`
293
305
  };
294
- var handleWith = (schemas, schema, statementParams, instruction, rootTable) => {
306
+ var handleWith = (models, model, statementParams, instruction, parentModel) => {
295
307
  const subStatement = composeConditions(
296
- schemas,
297
- schema,
308
+ models,
309
+ model,
298
310
  statementParams,
299
311
  "with",
300
312
  instruction,
301
- { rootTable }
313
+ { parentModel }
302
314
  );
303
315
  return `(${subStatement})`;
304
316
  };
305
317
 
306
- // src/utils/schema.ts
318
+ // src/utils/model.ts
307
319
  import title from "title";
308
- var getSchemaBySlug = (schemas, slug) => {
309
- const schema = schemas.find((schema2) => {
310
- return schema2.slug === slug || schema2.pluralSlug === slug;
320
+ var getModelBySlug = (models, slug) => {
321
+ const model = models.find((model2) => {
322
+ return model2.slug === slug || model2.pluralSlug === slug;
311
323
  });
312
- if (!schema) {
324
+ if (!model) {
313
325
  throw new RoninError({
314
- message: `No matching schema with either Slug or Plural Slug of "${slug}" could be found.`,
315
- code: "SCHEMA_NOT_FOUND"
326
+ message: `No matching model with either Slug or Plural Slug of "${slug}" could be found.`,
327
+ code: "MODEL_NOT_FOUND"
316
328
  });
317
329
  }
318
- return schema;
319
- };
320
- var getTableForSchema = (schema) => {
321
- return convertToSnakeCase(schema.pluralSlug);
330
+ return model;
322
331
  };
323
- var composeMetaSchemaSlug = (suffix) => convertToCamelCase(`ronin_${suffix}`);
324
- var composeAssociationSchemaSlug = (schema, field) => composeMetaSchemaSlug(`${schema.slug}_${field.slug}`);
325
- var getFieldSelector = (field, fieldPath, rootTable) => {
326
- const symbol = rootTable?.startsWith(RONIN_SCHEMA_SYMBOLS.FIELD) ? `${rootTable.replace(RONIN_SCHEMA_SYMBOLS.FIELD, "").slice(0, -1)}.` : "";
327
- const tablePrefix = symbol || (rootTable ? `"${rootTable}".` : "");
328
- if (field.type === "json") {
332
+ var composeAssociationModelSlug = (model, field) => convertToCamelCase(`ronin_link_${model.slug}_${field.slug}`);
333
+ var getFieldSelector = (model, field, fieldPath, instructionName) => {
334
+ const symbol = model.tableAlias?.startsWith(RONIN_MODEL_SYMBOLS.FIELD_PARENT) ? `${model.tableAlias.replace(RONIN_MODEL_SYMBOLS.FIELD_PARENT, "").slice(0, -1)}.` : "";
335
+ const tablePrefix = symbol || (model.tableAlias ? `"${model.tableAlias}".` : "");
336
+ if (field.type === "json" && instructionName !== "to") {
329
337
  const dotParts = fieldPath.split(".");
330
338
  const columnName = tablePrefix + dotParts.shift();
331
339
  const jsonField = dotParts.join(".");
@@ -333,28 +341,33 @@ var getFieldSelector = (field, fieldPath, rootTable) => {
333
341
  }
334
342
  return `${tablePrefix}"${fieldPath}"`;
335
343
  };
336
- var getFieldFromSchema = (schema, fieldPath, instructionName, rootTable) => {
344
+ var getFieldFromModel = (model, fieldPath, instructionName) => {
337
345
  const errorPrefix = `Field "${fieldPath}" defined for \`${instructionName}\``;
338
- const schemaFields = schema.fields || [];
339
- let schemaField;
346
+ const modelFields = model.fields || [];
347
+ let modelField;
340
348
  if (fieldPath.includes(".")) {
341
- schemaField = schemaFields.find((field) => field.slug === fieldPath.split(".")[0]);
342
- if (schemaField?.type === "json") {
343
- const fieldSelector2 = getFieldSelector(schemaField, fieldPath, rootTable);
344
- return { field: schemaField, fieldSelector: fieldSelector2 };
349
+ modelField = modelFields.find((field) => field.slug === fieldPath.split(".")[0]);
350
+ if (modelField?.type === "json") {
351
+ const fieldSelector2 = getFieldSelector(
352
+ model,
353
+ modelField,
354
+ fieldPath,
355
+ instructionName
356
+ );
357
+ return { field: modelField, fieldSelector: fieldSelector2 };
345
358
  }
346
359
  }
347
- schemaField = schemaFields.find((field) => field.slug === fieldPath);
348
- if (!schemaField) {
360
+ modelField = modelFields.find((field) => field.slug === fieldPath);
361
+ if (!modelField) {
349
362
  throw new RoninError({
350
- message: `${errorPrefix} does not exist in schema "${schema.name}".`,
363
+ message: `${errorPrefix} does not exist in model "${model.name}".`,
351
364
  code: "FIELD_NOT_FOUND",
352
365
  field: fieldPath,
353
366
  queries: null
354
367
  });
355
368
  }
356
- const fieldSelector = getFieldSelector(schemaField, fieldPath, rootTable);
357
- return { field: schemaField, fieldSelector };
369
+ const fieldSelector = getFieldSelector(model, modelField, fieldPath, instructionName);
370
+ return { field: modelField, fieldSelector };
358
371
  };
359
372
  var slugToName = (slug) => {
360
373
  const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
@@ -372,36 +385,37 @@ var pluralize = (word) => {
372
385
  }
373
386
  return `${word}s`;
374
387
  };
375
- var schemaSettings = [
388
+ var modelSettings = [
376
389
  ["pluralSlug", "slug", pluralize],
377
390
  ["name", "slug", slugToName],
378
391
  ["pluralName", "pluralSlug", slugToName],
379
- ["idPrefix", "slug", (slug) => slug.slice(0, 3)]
392
+ ["idPrefix", "slug", (slug) => slug.slice(0, 3)],
393
+ ["table", "pluralSlug", convertToSnakeCase]
380
394
  ];
381
- var addDefaultSchemaFields = (schema, isNew) => {
382
- const copiedSchema = { ...schema };
383
- for (const [setting, base, generator] of schemaSettings) {
384
- if (copiedSchema[setting] || !copiedSchema[base]) continue;
385
- copiedSchema[setting] = generator(copiedSchema[base]);
395
+ var addDefaultModelFields = (model, isNew) => {
396
+ const copiedModel = { ...model };
397
+ for (const [setting, base, generator] of modelSettings) {
398
+ if (copiedModel[setting] || !copiedModel[base]) continue;
399
+ copiedModel[setting] = generator(copiedModel[base]);
386
400
  }
387
- const newFields = copiedSchema.fields || [];
401
+ const newFields = copiedModel.fields || [];
388
402
  if (isNew || newFields.length > 0) {
389
- if (!copiedSchema.identifiers) copiedSchema.identifiers = {};
390
- if (!copiedSchema.identifiers.name) {
403
+ if (!copiedModel.identifiers) copiedModel.identifiers = {};
404
+ if (!copiedModel.identifiers.name) {
391
405
  const suitableField = newFields.find(
392
406
  (field) => field.type === "string" && field.required === true && ["name"].includes(field.slug)
393
407
  );
394
- copiedSchema.identifiers.name = suitableField?.slug || "id";
408
+ copiedModel.identifiers.name = suitableField?.slug || "id";
395
409
  }
396
- if (!copiedSchema.identifiers.slug) {
410
+ if (!copiedModel.identifiers.slug) {
397
411
  const suitableField = newFields.find(
398
412
  (field) => field.type === "string" && field.unique === true && field.required === true && ["slug", "handle"].includes(field.slug)
399
413
  );
400
- copiedSchema.identifiers.slug = suitableField?.slug || "id";
414
+ copiedModel.identifiers.slug = suitableField?.slug || "id";
401
415
  }
402
- copiedSchema.fields = [...SYSTEM_FIELDS, ...newFields];
416
+ copiedModel.fields = [...SYSTEM_FIELDS, ...newFields];
403
417
  }
404
- return copiedSchema;
418
+ return copiedModel;
405
419
  };
406
420
  var SYSTEM_FIELDS = [
407
421
  {
@@ -441,9 +455,9 @@ var SYSTEM_FIELDS = [
441
455
  slug: "ronin.updatedBy"
442
456
  }
443
457
  ];
444
- var SYSTEM_SCHEMAS = [
458
+ var SYSTEM_MODELS = [
445
459
  {
446
- slug: "schema",
460
+ slug: "model",
447
461
  identifiers: {
448
462
  name: "name",
449
463
  slug: "slug"
@@ -454,14 +468,14 @@ var SYSTEM_SCHEMAS = [
454
468
  { slug: "slug", type: "string" },
455
469
  { slug: "pluralSlug", type: "string" },
456
470
  { slug: "idPrefix", type: "string" },
471
+ { slug: "table", type: "string" },
457
472
  { slug: "identifiers", type: "group" },
458
473
  { slug: "identifiers.name", type: "string" },
459
474
  { slug: "identifiers.slug", type: "string" },
460
475
  { slug: "fields", type: "json" },
461
476
  { slug: "indexes", type: "json" },
462
477
  { slug: "triggers", type: "json" },
463
- { slug: "including", type: "json" },
464
- { slug: "for", type: "json" }
478
+ { slug: "presets", type: "json" }
465
479
  ]
466
480
  },
467
481
  {
@@ -475,9 +489,9 @@ var SYSTEM_SCHEMAS = [
475
489
  { slug: "slug", type: "string", required: true },
476
490
  { slug: "type", type: "string", required: true },
477
491
  {
478
- slug: "schema",
492
+ slug: "model",
479
493
  type: "reference",
480
- target: { slug: "schema" },
494
+ target: { slug: "model" },
481
495
  required: true
482
496
  },
483
497
  { slug: "required", type: "boolean" },
@@ -485,7 +499,7 @@ var SYSTEM_SCHEMAS = [
485
499
  { slug: "unique", type: "boolean" },
486
500
  { slug: "autoIncrement", type: "boolean" },
487
501
  // Only allowed for fields of type "reference".
488
- { slug: "target", type: "reference", target: { slug: "schema" } },
502
+ { slug: "target", type: "reference", target: { slug: "model" } },
489
503
  { slug: "kind", type: "string" },
490
504
  { slug: "actions", type: "group" },
491
505
  { slug: "actions.onDelete", type: "string" },
@@ -501,9 +515,9 @@ var SYSTEM_SCHEMAS = [
501
515
  fields: [
502
516
  { slug: "slug", type: "string", required: true },
503
517
  {
504
- slug: "schema",
518
+ slug: "model",
505
519
  type: "reference",
506
- target: { slug: "schema" },
520
+ target: { slug: "model" },
507
521
  required: true
508
522
  },
509
523
  { slug: "unique", type: "boolean" },
@@ -520,9 +534,9 @@ var SYSTEM_SCHEMAS = [
520
534
  fields: [
521
535
  { slug: "slug", type: "string", required: true },
522
536
  {
523
- slug: "schema",
537
+ slug: "model",
524
538
  type: "reference",
525
- target: { slug: "schema" },
539
+ target: { slug: "model" },
526
540
  required: true
527
541
  },
528
542
  { slug: "when", type: "string", required: true },
@@ -531,90 +545,124 @@ var SYSTEM_SCHEMAS = [
531
545
  { slug: "effects", type: "json", required: true },
532
546
  { slug: "fields", type: "json" }
533
547
  ]
548
+ },
549
+ {
550
+ slug: "preset",
551
+ fields: [
552
+ { slug: "slug", type: "string", required: true },
553
+ {
554
+ slug: "model",
555
+ type: "reference",
556
+ target: { slug: "model" },
557
+ required: true
558
+ },
559
+ { slug: "instructions", type: "json", required: true }
560
+ ]
534
561
  }
535
- ].map((schema) => addDefaultSchemaFields(schema, true));
536
- var SYSTEM_SCHEMA_SLUGS = SYSTEM_SCHEMAS.flatMap(({ slug, pluralSlug }) => [
562
+ ].map((model) => addDefaultModelFields(model, true));
563
+ var SYSTEM_MODEL_SLUGS = SYSTEM_MODELS.flatMap(({ slug, pluralSlug }) => [
537
564
  slug,
538
565
  pluralSlug
539
566
  ]);
540
- var addSystemSchemas = (schemas) => {
541
- const associativeSchemas = schemas.flatMap((schema) => {
542
- const addedSchemas = [];
543
- for (const field of schema.fields || []) {
567
+ var addSystemModels = (models) => {
568
+ const associativeModels = models.flatMap((model) => {
569
+ const addedModels = [];
570
+ for (const field of model.fields || []) {
544
571
  if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
545
- const relatedSchema = getSchemaBySlug(schemas, field.target.slug);
546
- let fieldSlug = relatedSchema.slug;
572
+ const relatedModel = getModelBySlug(models, field.target.slug);
573
+ let fieldSlug = relatedModel.slug;
547
574
  if (field.kind === "many") {
548
- fieldSlug = composeAssociationSchemaSlug(schema, field);
549
- addedSchemas.push({
575
+ fieldSlug = composeAssociationModelSlug(model, field);
576
+ addedModels.push({
550
577
  pluralSlug: fieldSlug,
551
578
  slug: fieldSlug,
579
+ associationSlug: field.slug,
552
580
  fields: [
553
581
  {
554
582
  slug: "source",
555
583
  type: "reference",
556
- target: { slug: schema.slug }
584
+ target: { slug: model.slug }
557
585
  },
558
586
  {
559
587
  slug: "target",
560
588
  type: "reference",
561
- target: { slug: relatedSchema.slug }
589
+ target: { slug: relatedModel.slug }
562
590
  }
563
591
  ]
564
592
  });
565
593
  }
566
594
  }
567
595
  }
568
- return addedSchemas;
596
+ return addedModels;
569
597
  });
570
- return [...SYSTEM_SCHEMAS, ...associativeSchemas, ...schemas];
598
+ return [...SYSTEM_MODELS, ...associativeModels, ...models];
571
599
  };
572
- var addDefaultSchemaShortcuts = (list, schema) => {
573
- const defaultIncluding = {};
574
- for (const field of schema.fields || []) {
600
+ var addDefaultModelPresets = (list, model) => {
601
+ const defaultPresets = [];
602
+ for (const field of model.fields || []) {
575
603
  if (field.type === "reference" && !field.slug.startsWith("ronin.")) {
576
- const relatedSchema = getSchemaBySlug(list, field.target.slug);
577
- let fieldSlug = relatedSchema.slug;
578
- if (field.kind === "many") {
579
- fieldSlug = composeAssociationSchemaSlug(schema, field);
580
- }
581
- defaultIncluding[field.slug] = {
582
- get: {
583
- [fieldSlug]: {
584
- with: {
585
- // Compare the `id` field of the related schema to the reference field on
586
- // the root schema (`field.slug`).
587
- id: `${RONIN_SCHEMA_SYMBOLS.FIELD}${field.slug}`
604
+ const relatedModel = getModelBySlug(list, field.target.slug);
605
+ if (field.kind === "many") continue;
606
+ defaultPresets.push({
607
+ instructions: {
608
+ including: {
609
+ [field.slug]: {
610
+ [RONIN_MODEL_SYMBOLS.QUERY]: {
611
+ get: {
612
+ [relatedModel.slug]: {
613
+ with: {
614
+ // Compare the `id` field of the related model to the reference field on
615
+ // the root model (`field.slug`).
616
+ id: {
617
+ [RONIN_MODEL_SYMBOLS.EXPRESSION]: `${RONIN_MODEL_SYMBOLS.FIELD_PARENT}${field.slug}`
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
588
623
  }
589
624
  }
590
- }
591
- };
625
+ },
626
+ slug: field.slug
627
+ });
592
628
  }
593
629
  }
594
- const childSchemas = list.map((subSchema) => {
595
- const field = subSchema.fields?.find((field2) => {
596
- return field2.type === "reference" && field2.target.slug === schema.slug;
630
+ const childModels = list.map((subModel) => {
631
+ const field = subModel.fields?.find((field2) => {
632
+ return field2.type === "reference" && field2.target.slug === model.slug;
597
633
  });
598
634
  if (!field) return null;
599
- return { schema: subSchema, field };
635
+ return { model: subModel, field };
600
636
  }).filter((match) => match !== null);
601
- for (const childMatch of childSchemas) {
602
- const { schema: childSchema, field: childField } = childMatch;
603
- const pluralSlug = childSchema.pluralSlug;
604
- defaultIncluding[pluralSlug] = {
605
- get: {
606
- [pluralSlug]: {
607
- with: {
608
- [childField.slug]: `${RONIN_SCHEMA_SYMBOLS.FIELD}id`
637
+ for (const childMatch of childModels) {
638
+ const { model: childModel, field: childField } = childMatch;
639
+ const pluralSlug = childModel.pluralSlug;
640
+ const presetSlug = childModel.associationSlug || pluralSlug;
641
+ defaultPresets.push({
642
+ instructions: {
643
+ including: {
644
+ [presetSlug]: {
645
+ [RONIN_MODEL_SYMBOLS.QUERY]: {
646
+ get: {
647
+ [pluralSlug]: {
648
+ with: {
649
+ [childField.slug]: {
650
+ [RONIN_MODEL_SYMBOLS.EXPRESSION]: `${RONIN_MODEL_SYMBOLS.FIELD_PARENT}id`
651
+ }
652
+ }
653
+ }
654
+ }
655
+ }
609
656
  }
610
657
  }
611
- }
612
- };
658
+ },
659
+ slug: presetSlug
660
+ });
613
661
  }
614
- if (Object.keys(defaultIncluding).length > 0) {
615
- schema.including = { ...defaultIncluding, ...schema.including };
662
+ if (Object.keys(defaultPresets).length > 0) {
663
+ model.presets = [...defaultPresets, ...model.presets || []];
616
664
  }
617
- return schema;
665
+ return model;
618
666
  };
619
667
  var mappedInstructions = {
620
668
  create: "to",
@@ -650,35 +698,31 @@ var getFieldStatement = (field) => {
650
698
  }
651
699
  return statement;
652
700
  };
653
- var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
654
- const {
655
- queryType,
656
- querySchema,
657
- queryInstructions: queryInstructionsRaw
658
- } = queryDetails;
701
+ var addModelQueries = (models, queryDetails, dependencyStatements) => {
702
+ const { queryType, queryModel, queryInstructions: queryInstructionsRaw } = queryDetails;
659
703
  const queryInstructions = queryInstructionsRaw;
660
704
  if (!["create", "set", "drop"].includes(queryType)) return queryInstructions;
661
- if (!SYSTEM_SCHEMA_SLUGS.includes(querySchema)) return queryInstructions;
705
+ if (!SYSTEM_MODEL_SLUGS.includes(queryModel)) return queryInstructions;
662
706
  const instructionName = mappedInstructions[queryType];
663
707
  const instructionList = queryInstructions[instructionName];
664
- const kind = getSchemaBySlug(SYSTEM_SCHEMAS, querySchema).pluralSlug;
708
+ const kind = getModelBySlug(SYSTEM_MODELS, queryModel).pluralSlug;
665
709
  let tableAction = "ALTER";
666
710
  let queryTypeReadable = null;
667
711
  switch (queryType) {
668
712
  case "create": {
669
- if (kind === "schemas" || kind === "indexes" || kind === "triggers") {
713
+ if (kind === "models" || kind === "indexes" || kind === "triggers") {
670
714
  tableAction = "CREATE";
671
715
  }
672
716
  queryTypeReadable = "creating";
673
717
  break;
674
718
  }
675
719
  case "set": {
676
- if (kind === "schemas") tableAction = "ALTER";
720
+ if (kind === "models") tableAction = "ALTER";
677
721
  queryTypeReadable = "updating";
678
722
  break;
679
723
  }
680
724
  case "drop": {
681
- if (kind === "schemas" || kind === "indexes" || kind === "triggers") {
725
+ if (kind === "models" || kind === "indexes" || kind === "triggers") {
682
726
  tableAction = "DROP";
683
727
  }
684
728
  queryTypeReadable = "deleting";
@@ -693,18 +737,18 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
693
737
  fields: ["slug"]
694
738
  });
695
739
  }
696
- const schemaInstruction = instructionList?.schema;
697
- const schemaSlug = schemaInstruction?.slug?.being || schemaInstruction?.slug;
698
- if (kind !== "schemas" && !schemaSlug) {
740
+ const modelInstruction = instructionList?.model;
741
+ const modelSlug = modelInstruction?.slug?.being || modelInstruction?.slug;
742
+ if (kind !== "models" && !modelSlug) {
699
743
  throw new RoninError({
700
- message: `When ${queryTypeReadable} ${kind}, a \`schema.slug\` field must be provided in the \`${instructionName}\` instruction.`,
744
+ message: `When ${queryTypeReadable} ${kind}, a \`model.slug\` field must be provided in the \`${instructionName}\` instruction.`,
701
745
  code: "MISSING_FIELD",
702
- fields: ["schema.slug"]
746
+ fields: ["model.slug"]
703
747
  });
704
748
  }
705
- const usableSlug = kind === "schemas" ? slug : schemaSlug;
749
+ const usableSlug = kind === "models" ? slug : modelSlug;
706
750
  const tableName = convertToSnakeCase(pluralize(usableSlug));
707
- const targetSchema = kind === "schemas" && queryType === "create" ? null : getSchemaBySlug(schemas, usableSlug);
751
+ const targetModel = kind === "models" && queryType === "create" ? null : getModelBySlug(models, usableSlug);
708
752
  if (kind === "indexes") {
709
753
  const indexName = convertToSnakeCase(slug);
710
754
  const unique = instructionList?.unique;
@@ -713,16 +757,13 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
713
757
  const params = [];
714
758
  let statement2 = `${tableAction}${unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
715
759
  if (queryType === "create") {
716
- const schema = targetSchema;
760
+ const model = targetModel;
717
761
  const columns = fields.map((field) => {
718
762
  let fieldSelector = "";
719
763
  if ("slug" in field) {
720
- ({ fieldSelector } = getFieldFromSchema(schema, field.slug, "to"));
764
+ ({ fieldSelector } = getFieldFromModel(model, field.slug, "to"));
721
765
  } else if ("expression" in field) {
722
- fieldSelector = field.expression.replace(RONIN_SCHEMA_FIELD_REGEX, (match) => {
723
- const fieldSlug = match.replace(RONIN_SCHEMA_SYMBOLS.FIELD, "");
724
- return getFieldFromSchema(schema, fieldSlug, "to").fieldSelector;
725
- });
766
+ fieldSelector = parseFieldExpression(model, "to", field.expression, model);
726
767
  }
727
768
  if (field.collation) fieldSelector += ` COLLATE ${field.collation}`;
728
769
  if (field.order) fieldSelector += ` ${field.order}`;
@@ -731,8 +772,8 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
731
772
  statement2 += ` ON "${tableName}" (${columns.join(", ")})`;
732
773
  if (filterQuery) {
733
774
  const withStatement = handleWith(
734
- schemas,
735
- targetSchema,
775
+ models,
776
+ targetModel,
736
777
  params,
737
778
  filterQuery
738
779
  );
@@ -747,6 +788,7 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
747
788
  const params = [];
748
789
  let statement2 = `${tableAction} TRIGGER "${triggerName}"`;
749
790
  if (queryType === "create") {
791
+ const currentModel = targetModel;
750
792
  const { when, action } = instructionList;
751
793
  const statementParts = [`${when} ${action}`];
752
794
  const effectQueries = instructionList?.effects;
@@ -756,33 +798,33 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
756
798
  if (action !== "UPDATE") {
757
799
  throw new RoninError({
758
800
  message: `When ${queryTypeReadable} ${kind}, targeting specific fields requires the \`UPDATE\` action.`,
759
- code: "INVALID_SCHEMA_VALUE",
801
+ code: "INVALID_MODEL_VALUE",
760
802
  fields: ["action"]
761
803
  });
762
804
  }
763
805
  const fieldSelectors = fields.map((field) => {
764
- return getFieldFromSchema(targetSchema, field.slug, "to").fieldSelector;
806
+ return getFieldFromModel(currentModel, field.slug, "to").fieldSelector;
765
807
  });
766
808
  statementParts.push(`OF (${fieldSelectors.join(", ")})`);
767
809
  }
768
810
  statementParts.push("ON", `"${tableName}"`);
769
- if (filterQuery || effectQueries.some((query) => findInObject(query, RONIN_SCHEMA_SYMBOLS.FIELD))) {
811
+ if (filterQuery || effectQueries.some((query) => findInObject(query, RONIN_MODEL_SYMBOLS.FIELD))) {
770
812
  statementParts.push("FOR EACH ROW");
771
813
  }
772
814
  if (filterQuery) {
773
- const tablePlaceholder = action === "DELETE" ? RONIN_SCHEMA_SYMBOLS.FIELD_OLD : RONIN_SCHEMA_SYMBOLS.FIELD_NEW;
815
+ const tableAlias = action === "DELETE" ? RONIN_MODEL_SYMBOLS.FIELD_PARENT_OLD : RONIN_MODEL_SYMBOLS.FIELD_PARENT_NEW;
774
816
  const withStatement = handleWith(
775
- schemas,
776
- targetSchema,
817
+ models,
818
+ { ...currentModel, tableAlias },
777
819
  params,
778
- filterQuery,
779
- tablePlaceholder
820
+ filterQuery
780
821
  );
781
822
  statementParts.push("WHEN", `(${withStatement})`);
782
823
  }
783
824
  const effectStatements = effectQueries.map((effectQuery) => {
784
- return compileQueryInput(effectQuery, schemas, params, {
785
- returning: false
825
+ return compileQueryInput(effectQuery, models, params, {
826
+ returning: false,
827
+ parentModel: currentModel
786
828
  }).main.statement;
787
829
  });
788
830
  if (effectStatements.length > 1) statementParts.push("BEGIN");
@@ -794,14 +836,14 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
794
836
  return queryInstructions;
795
837
  }
796
838
  const statement = `${tableAction} TABLE "${tableName}"`;
797
- if (kind === "schemas") {
839
+ if (kind === "models") {
798
840
  if (queryType === "create" || queryType === "set") {
799
- const schemaWithFields = addDefaultSchemaFields(
841
+ const modelWithFields = addDefaultModelFields(
800
842
  queryInstructions.to,
801
843
  queryType === "create"
802
844
  );
803
- const schemaWithShortcuts = addDefaultSchemaShortcuts(schemas, schemaWithFields);
804
- queryInstructions.to = schemaWithShortcuts;
845
+ const modelWithPresets = addDefaultModelPresets(models, modelWithFields);
846
+ queryInstructions.to = modelWithPresets;
805
847
  }
806
848
  if (queryType === "create") {
807
849
  const { fields } = queryInstructions.to;
@@ -810,7 +852,7 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
810
852
  statement: `${statement} (${columns.join(", ")})`,
811
853
  params: []
812
854
  });
813
- schemas.push(queryInstructions.to);
855
+ models.push(queryInstructions.to);
814
856
  } else if (queryType === "set") {
815
857
  const newSlug = queryInstructions.to?.pluralSlug;
816
858
  if (newSlug) {
@@ -820,9 +862,9 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
820
862
  params: []
821
863
  });
822
864
  }
823
- Object.assign(targetSchema, queryInstructions.to);
865
+ Object.assign(targetModel, queryInstructions.to);
824
866
  } else if (queryType === "drop") {
825
- schemas.splice(schemas.indexOf(targetSchema), 1);
867
+ models.splice(models.indexOf(targetModel), 1);
826
868
  dependencyStatements.push({ statement, params: [] });
827
869
  }
828
870
  return queryInstructions;
@@ -861,7 +903,7 @@ var addSchemaQueries = (schemas, queryDetails, dependencyStatements) => {
861
903
  // src/instructions/before-after.ts
862
904
  var CURSOR_SEPARATOR = ",";
863
905
  var CURSOR_NULL_PLACEHOLDER = "RONIN_NULL";
864
- var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) => {
906
+ var handleBeforeOrAfter = (model, statementParams, instructions) => {
865
907
  if (!(instructions.before || instructions.after)) {
866
908
  throw new RoninError({
867
909
  message: "The `before` or `after` instruction must not be empty.",
@@ -892,7 +934,7 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
892
934
  if (value === CURSOR_NULL_PLACEHOLDER) {
893
935
  return "NULL";
894
936
  }
895
- const { field } = getFieldFromSchema(schema, key, "orderedBy");
937
+ const { field } = getFieldFromModel(model, key, "orderedBy");
896
938
  if (field.type === "boolean") {
897
939
  return prepareStatementValue(statementParams, value === "true");
898
940
  }
@@ -918,12 +960,7 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
918
960
  for (let j = 0; j <= i; j++) {
919
961
  const key = keys[j];
920
962
  const value = values[j];
921
- let { field, fieldSelector } = getFieldFromSchema(
922
- schema,
923
- key,
924
- "orderedBy",
925
- rootTable
926
- );
963
+ let { field, fieldSelector } = getFieldFromModel(model, key, "orderedBy");
927
964
  if (j === i) {
928
965
  const closingParentheses = ")".repeat(condition.length);
929
966
  const operator = value === "NULL" ? "IS NOT" : compareOperators[j];
@@ -945,57 +982,66 @@ var handleBeforeOrAfter = (schema, statementParams, instructions, rootTable) =>
945
982
  };
946
983
 
947
984
  // src/instructions/for.ts
948
- var handleFor = (schemas, schema, statementParams, instruction, rootTable) => {
949
- let statement = "";
950
- if (!instruction) return statement;
951
- for (const shortcut in instruction) {
952
- const args = instruction[shortcut];
953
- const forFilter = schema.for?.[shortcut];
954
- if (!forFilter) {
985
+ var handleFor = (model, instructions) => {
986
+ const normalizedFor = Array.isArray(instructions.for) ? Object.fromEntries(instructions.for.map((presetSlug) => [presetSlug, null])) : instructions.for;
987
+ for (const presetSlug in normalizedFor) {
988
+ const arg = normalizedFor[presetSlug];
989
+ const preset = model.presets?.find((preset2) => preset2.slug === presetSlug);
990
+ if (!preset) {
955
991
  throw new RoninError({
956
- message: `The provided \`for\` shortcut "${shortcut}" does not exist in schema "${schema.name}".`,
957
- code: "INVALID_FOR_VALUE"
992
+ message: `Preset "${presetSlug}" does not exist in model "${model.name}".`,
993
+ code: "PRESET_NOT_FOUND"
994
+ });
995
+ }
996
+ const replacedForFilter = structuredClone(preset.instructions);
997
+ if (arg !== null) {
998
+ findInObject(
999
+ replacedForFilter,
1000
+ RONIN_MODEL_SYMBOLS.VALUE,
1001
+ (match) => match.replace(RONIN_MODEL_SYMBOLS.VALUE, arg)
1002
+ );
1003
+ }
1004
+ for (const subInstruction in replacedForFilter) {
1005
+ const instructionName = subInstruction;
1006
+ const currentValue = instructions[instructionName];
1007
+ if (currentValue) {
1008
+ let newValue;
1009
+ if (Array.isArray(currentValue)) {
1010
+ newValue = [
1011
+ ...replacedForFilter[instructionName],
1012
+ ...currentValue
1013
+ ];
1014
+ } else if (isObject(currentValue)) {
1015
+ newValue = {
1016
+ ...replacedForFilter[instructionName],
1017
+ ...currentValue
1018
+ };
1019
+ }
1020
+ Object.assign(instructions, { [instructionName]: newValue });
1021
+ continue;
1022
+ }
1023
+ Object.assign(instructions, {
1024
+ [instructionName]: replacedForFilter[instructionName]
958
1025
  });
959
1026
  }
960
- const replacedForFilter = structuredClone(forFilter);
961
- findInObject(
962
- replacedForFilter,
963
- RONIN_SCHEMA_SYMBOLS.VALUE,
964
- (match) => match.replace(RONIN_SCHEMA_SYMBOLS.VALUE, args)
965
- );
966
- const subStatement = composeConditions(
967
- schemas,
968
- schema,
969
- statementParams,
970
- "for",
971
- replacedForFilter,
972
- { rootTable }
973
- );
974
- statement += `(${subStatement})`;
975
1027
  }
976
- return statement;
1028
+ return instructions;
977
1029
  };
978
1030
 
979
1031
  // src/instructions/including.ts
980
- var handleIncluding = (schemas, statementParams, schema, instruction, rootTable) => {
1032
+ var handleIncluding = (models, model, statementParams, instruction) => {
981
1033
  let statement = "";
982
- let rootTableSubQuery;
983
- let rootTableName = rootTable;
984
- for (const shortcut of instruction || []) {
985
- const includingQuery = schema.including?.[shortcut];
986
- if (!includingQuery) {
987
- throw new RoninError({
988
- message: `The provided \`including\` shortcut "${shortcut}" does not exist in schema "${schema.name}".`,
989
- code: "INVALID_INCLUDING_VALUE"
990
- });
991
- }
992
- const { queryType, querySchema, queryInstructions } = splitQuery(includingQuery);
1034
+ let tableSubQuery;
1035
+ for (const ephemeralFieldSlug in instruction) {
1036
+ const symbol = getSymbol(instruction[ephemeralFieldSlug]);
1037
+ if (symbol?.type !== "query") continue;
1038
+ const { queryType, queryModel, queryInstructions } = splitQuery(symbol.value);
993
1039
  let modifiableQueryInstructions = queryInstructions;
994
- const relatedSchema = getSchemaBySlug(schemas, querySchema);
1040
+ const relatedModel = getModelBySlug(models, queryModel);
995
1041
  let joinType = "LEFT";
996
- let relatedTableSelector = `"${getTableForSchema(relatedSchema)}"`;
997
- const tableAlias = `including_${shortcut}`;
998
- const single = querySchema !== relatedSchema.pluralSlug;
1042
+ let relatedTableSelector = `"${relatedModel.table}"`;
1043
+ const tableAlias = `including_${ephemeralFieldSlug}`;
1044
+ const single = queryModel !== relatedModel.pluralSlug;
999
1045
  if (!modifiableQueryInstructions?.with) {
1000
1046
  joinType = "CROSS";
1001
1047
  if (single) {
@@ -1007,35 +1053,35 @@ var handleIncluding = (schemas, statementParams, schema, instruction, rootTable)
1007
1053
  const subSelect = compileQueryInput(
1008
1054
  {
1009
1055
  [queryType]: {
1010
- [querySchema]: modifiableQueryInstructions
1056
+ [queryModel]: modifiableQueryInstructions
1011
1057
  }
1012
1058
  },
1013
- schemas,
1059
+ models,
1014
1060
  statementParams
1015
1061
  );
1016
1062
  relatedTableSelector = `(${subSelect.main.statement})`;
1017
1063
  }
1018
1064
  statement += `${joinType} JOIN ${relatedTableSelector} as ${tableAlias}`;
1065
+ model.tableAlias = model.table;
1019
1066
  if (joinType === "LEFT") {
1020
1067
  if (!single) {
1021
- rootTableSubQuery = `SELECT * FROM "${rootTable}" LIMIT 1`;
1022
- rootTableName = `sub_${rootTable}`;
1068
+ tableSubQuery = `SELECT * FROM "${model.table}" LIMIT 1`;
1069
+ model.tableAlias = `sub_${model.table}`;
1023
1070
  }
1024
1071
  const subStatement = composeConditions(
1025
- schemas,
1026
- relatedSchema,
1072
+ models,
1073
+ { ...relatedModel, tableAlias },
1027
1074
  statementParams,
1028
1075
  "including",
1029
1076
  queryInstructions?.with,
1030
1077
  {
1031
- rootTable: rootTableName,
1032
- customTable: tableAlias
1078
+ parentModel: model
1033
1079
  }
1034
1080
  );
1035
1081
  statement += ` ON (${subStatement})`;
1036
1082
  }
1037
1083
  }
1038
- return { statement, rootTableSubQuery, rootTableName };
1084
+ return { statement, tableSubQuery };
1039
1085
  };
1040
1086
 
1041
1087
  // src/instructions/limited-to.ts
@@ -1047,62 +1093,64 @@ var handleLimitedTo = (single, instruction) => {
1047
1093
  };
1048
1094
 
1049
1095
  // src/instructions/ordered-by.ts
1050
- var handleOrderedBy = (schema, instruction, rootTable) => {
1096
+ var handleOrderedBy = (model, instruction) => {
1051
1097
  let statement = "";
1052
- for (const field of instruction.ascending || []) {
1053
- const { field: schemaField, fieldSelector } = getFieldFromSchema(
1054
- schema,
1055
- field,
1056
- "orderedBy.ascending",
1057
- rootTable
1058
- );
1098
+ const items = [
1099
+ ...(instruction.ascending || []).map((value) => ({ value, order: "ASC" })),
1100
+ ...(instruction.descending || []).map((value) => ({ value, order: "DESC" }))
1101
+ ];
1102
+ for (const item of items) {
1059
1103
  if (statement.length > 0) {
1060
1104
  statement += ", ";
1061
1105
  }
1062
- const caseInsensitiveStatement = schemaField.type === "string" ? " COLLATE NOCASE" : "";
1063
- statement += `${fieldSelector}${caseInsensitiveStatement} ASC`;
1064
- }
1065
- for (const field of instruction.descending || []) {
1066
- const { field: schemaField, fieldSelector } = getFieldFromSchema(
1067
- schema,
1068
- field,
1069
- "orderedBy.descending",
1070
- rootTable
1071
- );
1072
- if (statement.length > 0) {
1073
- statement += ", ";
1106
+ const symbol = getSymbol(item.value);
1107
+ const instructionName = item.order === "ASC" ? "orderedBy.ascending" : "orderedBy.descending";
1108
+ if (symbol?.type === "expression") {
1109
+ statement += `(${parseFieldExpression(model, instructionName, symbol.value)}) ${item.order}`;
1110
+ continue;
1074
1111
  }
1075
- const caseInsensitiveStatement = schemaField.type === "string" ? " COLLATE NOCASE" : "";
1076
- statement += `${fieldSelector}${caseInsensitiveStatement} DESC`;
1112
+ const { field: modelField, fieldSelector } = getFieldFromModel(
1113
+ model,
1114
+ item.value,
1115
+ instructionName
1116
+ );
1117
+ const caseInsensitiveStatement = modelField.type === "string" ? " COLLATE NOCASE" : "";
1118
+ statement += `${fieldSelector}${caseInsensitiveStatement} ${item.order}`;
1077
1119
  }
1078
1120
  return `ORDER BY ${statement}`;
1079
1121
  };
1080
1122
 
1081
1123
  // src/instructions/selecting.ts
1082
- var handleSelecting = (schema, statementParams, instructions) => {
1124
+ var handleSelecting = (model, statementParams, instructions) => {
1125
+ let isJoining = false;
1083
1126
  let statement = instructions.selecting ? instructions.selecting.map((slug) => {
1084
- return getFieldFromSchema(schema, slug, "selecting").fieldSelector;
1127
+ return getFieldFromModel(model, slug, "selecting").fieldSelector;
1085
1128
  }).join(", ") : "*";
1086
- if (isObject(instructions.including)) {
1087
- statement += ", ";
1088
- statement += Object.entries(
1089
- flatten(instructions.including)
1090
- ).filter(([_, value]) => {
1091
- return !(isObject(value) && Object.hasOwn(value, RONIN_SCHEMA_SYMBOLS.QUERY));
1092
- }).map(([key, value]) => {
1093
- return `${prepareStatementValue(statementParams, value)} as "${key}"`;
1094
- }).join(", ");
1129
+ if (instructions.including) {
1130
+ const filteredObject = Object.entries(instructions.including).filter(([_, value]) => {
1131
+ const symbol = getSymbol(value);
1132
+ const hasQuery = symbol?.type === "query";
1133
+ if (hasQuery) isJoining = true;
1134
+ return !hasQuery;
1135
+ });
1136
+ const newObjectEntries = Object.entries(flatten(Object.fromEntries(filteredObject)));
1137
+ if (newObjectEntries.length > 0) {
1138
+ statement += ", ";
1139
+ statement += newObjectEntries.map(([key, value]) => {
1140
+ return `${prepareStatementValue(statementParams, value)} as "${key}"`;
1141
+ }).join(", ");
1142
+ }
1095
1143
  }
1096
- return statement;
1144
+ return { columns: statement, isJoining };
1097
1145
  };
1098
1146
 
1099
1147
  // src/instructions/to.ts
1100
- var handleTo = (schemas, schema, statementParams, queryType, dependencyStatements, instructions, rootTable) => {
1148
+ var handleTo = (models, model, statementParams, queryType, dependencyStatements, instructions, parentModel) => {
1101
1149
  const currentTime = (/* @__PURE__ */ new Date()).toISOString();
1102
1150
  const { with: withInstruction, to: toInstruction } = instructions;
1103
1151
  const defaultFields = {};
1104
1152
  if (queryType === "create") {
1105
- defaultFields.id = toInstruction.id || generateRecordId(schema.idPrefix);
1153
+ defaultFields.id = toInstruction.id || generateRecordId(model.idPrefix);
1106
1154
  }
1107
1155
  defaultFields.ronin = {
1108
1156
  // If records are being created, set their creation time.
@@ -1112,21 +1160,20 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
1112
1160
  // Allow for overwriting the default values provided above.
1113
1161
  ...toInstruction.ronin
1114
1162
  };
1115
- const hasSubQuery = Object.hasOwn(toInstruction, RONIN_SCHEMA_SYMBOLS.QUERY);
1116
- if (hasSubQuery) {
1117
- const subQuery = toInstruction[RONIN_SCHEMA_SYMBOLS.QUERY];
1118
- let { querySchema: subQuerySchemaSlug, queryInstructions: subQueryInstructions } = splitQuery(subQuery);
1119
- const subQuerySchema = getSchemaBySlug(schemas, subQuerySchemaSlug);
1163
+ const symbol = getSymbol(toInstruction);
1164
+ if (symbol?.type === "query") {
1165
+ let { queryModel: subQueryModelSlug, queryInstructions: subQueryInstructions } = splitQuery(symbol.value);
1166
+ const subQueryModel = getModelBySlug(models, subQueryModelSlug);
1120
1167
  const subQuerySelectedFields = subQueryInstructions?.selecting;
1121
1168
  const subQueryIncludedFields = subQueryInstructions?.including;
1122
1169
  const subQueryFields = [
1123
- ...subQuerySelectedFields || (subQuerySchema.fields || []).map((field) => field.slug),
1170
+ ...subQuerySelectedFields || (subQueryModel.fields || []).map((field) => field.slug),
1124
1171
  ...subQueryIncludedFields ? Object.keys(
1125
1172
  flatten(subQueryIncludedFields || {})
1126
1173
  ) : []
1127
1174
  ];
1128
1175
  for (const field of subQueryFields || []) {
1129
- getFieldFromSchema(schema, field, "to");
1176
+ getFieldFromModel(model, field, "to");
1130
1177
  }
1131
1178
  const defaultFieldsToAdd = subQuerySelectedFields ? Object.entries(flatten(defaultFields)).filter(([key]) => {
1132
1179
  return !subQuerySelectedFields.includes(key);
@@ -1139,18 +1186,15 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
1139
1186
  ...subQueryInstructions.including
1140
1187
  };
1141
1188
  }
1142
- return compileQueryInput(subQuery, schemas, statementParams).main.statement;
1189
+ return compileQueryInput(symbol.value, models, statementParams).main.statement;
1143
1190
  }
1144
1191
  Object.assign(toInstruction, defaultFields);
1145
1192
  for (const fieldSlug in toInstruction) {
1146
1193
  const fieldValue = toInstruction[fieldSlug];
1147
- const fieldDetails = getFieldFromSchema(schema, fieldSlug, "to");
1194
+ const fieldDetails = getFieldFromModel(model, fieldSlug, "to");
1148
1195
  if (fieldDetails.field.type === "reference" && fieldDetails.field.kind === "many") {
1149
1196
  delete toInstruction[fieldSlug];
1150
- const associativeSchemaSlug = composeAssociationSchemaSlug(
1151
- schema,
1152
- fieldDetails.field
1153
- );
1197
+ const associativeModelSlug = composeAssociationModelSlug(model, fieldDetails.field);
1154
1198
  const composeStatement = (subQueryType, value) => {
1155
1199
  const source = queryType === "create" ? { id: toInstruction.id } : withInstruction;
1156
1200
  const recordDetails = { source };
@@ -1158,10 +1202,10 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
1158
1202
  return compileQueryInput(
1159
1203
  {
1160
1204
  [subQueryType]: {
1161
- [associativeSchemaSlug]: subQueryType === "create" ? { to: recordDetails } : { with: recordDetails }
1205
+ [associativeModelSlug]: subQueryType === "create" ? { to: recordDetails } : { with: recordDetails }
1162
1206
  }
1163
1207
  },
1164
- schemas,
1208
+ models,
1165
1209
  [],
1166
1210
  { returning: false }
1167
1211
  ).main;
@@ -1181,26 +1225,19 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
1181
1225
  }
1182
1226
  }
1183
1227
  }
1184
- let statement = composeConditions(
1185
- schemas,
1186
- schema,
1187
- statementParams,
1188
- "to",
1189
- toInstruction,
1190
- {
1191
- rootTable,
1192
- type: queryType === "create" ? "fields" : void 0
1193
- }
1194
- );
1228
+ let statement = composeConditions(models, model, statementParams, "to", toInstruction, {
1229
+ parentModel,
1230
+ type: queryType === "create" ? "fields" : void 0
1231
+ });
1195
1232
  if (queryType === "create") {
1196
1233
  const deepStatement = composeConditions(
1197
- schemas,
1198
- schema,
1234
+ models,
1235
+ model,
1199
1236
  statementParams,
1200
1237
  "to",
1201
1238
  toInstruction,
1202
1239
  {
1203
- rootTable,
1240
+ parentModel,
1204
1241
  type: "values"
1205
1242
  }
1206
1243
  );
@@ -1212,21 +1249,23 @@ var handleTo = (schemas, schema, statementParams, queryType, dependencyStatement
1212
1249
  };
1213
1250
 
1214
1251
  // src/utils/index.ts
1215
- var compileQueryInput = (query, schemas, statementParams, options) => {
1252
+ var compileQueryInput = (query, models, statementParams, options) => {
1216
1253
  const parsedQuery = splitQuery(query);
1217
- const { queryType, querySchema, queryInstructions } = parsedQuery;
1218
- const schema = getSchemaBySlug(schemas, querySchema);
1219
- const single = querySchema !== schema.pluralSlug;
1220
- let instructions = formatIdentifiers(schema, queryInstructions);
1221
- let table = getTableForSchema(schema);
1254
+ const { queryType, queryModel, queryInstructions } = parsedQuery;
1255
+ const model = getModelBySlug(models, queryModel);
1256
+ const single = queryModel !== model.pluralSlug;
1257
+ let instructions = formatIdentifiers(model, queryInstructions);
1222
1258
  const dependencyStatements = [];
1223
1259
  const returning = options?.returning ?? true;
1224
- instructions = addSchemaQueries(
1225
- schemas,
1226
- { queryType, querySchema, queryInstructions: instructions },
1260
+ instructions = addModelQueries(
1261
+ models,
1262
+ { queryType, queryModel, queryInstructions: instructions },
1227
1263
  dependencyStatements
1228
1264
  );
1229
- const columns = handleSelecting(schema, statementParams, {
1265
+ if (instructions && Object.hasOwn(instructions, "for")) {
1266
+ instructions = handleFor(model, instructions);
1267
+ }
1268
+ const { columns, isJoining } = handleSelecting(model, statementParams, {
1230
1269
  selecting: instructions?.selecting,
1231
1270
  including: instructions?.including
1232
1271
  });
@@ -1248,24 +1287,23 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1248
1287
  statement += "UPDATE ";
1249
1288
  break;
1250
1289
  }
1251
- const isJoining = typeof instructions?.including !== "undefined" && !isObject(instructions.including);
1252
1290
  let isJoiningMultipleRows = false;
1253
1291
  if (isJoining) {
1254
- const {
1255
- statement: including,
1256
- rootTableSubQuery,
1257
- rootTableName
1258
- } = handleIncluding(schemas, statementParams, schema, instructions?.including, table);
1259
- if (rootTableSubQuery && rootTableName) {
1260
- table = rootTableName;
1261
- statement += `(${rootTableSubQuery}) as ${rootTableName} `;
1292
+ const { statement: including, tableSubQuery } = handleIncluding(
1293
+ models,
1294
+ model,
1295
+ statementParams,
1296
+ instructions?.including
1297
+ );
1298
+ if (tableSubQuery) {
1299
+ statement += `(${tableSubQuery}) as ${model.tableAlias} `;
1262
1300
  isJoiningMultipleRows = true;
1263
1301
  } else {
1264
- statement += `"${table}" `;
1302
+ statement += `"${model.table}" `;
1265
1303
  }
1266
1304
  statement += `${including} `;
1267
1305
  } else {
1268
- statement += `"${table}" `;
1306
+ statement += `"${model.table}" `;
1269
1307
  }
1270
1308
  if (queryType === "create" || queryType === "set") {
1271
1309
  if (!isObject(instructions.to) || Object.keys(instructions.to).length === 0) {
@@ -1276,37 +1314,27 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1276
1314
  });
1277
1315
  }
1278
1316
  const toStatement = handleTo(
1279
- schemas,
1280
- schema,
1317
+ models,
1318
+ model,
1281
1319
  statementParams,
1282
1320
  queryType,
1283
1321
  dependencyStatements,
1284
1322
  { with: instructions.with, to: instructions.to },
1285
- isJoining ? table : void 0
1323
+ options?.parentModel
1286
1324
  );
1287
1325
  statement += `${toStatement} `;
1288
1326
  }
1289
1327
  const conditions = [];
1290
1328
  if (queryType !== "create" && instructions && Object.hasOwn(instructions, "with")) {
1291
1329
  const withStatement = handleWith(
1292
- schemas,
1293
- schema,
1330
+ models,
1331
+ model,
1294
1332
  statementParams,
1295
1333
  instructions?.with,
1296
- isJoining ? table : void 0
1334
+ options?.parentModel
1297
1335
  );
1298
1336
  if (withStatement.length > 0) conditions.push(withStatement);
1299
1337
  }
1300
- if (instructions && Object.hasOwn(instructions, "for")) {
1301
- const forStatement = handleFor(
1302
- schemas,
1303
- schema,
1304
- statementParams,
1305
- instructions?.for,
1306
- isJoining ? table : void 0
1307
- );
1308
- if (forStatement.length > 0) conditions.push(forStatement);
1309
- }
1310
1338
  if ((queryType === "get" || queryType === "count") && !single && instructions?.limitedTo) {
1311
1339
  instructions = instructions || {};
1312
1340
  instructions.orderedBy = instructions.orderedBy || {};
@@ -1327,18 +1355,13 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1327
1355
  queries: [query]
1328
1356
  });
1329
1357
  }
1330
- const beforeAndAfterStatement = handleBeforeOrAfter(
1331
- schema,
1332
- statementParams,
1333
- {
1334
- before: instructions.before,
1335
- after: instructions.after,
1336
- with: instructions.with,
1337
- orderedBy: instructions.orderedBy,
1338
- limitedTo: instructions.limitedTo
1339
- },
1340
- isJoining ? table : void 0
1341
- );
1358
+ const beforeAndAfterStatement = handleBeforeOrAfter(model, statementParams, {
1359
+ before: instructions.before,
1360
+ after: instructions.after,
1361
+ with: instructions.with,
1362
+ orderedBy: instructions.orderedBy,
1363
+ limitedTo: instructions.limitedTo
1364
+ });
1342
1365
  conditions.push(beforeAndAfterStatement);
1343
1366
  }
1344
1367
  if (conditions.length > 0) {
@@ -1349,11 +1372,7 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1349
1372
  }
1350
1373
  }
1351
1374
  if (instructions?.orderedBy) {
1352
- const orderedByStatement = handleOrderedBy(
1353
- schema,
1354
- instructions.orderedBy,
1355
- isJoining ? table : void 0
1356
- );
1375
+ const orderedByStatement = handleOrderedBy(model, instructions.orderedBy);
1357
1376
  statement += `${orderedByStatement} `;
1358
1377
  }
1359
1378
  if (queryType === "get" && !isJoiningMultipleRows && (single || instructions?.limitedTo)) {
@@ -1374,20 +1393,20 @@ var compileQueryInput = (query, schemas, statementParams, options) => {
1374
1393
  };
1375
1394
 
1376
1395
  // src/index.ts
1377
- var compileQueries = (queries, schemas, options) => {
1378
- const schemaList = addSystemSchemas(schemas).map((schema) => {
1379
- return addDefaultSchemaFields(schema, true);
1396
+ var compileQueries = (queries, models, options) => {
1397
+ const modelList = addSystemModels(models).map((model) => {
1398
+ return addDefaultModelFields(model, true);
1380
1399
  });
1381
- const schemaListWithShortcuts = schemaList.map((schema) => {
1382
- return addDefaultSchemaShortcuts(schemaList, schema);
1400
+ const modelListWithPresets = modelList.map((model) => {
1401
+ return addDefaultModelPresets(modelList, model);
1383
1402
  });
1384
1403
  const dependencyStatements = [];
1385
1404
  const mainStatements = [];
1386
1405
  for (const query of queries) {
1387
1406
  const result = compileQueryInput(
1388
1407
  query,
1389
- schemaListWithShortcuts,
1390
- options?.inlineValues ? null : []
1408
+ modelListWithPresets,
1409
+ options?.inlineParams ? null : []
1391
1410
  );
1392
1411
  dependencyStatements.push(...result.dependencies);
1393
1412
  mainStatements.push(result.main);