@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.
- package/dist/index.js +454 -449
- 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
|
-
//
|
469
|
-
var
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
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 (
|
477
|
-
|
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
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
const
|
492
|
-
const
|
493
|
-
|
494
|
-
|
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
|
-
|
505
|
-
|
506
|
-
|
507
|
-
return
|
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
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
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
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
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:
|
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(
|
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];
|