@ronin/compiler 0.17.6 → 0.17.7

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.
Files changed (2) hide show
  1. package/dist/index.js +454 -449
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23,12 +23,6 @@ var RAW_FIELD_TYPES = ["string", "number", "boolean"];
23
23
  var CURRENT_TIME_EXPRESSION = {
24
24
  [QUERY_SYMBOLS.EXPRESSION]: `strftime('%Y-%m-%dT%H:%M:%f', 'now') || 'Z'`
25
25
  };
26
- var ID_EXPRESSION = (idPrefix) => ({
27
- // Since default values in SQLite cannot rely on other columns, we unfortunately
28
- // cannot rely on the `idPrefix` column here. Instead, we need to inject it directly
29
- // into the expression as a static string.
30
- [QUERY_SYMBOLS.EXPRESSION]: `'${idPrefix}_' || lower(substr(hex(randomblob(12)), 1, 16))`
31
- });
32
26
  var MOUNTING_PATH_SUFFIX = /(.*?)(\{(\d+)\})?$/;
33
27
  var composeMountingPath = (single, key, mountingPath) => {
34
28
  if (key === "ronin_root") {
@@ -465,72 +459,458 @@ var handleSelecting = (models, model, statementParams, single, instructions, opt
465
459
  return { columns: columns.join(", "), isJoining, selectedFields };
466
460
  };
467
461
 
468
- // src/instructions/to.ts
469
- var handleTo = (models, model, statementParams, queryType, dependencyStatements, instructions, options) => {
470
- const { with: withInstruction, to: toInstruction } = instructions;
471
- const defaultFields = {};
472
- const inlineDefaultInsertionFields = queryType === "add" && options?.inlineDefaults;
473
- if (inlineDefaultInsertionFields) {
474
- defaultFields.id = toInstruction.id || ID_EXPRESSION(model.idPrefix);
462
+ // node_modules/title/dist/esm/lower-case.js
463
+ var conjunctions = [
464
+ "for",
465
+ "and",
466
+ "nor",
467
+ "but",
468
+ "or",
469
+ "yet",
470
+ "so"
471
+ ];
472
+ var articles = [
473
+ "a",
474
+ "an",
475
+ "the"
476
+ ];
477
+ var prepositions = [
478
+ "aboard",
479
+ "about",
480
+ "above",
481
+ "across",
482
+ "after",
483
+ "against",
484
+ "along",
485
+ "amid",
486
+ "among",
487
+ "anti",
488
+ "around",
489
+ "as",
490
+ "at",
491
+ "before",
492
+ "behind",
493
+ "below",
494
+ "beneath",
495
+ "beside",
496
+ "besides",
497
+ "between",
498
+ "beyond",
499
+ "but",
500
+ "by",
501
+ "concerning",
502
+ "considering",
503
+ "despite",
504
+ "down",
505
+ "during",
506
+ "except",
507
+ "excepting",
508
+ "excluding",
509
+ "following",
510
+ "for",
511
+ "from",
512
+ "in",
513
+ "inside",
514
+ "into",
515
+ "like",
516
+ "minus",
517
+ "near",
518
+ "of",
519
+ "off",
520
+ "on",
521
+ "onto",
522
+ "opposite",
523
+ "over",
524
+ "past",
525
+ "per",
526
+ "plus",
527
+ "regarding",
528
+ "round",
529
+ "save",
530
+ "since",
531
+ "than",
532
+ "through",
533
+ "to",
534
+ "toward",
535
+ "towards",
536
+ "under",
537
+ "underneath",
538
+ "unlike",
539
+ "until",
540
+ "up",
541
+ "upon",
542
+ "versus",
543
+ "via",
544
+ "with",
545
+ "within",
546
+ "without"
547
+ ];
548
+ var lowerCase = /* @__PURE__ */ new Set([
549
+ ...conjunctions,
550
+ ...articles,
551
+ ...prepositions
552
+ ]);
553
+
554
+ // node_modules/title/dist/esm/specials.js
555
+ var specials = [
556
+ "ZEIT",
557
+ "ZEIT Inc.",
558
+ "Vercel",
559
+ "Vercel Inc.",
560
+ "CLI",
561
+ "API",
562
+ "HTTP",
563
+ "HTTPS",
564
+ "JSX",
565
+ "DNS",
566
+ "URL",
567
+ "now.sh",
568
+ "now.json",
569
+ "vercel.app",
570
+ "vercel.json",
571
+ "CI",
572
+ "CD",
573
+ "CDN",
574
+ "package.json",
575
+ "package.lock",
576
+ "yarn.lock",
577
+ "GitHub",
578
+ "GitLab",
579
+ "CSS",
580
+ "Sass",
581
+ "JS",
582
+ "JavaScript",
583
+ "TypeScript",
584
+ "HTML",
585
+ "WordPress",
586
+ "Next.js",
587
+ "Node.js",
588
+ "Webpack",
589
+ "Docker",
590
+ "Bash",
591
+ "Kubernetes",
592
+ "SWR",
593
+ "TinaCMS",
594
+ "UI",
595
+ "UX",
596
+ "TS",
597
+ "TSX",
598
+ "iPhone",
599
+ "iPad",
600
+ "watchOS",
601
+ "iOS",
602
+ "iPadOS",
603
+ "macOS",
604
+ "PHP",
605
+ "composer.json",
606
+ "composer.lock",
607
+ "CMS",
608
+ "SQL",
609
+ "C",
610
+ "C#",
611
+ "GraphQL",
612
+ "GraphiQL",
613
+ "JWT",
614
+ "JWTs"
615
+ ];
616
+
617
+ // node_modules/title/dist/esm/index.js
618
+ var word = `[^\\s'\u2019\\(\\)!?;:"-]`;
619
+ var regex = new RegExp(`(?:(?:(\\s?(?:^|[.\\(\\)!?;:"-])\\s*)(${word}))|(${word}))(${word}*[\u2019']*${word}*)`, "g");
620
+ var convertToRegExp = (specials2) => specials2.map((s) => [new RegExp(`\\b${s}\\b`, "gi"), s]);
621
+ function parseMatch(match) {
622
+ const firstCharacter = match[0];
623
+ if (/\s/.test(firstCharacter)) {
624
+ return match.slice(1);
475
625
  }
476
- if (queryType === "add" || queryType === "set" || toInstruction.ronin) {
477
- const defaults = {
478
- // If records are being created, set their creation time.
479
- ...inlineDefaultInsertionFields ? { createdAt: CURRENT_TIME_EXPRESSION } : {},
480
- // If records are being updated, bump their update time.
481
- ...queryType === "set" || inlineDefaultInsertionFields ? { updatedAt: CURRENT_TIME_EXPRESSION } : {},
482
- // Allow for overwriting the default values provided above.
483
- ...toInstruction.ronin
484
- };
485
- if (Object.keys(defaults).length > 0) defaultFields.ronin = defaults;
626
+ if (/[\(\)]/.test(firstCharacter)) {
627
+ return null;
486
628
  }
487
- const symbol = getQuerySymbol(toInstruction);
488
- if (symbol?.type === "query") {
489
- const { queryModel: subQueryModelSlug, queryInstructions: subQueryInstructions } = splitQuery(symbol.value);
490
- const subQueryModel = getModelBySlug(models, subQueryModelSlug);
491
- const subQuerySelectedFields = subQueryInstructions?.selecting;
492
- const subQueryIncludedFields = subQueryInstructions?.including;
493
- const subQueryFields = [
494
- ...filterSelectedFields(subQueryModel, subQuerySelectedFields).map(
495
- (field) => field.slug
496
- ),
497
- ...subQueryIncludedFields ? Object.keys(
498
- flatten(subQueryIncludedFields || {})
499
- ) : []
500
- ];
501
- for (const field of subQueryFields || []) {
502
- getFieldFromModel(model, field, { instructionName: "to" });
629
+ return match;
630
+ }
631
+ var src_default = (str, options = {}) => {
632
+ str = str.toLowerCase().replace(regex, (m, lead = "", forced, lower, rest, offset, string) => {
633
+ const isLastWord = m.length + offset >= string.length;
634
+ const parsedMatch = parseMatch(m);
635
+ if (!parsedMatch) {
636
+ return m;
503
637
  }
504
- let statement2 = "";
505
- if (subQuerySelectedFields) {
506
- const columns = subQueryFields.map((field) => {
507
- return getFieldFromModel(model, field, { instructionName: "to" }).fieldSelector;
508
- });
509
- statement2 = `(${columns.join(", ")}) `;
638
+ if (!forced) {
639
+ const fullLower = lower + rest;
640
+ if (lowerCase.has(fullLower) && !isLastWord) {
641
+ return parsedMatch;
642
+ }
510
643
  }
511
- statement2 += compileQueryInput(symbol.value, models, statementParams, {
512
- // biome-ignore lint/complexity/useSimplifiedLogicExpression: This is needed.
513
- inlineDefaults: options?.inlineDefaults || false
514
- }).main.statement;
515
- return statement2;
644
+ return lead + (lower || forced).toUpperCase() + rest;
645
+ });
646
+ const customSpecials = options.special || [];
647
+ const replace = [...specials, ...customSpecials];
648
+ const replaceRegExp = convertToRegExp(replace);
649
+ replaceRegExp.forEach(([pattern, s]) => {
650
+ str = str.replace(pattern, s);
651
+ });
652
+ return str;
653
+ };
654
+
655
+ // src/model/defaults.ts
656
+ var slugToName = (slug) => {
657
+ const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
658
+ return src_default(name);
659
+ };
660
+ var VOWELS = ["a", "e", "i", "o", "u"];
661
+ var pluralize = (word2) => {
662
+ const lastLetter = word2.slice(-1).toLowerCase();
663
+ const secondLastLetter = word2.slice(-2, -1).toLowerCase();
664
+ if (lastLetter === "y" && !VOWELS.includes(secondLastLetter)) {
665
+ return `${word2.slice(0, -1)}ies`;
516
666
  }
517
- Object.assign(toInstruction, defaultFields);
518
- for (const fieldSlug in toInstruction) {
519
- if (!Object.hasOwn(toInstruction, fieldSlug)) continue;
520
- const fieldValue = toInstruction[fieldSlug];
521
- const fieldDetails = getFieldFromModel(
522
- model,
523
- fieldSlug,
524
- { instructionName: "to" },
525
- false
526
- );
527
- if (fieldDetails?.field.type === "link" && fieldDetails.field.kind === "many") {
528
- delete toInstruction[fieldSlug];
529
- const associativeModelSlug = composeAssociationModelSlug(model, fieldDetails.field);
530
- const composeStatement = (subQueryType, value) => {
531
- const source = queryType === "add" ? toInstruction : withInstruction;
532
- const recordDetails = { source };
533
- if (value) recordDetails.target = value;
667
+ if (lastLetter === "s" || word2.slice(-2).toLowerCase() === "ch" || word2.slice(-2).toLowerCase() === "sh" || word2.slice(-2).toLowerCase() === "ex") {
668
+ return `${word2}es`;
669
+ }
670
+ return `${word2}s`;
671
+ };
672
+ var modelAttributes = [
673
+ ["pluralSlug", "slug", pluralize, true],
674
+ ["name", "slug", slugToName, false],
675
+ ["pluralName", "pluralSlug", slugToName, false],
676
+ ["idPrefix", "slug", (slug) => slug.slice(0, 3).toLowerCase(), false],
677
+ ["table", "pluralSlug", convertToSnakeCase, true]
678
+ ];
679
+ var getRecordIdentifier = (prefix) => {
680
+ return `${prefix}_${Array.from(crypto.getRandomValues(new Uint8Array(12))).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16).toLowerCase()}`;
681
+ };
682
+ var addDefaultModelAttributes = (model, isNew) => {
683
+ const copiedModel = { ...model };
684
+ if (isNew && !copiedModel.id) copiedModel.id = getRecordIdentifier("mod");
685
+ for (const [setting, base, generator, mustRegenerate] of modelAttributes) {
686
+ if (!(isNew || mustRegenerate)) continue;
687
+ if (copiedModel[setting] || !copiedModel[base]) continue;
688
+ copiedModel[setting] = generator(copiedModel[base]);
689
+ }
690
+ const newFields = copiedModel.fields || [];
691
+ if (isNew || Object.keys(newFields).length > 0) {
692
+ if (!copiedModel.identifiers) copiedModel.identifiers = {};
693
+ if (!copiedModel.identifiers.name) {
694
+ const suitableField = Object.entries(newFields).find(
695
+ ([fieldSlug, field]) => field.type === "string" && field.required === true && ["name"].includes(fieldSlug)
696
+ );
697
+ copiedModel.identifiers.name = suitableField?.[0] || "id";
698
+ }
699
+ if (!copiedModel.identifiers.slug) {
700
+ const suitableField = Object.entries(newFields).find(
701
+ ([fieldSlug, field]) => field.type === "string" && field.unique === true && field.required === true && ["slug", "handle"].includes(fieldSlug)
702
+ );
703
+ copiedModel.identifiers.slug = suitableField?.[0] || "id";
704
+ }
705
+ }
706
+ return copiedModel;
707
+ };
708
+ var addDefaultModelFields = (model, isNew) => {
709
+ const copiedModel = { ...model };
710
+ const existingFields = copiedModel.fields || [];
711
+ if (isNew || Object.keys(existingFields).length > 0) {
712
+ const additionalFields = Object.fromEntries(
713
+ Object.entries(getSystemFields(copiedModel.idPrefix)).filter(([newFieldSlug]) => {
714
+ return !Object.hasOwn(existingFields, newFieldSlug);
715
+ })
716
+ );
717
+ copiedModel.fields = { ...additionalFields, ...existingFields };
718
+ }
719
+ return copiedModel;
720
+ };
721
+ var addDefaultModelPresets = (list, model) => {
722
+ const defaultPresets = {};
723
+ for (const [fieldSlug, rest] of Object.entries(model.fields || {})) {
724
+ const field = { slug: fieldSlug, ...rest };
725
+ if (field.type === "link" && !fieldSlug.startsWith("ronin.")) {
726
+ const targetModel = getModelBySlug(list, field.target);
727
+ if (field.kind === "many") {
728
+ const systemModel = list.find(({ system }) => {
729
+ return system?.model === model.id && system?.associationSlug === field.slug;
730
+ });
731
+ if (!systemModel) continue;
732
+ const preset2 = {
733
+ instructions: {
734
+ // Perform a LEFT JOIN that adds the associative table.
735
+ including: {
736
+ [fieldSlug]: {
737
+ [QUERY_SYMBOLS.QUERY]: {
738
+ get: {
739
+ [systemModel.pluralSlug]: {
740
+ // ON associative_table.source = origin_model.id
741
+ with: {
742
+ source: {
743
+ [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}id`
744
+ }
745
+ },
746
+ // Perform a LEFT JOIN that adds the target model table.
747
+ including: {
748
+ [QUERY_SYMBOLS.QUERY]: {
749
+ get: {
750
+ [targetModel.slug]: {
751
+ // ON target_model.id = associative_table.target
752
+ with: {
753
+ id: {
754
+ [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}target`
755
+ }
756
+ }
757
+ }
758
+ }
759
+ }
760
+ },
761
+ selecting: ["**", "!source", "!target"]
762
+ }
763
+ }
764
+ }
765
+ }
766
+ }
767
+ }
768
+ };
769
+ defaultPresets[fieldSlug] = preset2;
770
+ continue;
771
+ }
772
+ const preset = {
773
+ instructions: {
774
+ including: {
775
+ [fieldSlug]: {
776
+ [QUERY_SYMBOLS.QUERY]: {
777
+ get: {
778
+ [targetModel.slug]: {
779
+ with: {
780
+ // Compare the `id` field of the related model to the link field on
781
+ // the root model (`field.slug`).
782
+ id: {
783
+ [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}${field.slug}`
784
+ }
785
+ }
786
+ }
787
+ }
788
+ }
789
+ }
790
+ }
791
+ }
792
+ };
793
+ defaultPresets[fieldSlug] = preset;
794
+ }
795
+ }
796
+ const childModels = list.map((subModel) => {
797
+ if (subModel.system?.associationSlug) return null;
798
+ const field = Object.entries(subModel.fields).find(([fieldSlug, rest]) => {
799
+ const field2 = { slug: fieldSlug, ...rest };
800
+ return field2.type === "link" && field2.target === model.slug;
801
+ });
802
+ if (!field) return null;
803
+ return { model: subModel, field: { slug: field[0], ...field[1] } };
804
+ }).filter((match) => match !== null);
805
+ for (const childMatch of childModels) {
806
+ const { model: childModel, field: childField } = childMatch;
807
+ const pluralSlug = childModel.pluralSlug;
808
+ const presetSlug = childModel.system?.associationSlug || pluralSlug;
809
+ const preset = {
810
+ instructions: {
811
+ including: {
812
+ [presetSlug]: {
813
+ [QUERY_SYMBOLS.QUERY]: {
814
+ get: {
815
+ [pluralSlug]: {
816
+ with: {
817
+ [childField.slug]: {
818
+ [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}id`
819
+ }
820
+ }
821
+ }
822
+ }
823
+ }
824
+ }
825
+ }
826
+ }
827
+ };
828
+ defaultPresets[presetSlug] = preset;
829
+ }
830
+ if (Object.keys(defaultPresets).length > 0) {
831
+ const existingPresets = model.presets;
832
+ const additionalPresets = Object.fromEntries(
833
+ Object.entries(defaultPresets).filter(([newPresetSlug]) => {
834
+ return !existingPresets?.[newPresetSlug];
835
+ })
836
+ );
837
+ model.presets = { ...additionalPresets, ...existingPresets };
838
+ }
839
+ return model;
840
+ };
841
+
842
+ // src/instructions/to.ts
843
+ var handleTo = (models, model, statementParams, queryType, dependencyStatements, instructions, options) => {
844
+ const { with: withInstruction, to: toInstruction } = instructions;
845
+ const defaultFields = {};
846
+ const currentTime = (/* @__PURE__ */ new Date()).toISOString();
847
+ if (queryType === "add" && options?.inlineDefaults) {
848
+ defaultFields.id = toInstruction.id || getRecordIdentifier(model.idPrefix);
849
+ }
850
+ if (queryType === "add" || queryType === "set" || toInstruction.ronin) {
851
+ const defaults = options?.inlineDefaults ? {
852
+ // If records are being created, set their creation time.
853
+ ...queryType === "add" && { createdAt: currentTime },
854
+ // If records are being updated or created, bump their update time.
855
+ updatedAt: currentTime,
856
+ // Allow for overwriting the default values provided above.
857
+ ...toInstruction.ronin
858
+ } : {
859
+ // If records are being updated, bump their update time.
860
+ // The creation time is already set using a default value in the DB.
861
+ ...queryType === "set" ? { updatedAt: CURRENT_TIME_EXPRESSION } : {},
862
+ // Allow for overwriting the default values provided above.
863
+ ...toInstruction.ronin
864
+ };
865
+ if (Object.keys(defaults).length > 0) defaultFields.ronin = defaults;
866
+ }
867
+ const symbol = getQuerySymbol(toInstruction);
868
+ if (symbol?.type === "query") {
869
+ const { queryModel: subQueryModelSlug, queryInstructions: subQueryInstructions } = splitQuery(symbol.value);
870
+ const subQueryModel = getModelBySlug(models, subQueryModelSlug);
871
+ const subQuerySelectedFields = subQueryInstructions?.selecting;
872
+ const subQueryIncludedFields = subQueryInstructions?.including;
873
+ const subQueryFields = [
874
+ ...filterSelectedFields(subQueryModel, subQuerySelectedFields).map(
875
+ (field) => field.slug
876
+ ),
877
+ ...subQueryIncludedFields ? Object.keys(
878
+ flatten(subQueryIncludedFields || {})
879
+ ) : []
880
+ ];
881
+ for (const field of subQueryFields || []) {
882
+ getFieldFromModel(model, field, { instructionName: "to" });
883
+ }
884
+ let statement2 = "";
885
+ if (subQuerySelectedFields) {
886
+ const columns = subQueryFields.map((field) => {
887
+ return getFieldFromModel(model, field, { instructionName: "to" }).fieldSelector;
888
+ });
889
+ statement2 = `(${columns.join(", ")}) `;
890
+ }
891
+ statement2 += compileQueryInput(symbol.value, models, statementParams, {
892
+ // biome-ignore lint/complexity/useSimplifiedLogicExpression: This is needed.
893
+ inlineDefaults: options?.inlineDefaults || false
894
+ }).main.statement;
895
+ return statement2;
896
+ }
897
+ Object.assign(toInstruction, defaultFields);
898
+ for (const fieldSlug in toInstruction) {
899
+ if (!Object.hasOwn(toInstruction, fieldSlug)) continue;
900
+ const fieldValue = toInstruction[fieldSlug];
901
+ const fieldDetails = getFieldFromModel(
902
+ model,
903
+ fieldSlug,
904
+ { instructionName: "to" },
905
+ false
906
+ );
907
+ if (fieldDetails?.field.type === "link" && fieldDetails.field.kind === "many") {
908
+ delete toInstruction[fieldSlug];
909
+ const associativeModelSlug = composeAssociationModelSlug(model, fieldDetails.field);
910
+ const composeStatement = (subQueryType, value) => {
911
+ const source = queryType === "add" ? toInstruction : withInstruction;
912
+ const recordDetails = { source };
913
+ if (value) recordDetails.target = value;
534
914
  const query = compileQueryInput(
535
915
  {
536
916
  [subQueryType]: {
@@ -1060,386 +1440,6 @@ var handleWith = (models, model, statementParams, instruction, parentModel) => {
1060
1440
  );
1061
1441
  };
1062
1442
 
1063
- // node_modules/title/dist/esm/lower-case.js
1064
- var conjunctions = [
1065
- "for",
1066
- "and",
1067
- "nor",
1068
- "but",
1069
- "or",
1070
- "yet",
1071
- "so"
1072
- ];
1073
- var articles = [
1074
- "a",
1075
- "an",
1076
- "the"
1077
- ];
1078
- var prepositions = [
1079
- "aboard",
1080
- "about",
1081
- "above",
1082
- "across",
1083
- "after",
1084
- "against",
1085
- "along",
1086
- "amid",
1087
- "among",
1088
- "anti",
1089
- "around",
1090
- "as",
1091
- "at",
1092
- "before",
1093
- "behind",
1094
- "below",
1095
- "beneath",
1096
- "beside",
1097
- "besides",
1098
- "between",
1099
- "beyond",
1100
- "but",
1101
- "by",
1102
- "concerning",
1103
- "considering",
1104
- "despite",
1105
- "down",
1106
- "during",
1107
- "except",
1108
- "excepting",
1109
- "excluding",
1110
- "following",
1111
- "for",
1112
- "from",
1113
- "in",
1114
- "inside",
1115
- "into",
1116
- "like",
1117
- "minus",
1118
- "near",
1119
- "of",
1120
- "off",
1121
- "on",
1122
- "onto",
1123
- "opposite",
1124
- "over",
1125
- "past",
1126
- "per",
1127
- "plus",
1128
- "regarding",
1129
- "round",
1130
- "save",
1131
- "since",
1132
- "than",
1133
- "through",
1134
- "to",
1135
- "toward",
1136
- "towards",
1137
- "under",
1138
- "underneath",
1139
- "unlike",
1140
- "until",
1141
- "up",
1142
- "upon",
1143
- "versus",
1144
- "via",
1145
- "with",
1146
- "within",
1147
- "without"
1148
- ];
1149
- var lowerCase = /* @__PURE__ */ new Set([
1150
- ...conjunctions,
1151
- ...articles,
1152
- ...prepositions
1153
- ]);
1154
-
1155
- // node_modules/title/dist/esm/specials.js
1156
- var specials = [
1157
- "ZEIT",
1158
- "ZEIT Inc.",
1159
- "Vercel",
1160
- "Vercel Inc.",
1161
- "CLI",
1162
- "API",
1163
- "HTTP",
1164
- "HTTPS",
1165
- "JSX",
1166
- "DNS",
1167
- "URL",
1168
- "now.sh",
1169
- "now.json",
1170
- "vercel.app",
1171
- "vercel.json",
1172
- "CI",
1173
- "CD",
1174
- "CDN",
1175
- "package.json",
1176
- "package.lock",
1177
- "yarn.lock",
1178
- "GitHub",
1179
- "GitLab",
1180
- "CSS",
1181
- "Sass",
1182
- "JS",
1183
- "JavaScript",
1184
- "TypeScript",
1185
- "HTML",
1186
- "WordPress",
1187
- "Next.js",
1188
- "Node.js",
1189
- "Webpack",
1190
- "Docker",
1191
- "Bash",
1192
- "Kubernetes",
1193
- "SWR",
1194
- "TinaCMS",
1195
- "UI",
1196
- "UX",
1197
- "TS",
1198
- "TSX",
1199
- "iPhone",
1200
- "iPad",
1201
- "watchOS",
1202
- "iOS",
1203
- "iPadOS",
1204
- "macOS",
1205
- "PHP",
1206
- "composer.json",
1207
- "composer.lock",
1208
- "CMS",
1209
- "SQL",
1210
- "C",
1211
- "C#",
1212
- "GraphQL",
1213
- "GraphiQL",
1214
- "JWT",
1215
- "JWTs"
1216
- ];
1217
-
1218
- // node_modules/title/dist/esm/index.js
1219
- var word = `[^\\s'\u2019\\(\\)!?;:"-]`;
1220
- var regex = new RegExp(`(?:(?:(\\s?(?:^|[.\\(\\)!?;:"-])\\s*)(${word}))|(${word}))(${word}*[\u2019']*${word}*)`, "g");
1221
- var convertToRegExp = (specials2) => specials2.map((s) => [new RegExp(`\\b${s}\\b`, "gi"), s]);
1222
- function parseMatch(match) {
1223
- const firstCharacter = match[0];
1224
- if (/\s/.test(firstCharacter)) {
1225
- return match.slice(1);
1226
- }
1227
- if (/[\(\)]/.test(firstCharacter)) {
1228
- return null;
1229
- }
1230
- return match;
1231
- }
1232
- var src_default = (str, options = {}) => {
1233
- str = str.toLowerCase().replace(regex, (m, lead = "", forced, lower, rest, offset, string) => {
1234
- const isLastWord = m.length + offset >= string.length;
1235
- const parsedMatch = parseMatch(m);
1236
- if (!parsedMatch) {
1237
- return m;
1238
- }
1239
- if (!forced) {
1240
- const fullLower = lower + rest;
1241
- if (lowerCase.has(fullLower) && !isLastWord) {
1242
- return parsedMatch;
1243
- }
1244
- }
1245
- return lead + (lower || forced).toUpperCase() + rest;
1246
- });
1247
- const customSpecials = options.special || [];
1248
- const replace = [...specials, ...customSpecials];
1249
- const replaceRegExp = convertToRegExp(replace);
1250
- replaceRegExp.forEach(([pattern, s]) => {
1251
- str = str.replace(pattern, s);
1252
- });
1253
- return str;
1254
- };
1255
-
1256
- // src/model/defaults.ts
1257
- var slugToName = (slug) => {
1258
- const name = slug.replace(/([a-z])([A-Z])/g, "$1 $2");
1259
- return src_default(name);
1260
- };
1261
- var VOWELS = ["a", "e", "i", "o", "u"];
1262
- var pluralize = (word2) => {
1263
- const lastLetter = word2.slice(-1).toLowerCase();
1264
- const secondLastLetter = word2.slice(-2, -1).toLowerCase();
1265
- if (lastLetter === "y" && !VOWELS.includes(secondLastLetter)) {
1266
- return `${word2.slice(0, -1)}ies`;
1267
- }
1268
- if (lastLetter === "s" || word2.slice(-2).toLowerCase() === "ch" || word2.slice(-2).toLowerCase() === "sh" || word2.slice(-2).toLowerCase() === "ex") {
1269
- return `${word2}es`;
1270
- }
1271
- return `${word2}s`;
1272
- };
1273
- var modelAttributes = [
1274
- ["pluralSlug", "slug", pluralize, true],
1275
- ["name", "slug", slugToName, false],
1276
- ["pluralName", "pluralSlug", slugToName, false],
1277
- ["idPrefix", "slug", (slug) => slug.slice(0, 3).toLowerCase(), false],
1278
- ["table", "pluralSlug", convertToSnakeCase, true]
1279
- ];
1280
- var getModelIdentifier = () => {
1281
- return `mod_${Array.from(crypto.getRandomValues(new Uint8Array(12))).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16).toLowerCase()}`;
1282
- };
1283
- var addDefaultModelAttributes = (model, isNew) => {
1284
- const copiedModel = { ...model };
1285
- if (isNew && !copiedModel.id) copiedModel.id = getModelIdentifier();
1286
- for (const [setting, base, generator, mustRegenerate] of modelAttributes) {
1287
- if (!(isNew || mustRegenerate)) continue;
1288
- if (copiedModel[setting] || !copiedModel[base]) continue;
1289
- copiedModel[setting] = generator(copiedModel[base]);
1290
- }
1291
- const newFields = copiedModel.fields || [];
1292
- if (isNew || Object.keys(newFields).length > 0) {
1293
- if (!copiedModel.identifiers) copiedModel.identifiers = {};
1294
- if (!copiedModel.identifiers.name) {
1295
- const suitableField = Object.entries(newFields).find(
1296
- ([fieldSlug, field]) => field.type === "string" && field.required === true && ["name"].includes(fieldSlug)
1297
- );
1298
- copiedModel.identifiers.name = suitableField?.[0] || "id";
1299
- }
1300
- if (!copiedModel.identifiers.slug) {
1301
- const suitableField = Object.entries(newFields).find(
1302
- ([fieldSlug, field]) => field.type === "string" && field.unique === true && field.required === true && ["slug", "handle"].includes(fieldSlug)
1303
- );
1304
- copiedModel.identifiers.slug = suitableField?.[0] || "id";
1305
- }
1306
- }
1307
- return copiedModel;
1308
- };
1309
- var addDefaultModelFields = (model, isNew) => {
1310
- const copiedModel = { ...model };
1311
- const existingFields = copiedModel.fields || [];
1312
- if (isNew || Object.keys(existingFields).length > 0) {
1313
- const additionalFields = Object.fromEntries(
1314
- Object.entries(getSystemFields(copiedModel.idPrefix)).filter(([newFieldSlug]) => {
1315
- return !Object.hasOwn(existingFields, newFieldSlug);
1316
- })
1317
- );
1318
- copiedModel.fields = { ...additionalFields, ...existingFields };
1319
- }
1320
- return copiedModel;
1321
- };
1322
- var addDefaultModelPresets = (list, model) => {
1323
- const defaultPresets = {};
1324
- for (const [fieldSlug, rest] of Object.entries(model.fields || {})) {
1325
- const field = { slug: fieldSlug, ...rest };
1326
- if (field.type === "link" && !fieldSlug.startsWith("ronin.")) {
1327
- const targetModel = getModelBySlug(list, field.target);
1328
- if (field.kind === "many") {
1329
- const systemModel = list.find(({ system }) => {
1330
- return system?.model === model.id && system?.associationSlug === field.slug;
1331
- });
1332
- if (!systemModel) continue;
1333
- const preset2 = {
1334
- instructions: {
1335
- // Perform a LEFT JOIN that adds the associative table.
1336
- including: {
1337
- [fieldSlug]: {
1338
- [QUERY_SYMBOLS.QUERY]: {
1339
- get: {
1340
- [systemModel.pluralSlug]: {
1341
- // ON associative_table.source = origin_model.id
1342
- with: {
1343
- source: {
1344
- [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}id`
1345
- }
1346
- },
1347
- // Perform a LEFT JOIN that adds the target model table.
1348
- including: {
1349
- [QUERY_SYMBOLS.QUERY]: {
1350
- get: {
1351
- [targetModel.slug]: {
1352
- // ON target_model.id = associative_table.target
1353
- with: {
1354
- id: {
1355
- [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}target`
1356
- }
1357
- }
1358
- }
1359
- }
1360
- }
1361
- },
1362
- selecting: ["**", "!source", "!target"]
1363
- }
1364
- }
1365
- }
1366
- }
1367
- }
1368
- }
1369
- };
1370
- defaultPresets[fieldSlug] = preset2;
1371
- continue;
1372
- }
1373
- const preset = {
1374
- instructions: {
1375
- including: {
1376
- [fieldSlug]: {
1377
- [QUERY_SYMBOLS.QUERY]: {
1378
- get: {
1379
- [targetModel.slug]: {
1380
- with: {
1381
- // Compare the `id` field of the related model to the link field on
1382
- // the root model (`field.slug`).
1383
- id: {
1384
- [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}${field.slug}`
1385
- }
1386
- }
1387
- }
1388
- }
1389
- }
1390
- }
1391
- }
1392
- }
1393
- };
1394
- defaultPresets[fieldSlug] = preset;
1395
- }
1396
- }
1397
- const childModels = list.map((subModel) => {
1398
- if (subModel.system?.associationSlug) return null;
1399
- const field = Object.entries(subModel.fields).find(([fieldSlug, rest]) => {
1400
- const field2 = { slug: fieldSlug, ...rest };
1401
- return field2.type === "link" && field2.target === model.slug;
1402
- });
1403
- if (!field) return null;
1404
- return { model: subModel, field: { slug: field[0], ...field[1] } };
1405
- }).filter((match) => match !== null);
1406
- for (const childMatch of childModels) {
1407
- const { model: childModel, field: childField } = childMatch;
1408
- const pluralSlug = childModel.pluralSlug;
1409
- const presetSlug = childModel.system?.associationSlug || pluralSlug;
1410
- const preset = {
1411
- instructions: {
1412
- including: {
1413
- [presetSlug]: {
1414
- [QUERY_SYMBOLS.QUERY]: {
1415
- get: {
1416
- [pluralSlug]: {
1417
- with: {
1418
- [childField.slug]: {
1419
- [QUERY_SYMBOLS.EXPRESSION]: `${QUERY_SYMBOLS.FIELD_PARENT}id`
1420
- }
1421
- }
1422
- }
1423
- }
1424
- }
1425
- }
1426
- }
1427
- }
1428
- };
1429
- defaultPresets[presetSlug] = preset;
1430
- }
1431
- if (Object.keys(defaultPresets).length > 0) {
1432
- const existingPresets = model.presets;
1433
- const additionalPresets = Object.fromEntries(
1434
- Object.entries(defaultPresets).filter(([newPresetSlug]) => {
1435
- return !existingPresets?.[newPresetSlug];
1436
- })
1437
- );
1438
- model.presets = { ...additionalPresets, ...existingPresets };
1439
- }
1440
- return model;
1441
- };
1442
-
1443
1443
  // src/model/index.ts
1444
1444
  var getModelBySlug = (models, slug) => {
1445
1445
  const model = models.find((model2) => {
@@ -1500,7 +1500,12 @@ var getSystemFields = (idPrefix) => ({
1500
1500
  id: {
1501
1501
  name: "ID",
1502
1502
  type: "string",
1503
- defaultValue: ID_EXPRESSION(idPrefix)
1503
+ defaultValue: {
1504
+ // Since default values in SQLite cannot rely on other columns, we unfortunately
1505
+ // cannot rely on the `idPrefix` column here. Instead, we need to inject it
1506
+ // directly into the expression as a static string.
1507
+ [QUERY_SYMBOLS.EXPRESSION]: `'${idPrefix}_' || lower(substr(hex(randomblob(12)), 1, 16))`
1508
+ }
1504
1509
  },
1505
1510
  "ronin.createdAt": {
1506
1511
  name: "RONIN - Created At",
@@ -1856,7 +1861,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query,
1856
1861
  }
1857
1862
  };
1858
1863
  }
1859
- const modelBeforeUpdate = structuredClone(JSON.parse(JSON.stringify(model)));
1864
+ const modelBeforeUpdate = structuredClone(model);
1860
1865
  const existingModel = model;
1861
1866
  const pluralType = PLURAL_MODEL_ENTITIES[entity];
1862
1867
  const existingEntity = existingModel[pluralType]?.[slug];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronin/compiler",
3
- "version": "0.17.6",
3
+ "version": "0.17.7",
4
4
  "type": "module",
5
5
  "description": "Compiles RONIN queries to SQL statements.",
6
6
  "publishConfig": {