@ronin/compiler 0.9.0-leo-ron-1083-experimental-203 → 0.9.0-leo-ron-1083-experimental-205

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 CHANGED
@@ -5920,7 +5920,7 @@ type ModelTriggerField<T extends Array<ModelField> = Array<ModelField>> = {
5920
5920
  };
5921
5921
  type ModelTrigger<T extends Array<ModelField> = Array<ModelField>> = {
5922
5922
  /**
5923
- * The identifier of the index.
5923
+ * The identifier of the trigger.
5924
5924
  */
5925
5925
  slug?: string;
5926
5926
  /** The type of query for which the trigger should fire. */
@@ -5961,17 +5961,25 @@ interface Model<T extends Array<ModelField> = Array<ModelField>> {
5961
5961
  */
5962
5962
  tableAlias?: string;
5963
5963
  /**
5964
- * If the model is used to associate two models with each other (in the case of
5965
- * many-cardinality link fields), this property should contain the field slug to which
5966
- * the associative model should be mounted on the source model.
5964
+ * Details that identify the model as a model that was automatically created by RONIN,
5965
+ * instead of being manually created by a developer.
5967
5966
  */
5968
- associationSlug?: string;
5967
+ system?: {
5968
+ /** The model that caused the system model to get created. */
5969
+ model: string | 'root';
5970
+ /**
5971
+ * If the model is used to associate two models with each other (in the case of
5972
+ * many-cardinality link fields), this property should contain the field slug to
5973
+ * which the associative model should be mounted on the source model.
5974
+ */
5975
+ associationSlug?: string;
5976
+ };
5969
5977
  fields: T;
5970
5978
  indexes?: Array<ModelIndex<T>>;
5971
5979
  triggers?: Array<ModelTrigger<T>>;
5972
5980
  presets?: Array<ModelPreset>;
5973
5981
  }
5974
- type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'associationSlug' | 'table' | 'tableAlias'> & {
5982
+ type PublicModel<T extends Array<ModelField> = Array<ModelField>> = Omit<Partial<Model<T>>, 'slug' | 'identifiers' | 'system' | 'table' | 'tableAlias'> & {
5975
5983
  slug: Required<Model['slug']>;
5976
5984
  identifiers?: Partial<Model['identifiers']>;
5977
5985
  };
package/dist/index.js CHANGED
@@ -465,67 +465,67 @@ var SYSTEM_FIELDS = [
465
465
  slug: "ronin.updatedBy"
466
466
  }
467
467
  ];
468
- var SYSTEM_MODELS = [
469
- {
470
- slug: "model",
471
- identifiers: {
472
- name: "name",
473
- slug: "slug"
474
- },
475
- // This name mimics the `sqlite_schema` table in SQLite.
476
- table: "ronin_schema",
477
- fields: [
478
- { slug: "name", type: "string" },
479
- { slug: "pluralName", type: "string" },
480
- { slug: "slug", type: "string" },
481
- { slug: "pluralSlug", type: "string" },
482
- { slug: "idPrefix", type: "string" },
483
- { slug: "table", type: "string" },
484
- { slug: "identifiers", type: "group" },
485
- { slug: "identifiers.name", type: "string" },
486
- { slug: "identifiers.slug", type: "string" },
487
- // Providing an empty object as a default value allows us to use `json_insert`
488
- // without needing to fall back to an empty object in the insertion statement,
489
- // which makes the statement shorter.
490
- { slug: "fields", type: "json", defaultValue: "{}" },
491
- { slug: "indexes", type: "json", defaultValue: "{}" },
492
- { slug: "triggers", type: "json", defaultValue: "{}" },
493
- { slug: "presets", type: "json", defaultValue: "{}" }
494
- ]
495
- }
496
- ];
497
- var addSystemModels = (models) => {
498
- const associativeModels = models.flatMap((model) => {
499
- const addedModels = [];
500
- for (const field of model.fields || []) {
501
- if (field.type === "link" && !field.slug.startsWith("ronin.")) {
502
- const relatedModel = getModelBySlug(models, field.target);
503
- let fieldSlug = relatedModel.slug;
504
- if (field.kind === "many") {
505
- fieldSlug = composeAssociationModelSlug(model, field);
506
- addedModels.push({
507
- pluralSlug: fieldSlug,
508
- slug: fieldSlug,
509
- associationSlug: field.slug,
510
- fields: [
511
- {
512
- slug: "source",
513
- type: "link",
514
- target: model.slug
515
- },
516
- {
517
- slug: "target",
518
- type: "link",
519
- target: relatedModel.slug
520
- }
521
- ]
522
- });
523
- }
468
+ var ROOT_MODEL = {
469
+ slug: "model",
470
+ identifiers: {
471
+ name: "name",
472
+ slug: "slug"
473
+ },
474
+ // This name mimics the `sqlite_schema` table in SQLite.
475
+ table: "ronin_schema",
476
+ // Indicates that the model was automatically generated by RONIN.
477
+ system: { model: "root" },
478
+ fields: [
479
+ { slug: "name", type: "string" },
480
+ { slug: "pluralName", type: "string" },
481
+ { slug: "slug", type: "string" },
482
+ { slug: "pluralSlug", type: "string" },
483
+ { slug: "idPrefix", type: "string" },
484
+ { slug: "table", type: "string" },
485
+ { slug: "identifiers", type: "group" },
486
+ { slug: "identifiers.name", type: "string" },
487
+ { slug: "identifiers.slug", type: "string" },
488
+ // Providing an empty object as a default value allows us to use `json_insert`
489
+ // without needing to fall back to an empty object in the insertion statement,
490
+ // which makes the statement shorter.
491
+ { slug: "fields", type: "json", defaultValue: "{}" },
492
+ { slug: "indexes", type: "json", defaultValue: "{}" },
493
+ { slug: "triggers", type: "json", defaultValue: "{}" },
494
+ { slug: "presets", type: "json", defaultValue: "{}" }
495
+ ]
496
+ };
497
+ var getSystemModels = (models, model) => {
498
+ const addedModels = [];
499
+ for (const field of model.fields || []) {
500
+ if (field.type === "link" && !field.slug.startsWith("ronin.")) {
501
+ const relatedModel = getModelBySlug(models, field.target);
502
+ let fieldSlug = relatedModel.slug;
503
+ if (field.kind === "many") {
504
+ fieldSlug = composeAssociationModelSlug(model, field);
505
+ addedModels.push({
506
+ pluralSlug: fieldSlug,
507
+ slug: fieldSlug,
508
+ system: {
509
+ model: model.slug,
510
+ associationSlug: field.slug
511
+ },
512
+ fields: [
513
+ {
514
+ slug: "source",
515
+ type: "link",
516
+ target: model.slug
517
+ },
518
+ {
519
+ slug: "target",
520
+ type: "link",
521
+ target: relatedModel.slug
522
+ }
523
+ ]
524
+ });
524
525
  }
525
526
  }
526
- return addedModels;
527
- });
528
- return [...SYSTEM_MODELS, ...associativeModels, ...models];
527
+ }
528
+ return addedModels;
529
529
  };
530
530
  var addDefaultModelPresets = (list, model) => {
531
531
  const defaultPresets = [];
@@ -567,7 +567,7 @@ var addDefaultModelPresets = (list, model) => {
567
567
  for (const childMatch of childModels) {
568
568
  const { model: childModel, field: childField } = childMatch;
569
569
  const pluralSlug = childModel.pluralSlug;
570
- const presetSlug = childModel.associationSlug || pluralSlug;
570
+ const presetSlug = childModel.system?.associationSlug || pluralSlug;
571
571
  defaultPresets.push({
572
572
  instructions: {
573
573
  including: {
@@ -648,6 +648,14 @@ var formatModelEntity = (type, entities) => {
648
648
  });
649
649
  return entries ? Object.fromEntries(entries) : void 0;
650
650
  };
651
+ var composeSystemModelStatement = (models, dependencyStatements, action, systemModel) => {
652
+ const { system: _, ...systemModelClean } = systemModel;
653
+ const query = {
654
+ [action]: { model: action === "create" ? systemModelClean : systemModelClean.slug }
655
+ };
656
+ const statement = compileQueryInput(query, models, []);
657
+ dependencyStatements.push(...statement.dependencies);
658
+ };
651
659
  var transformMetaQuery = (models, dependencyStatements, statementParams, query) => {
652
660
  const { queryType } = splitQuery(query);
653
661
  const subAltering = query.alter && !("to" in query.alter);
@@ -676,16 +684,16 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
676
684
  }
677
685
  }
678
686
  if (!(modelSlug && slug)) return query;
679
- const tableAction = ["model", "index", "trigger"].includes(entity) ? action.toUpperCase() : "ALTER";
680
- const tableName = convertToSnakeCase(pluralize(modelSlug));
681
687
  const model = action === "create" && entity === "model" ? null : getModelBySlug(models, modelSlug);
682
- const statement = `${tableAction} TABLE "${tableName}"`;
683
688
  if (entity === "model") {
684
689
  let queryTypeDetails;
685
690
  if (action === "create") {
686
691
  const newModel = jsonValue;
687
692
  const modelWithFields = addDefaultModelFields(newModel, true);
688
- const modelWithPresets = addDefaultModelPresets(models, modelWithFields);
693
+ const modelWithPresets = addDefaultModelPresets(
694
+ [...models, modelWithFields],
695
+ modelWithFields
696
+ );
689
697
  const entities = Object.fromEntries(
690
698
  Object.entries(PLURAL_MODEL_ENTITIES).map(([type, pluralType2]) => {
691
699
  const list = modelWithPresets[pluralType2];
@@ -694,7 +702,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
694
702
  );
695
703
  const columns = modelWithPresets.fields.map((field2) => getFieldStatement(models, modelWithPresets, field2)).filter(Boolean);
696
704
  dependencyStatements.push({
697
- statement: `${statement} (${columns.join(", ")})`,
705
+ statement: `CREATE TABLE "${modelWithPresets.table}" (${columns.join(", ")})`,
698
706
  params: []
699
707
  });
700
708
  models.push(modelWithPresets);
@@ -703,16 +711,23 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
703
711
  if (entities[entity2]) finalModel[entity2] = entities[entity2];
704
712
  }
705
713
  queryTypeDetails = { to: finalModel };
714
+ getSystemModels(models, modelWithPresets).map((systemModel) => {
715
+ return composeSystemModelStatement(
716
+ models,
717
+ dependencyStatements,
718
+ "create",
719
+ systemModel
720
+ );
721
+ });
706
722
  }
707
723
  if (action === "alter" && model) {
708
724
  const newModel = jsonValue;
709
725
  const modelWithFields = addDefaultModelFields(newModel, false);
710
726
  const modelWithPresets = addDefaultModelPresets(models, modelWithFields);
711
- const newSlug = modelWithPresets.pluralSlug;
712
- if (newSlug) {
713
- const newTable = convertToSnakeCase(newSlug);
727
+ const newTableName = modelWithPresets.table;
728
+ if (newTableName) {
714
729
  dependencyStatements.push({
715
- statement: `${statement} RENAME TO "${newTable}"`,
730
+ statement: `ALTER TABLE "${model.table}" RENAME TO "${newTableName}"`,
716
731
  params: []
717
732
  });
718
733
  }
@@ -726,8 +741,16 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
726
741
  }
727
742
  if (action === "drop" && model) {
728
743
  models.splice(models.indexOf(model), 1);
729
- dependencyStatements.push({ statement, params: [] });
744
+ dependencyStatements.push({ statement: `DROP TABLE "${model.table}"`, params: [] });
730
745
  queryTypeDetails = { with: { slug } };
746
+ models.filter(({ system }) => system?.model === model.slug).map((systemModel) => {
747
+ return composeSystemModelStatement(
748
+ models,
749
+ dependencyStatements,
750
+ "drop",
751
+ systemModel
752
+ );
753
+ });
731
754
  }
732
755
  const queryTypeAction = action === "create" ? "add" : action === "alter" ? "set" : "remove";
733
756
  return {
@@ -737,7 +760,19 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
737
760
  };
738
761
  }
739
762
  const existingModel = model;
763
+ const pluralType = PLURAL_MODEL_ENTITIES[entity];
764
+ const targetEntityIndex = existingModel[pluralType]?.findIndex(
765
+ (entity2) => entity2.slug === slug
766
+ );
767
+ if ((action === "alter" || action === "drop") && (typeof targetEntityIndex === "undefined" || targetEntityIndex === -1)) {
768
+ throw new RoninError({
769
+ message: `No ${entity} with slug "${slug}" defined in model "${existingModel.name}".`,
770
+ code: MODEL_ENTITY_ERROR_CODES[entity]
771
+ });
772
+ }
773
+ const existingEntity = existingModel[pluralType]?.[targetEntityIndex];
740
774
  if (entity === "field") {
775
+ const statement = `ALTER TABLE "${existingModel.table}"`;
741
776
  if (action === "create") {
742
777
  const field2 = jsonValue;
743
778
  field2.type = field2.type || "string";
@@ -757,17 +792,21 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
757
792
  });
758
793
  }
759
794
  } else if (action === "drop") {
760
- dependencyStatements.push({
761
- statement: `${statement} DROP COLUMN "${slug}"`,
762
- params: []
763
- });
795
+ const existingField = existingEntity;
796
+ if (!(existingField.type === "link" && existingField.kind === "many")) {
797
+ dependencyStatements.push({
798
+ statement: `${statement} DROP COLUMN "${slug}"`,
799
+ params: []
800
+ });
801
+ }
764
802
  }
765
803
  }
804
+ const statementAction = action.toUpperCase();
766
805
  if (entity === "index") {
767
806
  const index = jsonValue;
768
807
  const indexName = convertToSnakeCase(slug);
769
808
  const params = [];
770
- let statement2 = `${tableAction}${index?.unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
809
+ let statement = `${statementAction}${index?.unique ? " UNIQUE" : ""} INDEX "${indexName}"`;
771
810
  if (action === "create") {
772
811
  const columns = index.fields.map((field2) => {
773
812
  let fieldSelector = "";
@@ -780,18 +819,18 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
780
819
  if (field2.order) fieldSelector += ` ${field2.order}`;
781
820
  return fieldSelector;
782
821
  });
783
- statement2 += ` ON "${tableName}" (${columns.join(", ")})`;
822
+ statement += ` ON "${existingModel.table}" (${columns.join(", ")})`;
784
823
  if (index.filter) {
785
824
  const withStatement = handleWith(models, existingModel, params, index.filter);
786
- statement2 += ` WHERE (${withStatement})`;
825
+ statement += ` WHERE (${withStatement})`;
787
826
  }
788
827
  }
789
- dependencyStatements.push({ statement: statement2, params });
828
+ dependencyStatements.push({ statement, params });
790
829
  }
791
830
  if (entity === "trigger") {
792
831
  const triggerName = convertToSnakeCase(slug);
793
832
  const params = [];
794
- let statement2 = `${tableAction} TRIGGER "${triggerName}"`;
833
+ let statement = `${statementAction} TRIGGER "${triggerName}"`;
795
834
  if (action === "create") {
796
835
  const trigger = jsonValue;
797
836
  const statementParts = [`${trigger.when} ${trigger.action}`];
@@ -808,7 +847,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
808
847
  });
809
848
  statementParts.push(`OF (${fieldSelectors.join(", ")})`);
810
849
  }
811
- statementParts.push("ON", `"${tableName}"`);
850
+ statementParts.push("ON", `"${existingModel.table}"`);
812
851
  if (trigger.filter || trigger.effects.some((query2) => findInObject(query2, RONIN_MODEL_SYMBOLS.FIELD))) {
813
852
  statementParts.push("FOR EACH ROW");
814
853
  }
@@ -831,21 +870,11 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
831
870
  if (effectStatements.length > 1) statementParts.push("BEGIN");
832
871
  statementParts.push(effectStatements.join("; "));
833
872
  if (effectStatements.length > 1) statementParts.push("END");
834
- statement2 += ` ${statementParts.join(" ")}`;
873
+ statement += ` ${statementParts.join(" ")}`;
835
874
  }
836
- dependencyStatements.push({ statement: statement2, params });
875
+ dependencyStatements.push({ statement, params });
837
876
  }
838
- const pluralType = PLURAL_MODEL_ENTITIES[entity];
839
877
  const field = `${RONIN_MODEL_SYMBOLS.FIELD}${pluralType}`;
840
- const targetEntityIndex = existingModel[pluralType]?.findIndex(
841
- (entity2) => entity2.slug === slug
842
- );
843
- if ((action === "alter" || action === "drop") && (typeof targetEntityIndex === "undefined" || targetEntityIndex === -1)) {
844
- throw new RoninError({
845
- message: `No ${entity} with slug "${slug}" defined in model "${existingModel.name}".`,
846
- code: MODEL_ENTITY_ERROR_CODES[entity]
847
- });
848
- }
849
878
  let json;
850
879
  switch (action) {
851
880
  case "create": {
@@ -867,9 +896,23 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query)
867
896
  case "drop": {
868
897
  json = `json_remove(${field}, '$.${slug}')`;
869
898
  const targetEntity = existingModel[pluralType];
870
- delete targetEntity[targetEntityIndex];
899
+ targetEntity.splice(targetEntityIndex, 1);
871
900
  }
872
901
  }
902
+ const currentSystemModels = models.filter(({ system }) => {
903
+ return system?.model === existingModel.slug;
904
+ });
905
+ const newSystemModels = getSystemModels(models, existingModel);
906
+ for (const systemModel of currentSystemModels) {
907
+ const exists = newSystemModels.find((model2) => model2.slug === systemModel.slug);
908
+ if (exists) continue;
909
+ composeSystemModelStatement(models, dependencyStatements, "drop", systemModel);
910
+ }
911
+ for (const systemModel of newSystemModels) {
912
+ const exists = currentSystemModels.find((model2) => model2.slug === systemModel.slug);
913
+ if (exists) continue;
914
+ composeSystemModelStatement(models, dependencyStatements, "create", systemModel);
915
+ }
873
916
  return {
874
917
  set: {
875
918
  model: {
@@ -1240,13 +1283,19 @@ var handleTo = (models, model, statementParams, queryType, dependencyStatements,
1240
1283
  };
1241
1284
 
1242
1285
  // src/utils/index.ts
1243
- var compileQueryInput = (query, models, statementParams, options) => {
1286
+ var compileQueryInput = (defaultQuery, models, statementParams, options) => {
1287
+ const dependencyStatements = [];
1288
+ const query = transformMetaQuery(
1289
+ models,
1290
+ dependencyStatements,
1291
+ statementParams,
1292
+ defaultQuery
1293
+ );
1244
1294
  const parsedQuery = splitQuery(query);
1245
1295
  const { queryType, queryModel, queryInstructions } = parsedQuery;
1246
1296
  const model = getModelBySlug(models, queryModel);
1247
1297
  const single = queryModel !== model.pluralSlug;
1248
1298
  let instructions = formatIdentifiers(model, queryInstructions);
1249
- const dependencyStatements = [];
1250
1299
  const returning = options?.returning ?? true;
1251
1300
  if (instructions && Object.hasOwn(instructions, "for")) {
1252
1301
  instructions = handleFor(model, instructions);
@@ -1398,7 +1447,11 @@ var Transaction = class {
1398
1447
  * @returns The composed SQL statements.
1399
1448
  */
1400
1449
  compileQueries = (queries, models, options) => {
1401
- const modelList = addSystemModels(models).map((model) => {
1450
+ const modelList = [
1451
+ ROOT_MODEL,
1452
+ ...models.flatMap((model) => getSystemModels(models, model)),
1453
+ ...models
1454
+ ].map((model) => {
1402
1455
  return addDefaultModelFields(model, true);
1403
1456
  });
1404
1457
  const modelListWithPresets = modelList.map((model) => {
@@ -1407,17 +1460,10 @@ var Transaction = class {
1407
1460
  const dependencyStatements = [];
1408
1461
  const mainStatements = [];
1409
1462
  for (const query of queries) {
1410
- const statementValues = options?.inlineParams ? null : [];
1411
- const transformedQuery = transformMetaQuery(
1412
- modelListWithPresets,
1413
- dependencyStatements,
1414
- statementValues,
1415
- query
1416
- );
1417
1463
  const result = compileQueryInput(
1418
- transformedQuery,
1464
+ query,
1419
1465
  modelListWithPresets,
1420
- statementValues
1466
+ options?.inlineParams ? null : []
1421
1467
  );
1422
1468
  dependencyStatements.push(...result.dependencies);
1423
1469
  mainStatements.push(result.main);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronin/compiler",
3
- "version": "0.9.0-leo-ron-1083-experimental-203",
3
+ "version": "0.9.0-leo-ron-1083-experimental-205",
4
4
  "type": "module",
5
5
  "description": "Compiles RONIN queries to SQL statements.",
6
6
  "publishConfig": {