@ronin/compiler 0.17.6 → 0.17.7-leo-ron-1099-experimental-394
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 +10 -2
- package/dist/index.js +599 -584
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
@@ -117,6 +117,14 @@ type AddQuery = Record<string, Omit<CombinedInstructions, 'with' | 'using'> & {
|
|
117
117
|
}>;
|
118
118
|
type RemoveQuery = Record<string, Omit<CombinedInstructions, 'to'>>;
|
119
119
|
type CountQuery = Record<string, Omit<CombinedInstructions, 'to'> | null>;
|
120
|
+
type AllQueryInstructions = {
|
121
|
+
for?: string;
|
122
|
+
};
|
123
|
+
type AllQuery = {
|
124
|
+
all: AllQueryInstructions | null;
|
125
|
+
};
|
126
|
+
type GetAllQuery = AllQuery;
|
127
|
+
type CountAllQuery = AllQuery;
|
120
128
|
type GetInstructions = Omit<CombinedInstructions, 'to'>;
|
121
129
|
type SetInstructions = Omit<CombinedInstructions, 'to'> & {
|
122
130
|
to: FieldSelector;
|
@@ -158,11 +166,11 @@ type DropQuery = {
|
|
158
166
|
model: string;
|
159
167
|
};
|
160
168
|
type Query = {
|
161
|
-
get?: GetQuery;
|
169
|
+
get?: GetQuery | GetAllQuery;
|
162
170
|
set?: SetQuery;
|
163
171
|
add?: AddQuery;
|
164
172
|
remove?: RemoveQuery;
|
165
|
-
count?: CountQuery;
|
173
|
+
count?: CountQuery | CountAllQuery;
|
166
174
|
create?: CreateQuery;
|
167
175
|
alter?: AlterQuery;
|
168
176
|
drop?: DropQuery;
|
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]: {
|
@@ -912,532 +1292,152 @@ var composeFieldValues = (models, model, statementParams, instructionName, value
|
|
912
1292
|
} else if (collectStatementValue) {
|
913
1293
|
conditionValue = prepareStatementValue(statementParams, conditionValue);
|
914
1294
|
}
|
915
|
-
if (options.type === "fields") return conditionSelector;
|
916
|
-
if (options.type === "values") return conditionValue;
|
917
|
-
return `${conditionSelector} ${conditionMatcher} ${conditionValue}`;
|
918
|
-
};
|
919
|
-
var composeConditions = (models, model, statementParams, instructionName, value, options) => {
|
920
|
-
const isNested = isObject(value) && Object.keys(value).length > 0;
|
921
|
-
if (isNested && Object.keys(value).every((key) => key in WITH_CONDITIONS)) {
|
922
|
-
const conditions = Object.entries(value).map(
|
923
|
-
([conditionType, checkValue]) => composeConditions(models, model, statementParams, instructionName, checkValue, {
|
924
|
-
...options,
|
925
|
-
condition: conditionType
|
926
|
-
})
|
927
|
-
);
|
928
|
-
return conditions.join(" AND ");
|
929
|
-
}
|
930
|
-
if (options.fieldSlug) {
|
931
|
-
const childField = Object.keys(model.fields).some((slug) => {
|
932
|
-
return slug.includes(".") && slug.split(".")[0] === options.fieldSlug;
|
933
|
-
});
|
934
|
-
if (!childField) {
|
935
|
-
const fieldDetails = getFieldFromModel(model, options.fieldSlug, {
|
936
|
-
instructionName
|
937
|
-
});
|
938
|
-
const { field: modelField } = fieldDetails || {};
|
939
|
-
const consumeJSON = (modelField?.type === "json" || modelField?.type === "blob") && instructionName === "to";
|
940
|
-
if (modelField && !(isObject(value) || Array.isArray(value)) || getQuerySymbol(value) || consumeJSON) {
|
941
|
-
return composeFieldValues(
|
942
|
-
models,
|
943
|
-
model,
|
944
|
-
statementParams,
|
945
|
-
instructionName,
|
946
|
-
value,
|
947
|
-
{ ...options, fieldSlug: options.fieldSlug }
|
948
|
-
);
|
949
|
-
}
|
950
|
-
if (modelField?.type === "link" && isNested) {
|
951
|
-
const keys = Object.keys(value);
|
952
|
-
const values = Object.values(value);
|
953
|
-
let recordTarget;
|
954
|
-
if (keys.length === 1 && keys[0] === "id") {
|
955
|
-
recordTarget = values[0];
|
956
|
-
} else {
|
957
|
-
const relatedModel = getModelBySlug(models, modelField.target);
|
958
|
-
const subQuery = {
|
959
|
-
get: {
|
960
|
-
[relatedModel.slug]: {
|
961
|
-
with: value,
|
962
|
-
selecting: ["id"]
|
963
|
-
}
|
964
|
-
}
|
965
|
-
};
|
966
|
-
recordTarget = {
|
967
|
-
[QUERY_SYMBOLS.QUERY]: subQuery
|
968
|
-
};
|
969
|
-
}
|
970
|
-
return composeConditions(
|
971
|
-
models,
|
972
|
-
model,
|
973
|
-
statementParams,
|
974
|
-
instructionName,
|
975
|
-
recordTarget,
|
976
|
-
options
|
977
|
-
);
|
978
|
-
}
|
979
|
-
}
|
980
|
-
}
|
981
|
-
if (isNested) {
|
982
|
-
const conditions = Object.entries(value).map(([field, value2]) => {
|
983
|
-
const nestedFieldSlug = options.fieldSlug ? `${options.fieldSlug}.${field}` : field;
|
984
|
-
return composeConditions(models, model, statementParams, instructionName, value2, {
|
985
|
-
...options,
|
986
|
-
fieldSlug: nestedFieldSlug
|
987
|
-
});
|
988
|
-
});
|
989
|
-
const joiner = instructionName === "to" ? ", " : " AND ";
|
990
|
-
if (instructionName === "to") return `${conditions.join(joiner)}`;
|
991
|
-
return conditions.length === 1 ? conditions[0] : options.fieldSlug ? `(${conditions.join(joiner)})` : conditions.join(joiner);
|
992
|
-
}
|
993
|
-
if (Array.isArray(value)) {
|
994
|
-
const conditions = value.map(
|
995
|
-
(filter) => composeConditions(models, model, statementParams, instructionName, filter, options)
|
996
|
-
);
|
997
|
-
return conditions.join(" OR ");
|
998
|
-
}
|
999
|
-
throw new RoninError({
|
1000
|
-
message: `The \`with\` instruction must not contain an empty field. The following fields are empty: \`${options.fieldSlug}\`. If you meant to query by an empty field, try using \`null\` instead.`,
|
1001
|
-
code: "INVALID_WITH_VALUE",
|
1002
|
-
queries: null
|
1003
|
-
});
|
1004
|
-
};
|
1005
|
-
var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
1006
|
-
if (!queryInstructions) return queryInstructions;
|
1007
|
-
const type = "with" in queryInstructions ? "with" : null;
|
1008
|
-
if (!type) return queryInstructions;
|
1009
|
-
const nestedInstructions = queryInstructions[type];
|
1010
|
-
if (!nestedInstructions || Array.isArray(nestedInstructions))
|
1011
|
-
return queryInstructions;
|
1012
|
-
const newNestedInstructions = { ...nestedInstructions };
|
1013
|
-
for (const oldKey of Object.keys(newNestedInstructions)) {
|
1014
|
-
if (oldKey !== "nameIdentifier" && oldKey !== "slugIdentifier") continue;
|
1015
|
-
const identifierName = oldKey === "nameIdentifier" ? "name" : "slug";
|
1016
|
-
const value = newNestedInstructions[oldKey];
|
1017
|
-
const newKey = identifiers[identifierName];
|
1018
|
-
newNestedInstructions[newKey] = value;
|
1019
|
-
delete newNestedInstructions[oldKey];
|
1020
|
-
}
|
1021
|
-
return {
|
1022
|
-
...queryInstructions,
|
1023
|
-
[type]: newNestedInstructions
|
1024
|
-
};
|
1025
|
-
};
|
1026
|
-
|
1027
|
-
// src/instructions/with.ts
|
1028
|
-
var getMatcher = (value, negative) => {
|
1029
|
-
if (negative) {
|
1030
|
-
if (value === null) return "IS NOT";
|
1031
|
-
return "!=";
|
1032
|
-
}
|
1033
|
-
if (value === null) return "IS";
|
1034
|
-
return "=";
|
1035
|
-
};
|
1036
|
-
var WITH_CONDITIONS = {
|
1037
|
-
being: (value) => [getMatcher(value, false), value],
|
1038
|
-
notBeing: (value) => [getMatcher(value, true), value],
|
1039
|
-
startingWith: (value) => ["LIKE", `${value}%`],
|
1040
|
-
notStartingWith: (value) => ["NOT LIKE", `${value}%`],
|
1041
|
-
endingWith: (value) => ["LIKE", `%${value}`],
|
1042
|
-
notEndingWith: (value) => ["NOT LIKE", `%${value}`],
|
1043
|
-
containing: (value) => ["LIKE", `%${value}%`],
|
1044
|
-
notContaining: (value) => ["NOT LIKE", `%${value}%`],
|
1045
|
-
greaterThan: (value) => [">", value],
|
1046
|
-
greaterOrEqual: (value) => [">=", value],
|
1047
|
-
lessThan: (value) => ["<", value],
|
1048
|
-
lessOrEqual: (value) => ["<=", value]
|
1049
|
-
};
|
1050
|
-
var handleWith = (models, model, statementParams, instruction, parentModel) => {
|
1051
|
-
return composeConditions(
|
1052
|
-
models,
|
1053
|
-
model,
|
1054
|
-
statementParams,
|
1055
|
-
"with",
|
1056
|
-
instruction,
|
1057
|
-
{
|
1058
|
-
parentModel
|
1059
|
-
}
|
1060
|
-
);
|
1061
|
-
};
|
1062
|
-
|
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;
|
1295
|
+
if (options.type === "fields") return conditionSelector;
|
1296
|
+
if (options.type === "values") return conditionValue;
|
1297
|
+
return `${conditionSelector} ${conditionMatcher} ${conditionValue}`;
|
1308
1298
|
};
|
1309
|
-
var
|
1310
|
-
const
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1299
|
+
var composeConditions = (models, model, statementParams, instructionName, value, options) => {
|
1300
|
+
const isNested = isObject(value) && Object.keys(value).length > 0;
|
1301
|
+
if (isNested && Object.keys(value).every((key) => key in WITH_CONDITIONS)) {
|
1302
|
+
const conditions = Object.entries(value).map(
|
1303
|
+
([conditionType, checkValue]) => composeConditions(models, model, statementParams, instructionName, checkValue, {
|
1304
|
+
...options,
|
1305
|
+
condition: conditionType
|
1316
1306
|
})
|
1317
1307
|
);
|
1318
|
-
|
1308
|
+
return conditions.join(" AND ");
|
1319
1309
|
}
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
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;
|
1310
|
+
if (options.fieldSlug) {
|
1311
|
+
const childField = Object.keys(model.fields).some((slug) => {
|
1312
|
+
return slug.includes(".") && slug.split(".")[0] === options.fieldSlug;
|
1313
|
+
});
|
1314
|
+
if (!childField) {
|
1315
|
+
const fieldDetails = getFieldFromModel(model, options.fieldSlug, {
|
1316
|
+
instructionName
|
1317
|
+
});
|
1318
|
+
const { field: modelField } = fieldDetails || {};
|
1319
|
+
const consumeJSON = (modelField?.type === "json" || modelField?.type === "blob") && instructionName === "to";
|
1320
|
+
if (modelField && !(isObject(value) || Array.isArray(value)) || getQuerySymbol(value) || consumeJSON) {
|
1321
|
+
return composeFieldValues(
|
1322
|
+
models,
|
1323
|
+
model,
|
1324
|
+
statementParams,
|
1325
|
+
instructionName,
|
1326
|
+
value,
|
1327
|
+
{ ...options, fieldSlug: options.fieldSlug }
|
1328
|
+
);
|
1372
1329
|
}
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
}
|
1387
|
-
}
|
1388
|
-
}
|
1330
|
+
if (modelField?.type === "link" && isNested) {
|
1331
|
+
const keys = Object.keys(value);
|
1332
|
+
const values = Object.values(value);
|
1333
|
+
let recordTarget;
|
1334
|
+
if (keys.length === 1 && keys[0] === "id") {
|
1335
|
+
recordTarget = values[0];
|
1336
|
+
} else {
|
1337
|
+
const relatedModel = getModelBySlug(models, modelField.target);
|
1338
|
+
const subQuery = {
|
1339
|
+
get: {
|
1340
|
+
[relatedModel.slug]: {
|
1341
|
+
with: value,
|
1342
|
+
selecting: ["id"]
|
1389
1343
|
}
|
1390
1344
|
}
|
1391
|
-
}
|
1345
|
+
};
|
1346
|
+
recordTarget = {
|
1347
|
+
[QUERY_SYMBOLS.QUERY]: subQuery
|
1348
|
+
};
|
1392
1349
|
}
|
1393
|
-
|
1394
|
-
|
1350
|
+
return composeConditions(
|
1351
|
+
models,
|
1352
|
+
model,
|
1353
|
+
statementParams,
|
1354
|
+
instructionName,
|
1355
|
+
recordTarget,
|
1356
|
+
options
|
1357
|
+
);
|
1358
|
+
}
|
1395
1359
|
}
|
1396
1360
|
}
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1361
|
+
if (isNested) {
|
1362
|
+
const conditions = Object.entries(value).map(([field, value2]) => {
|
1363
|
+
const nestedFieldSlug = options.fieldSlug ? `${options.fieldSlug}.${field}` : field;
|
1364
|
+
return composeConditions(models, model, statementParams, instructionName, value2, {
|
1365
|
+
...options,
|
1366
|
+
fieldSlug: nestedFieldSlug
|
1367
|
+
});
|
1402
1368
|
});
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
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;
|
1369
|
+
const joiner = instructionName === "to" ? ", " : " AND ";
|
1370
|
+
if (instructionName === "to") return `${conditions.join(joiner)}`;
|
1371
|
+
return conditions.length === 1 ? conditions[0] : options.fieldSlug ? `(${conditions.join(joiner)})` : conditions.join(joiner);
|
1430
1372
|
}
|
1431
|
-
if (
|
1432
|
-
const
|
1433
|
-
|
1434
|
-
Object.entries(defaultPresets).filter(([newPresetSlug]) => {
|
1435
|
-
return !existingPresets?.[newPresetSlug];
|
1436
|
-
})
|
1373
|
+
if (Array.isArray(value)) {
|
1374
|
+
const conditions = value.map(
|
1375
|
+
(filter) => composeConditions(models, model, statementParams, instructionName, filter, options)
|
1437
1376
|
);
|
1438
|
-
|
1377
|
+
return conditions.join(" OR ");
|
1439
1378
|
}
|
1440
|
-
|
1379
|
+
throw new RoninError({
|
1380
|
+
message: `The \`with\` instruction must not contain an empty field. The following fields are empty: \`${options.fieldSlug}\`. If you meant to query by an empty field, try using \`null\` instead.`,
|
1381
|
+
code: "INVALID_WITH_VALUE",
|
1382
|
+
queries: null
|
1383
|
+
});
|
1384
|
+
};
|
1385
|
+
var formatIdentifiers = ({ identifiers }, queryInstructions) => {
|
1386
|
+
if (!queryInstructions) return queryInstructions;
|
1387
|
+
const type = "with" in queryInstructions ? "with" : null;
|
1388
|
+
if (!type) return queryInstructions;
|
1389
|
+
const nestedInstructions = queryInstructions[type];
|
1390
|
+
if (!nestedInstructions || Array.isArray(nestedInstructions))
|
1391
|
+
return queryInstructions;
|
1392
|
+
const newNestedInstructions = { ...nestedInstructions };
|
1393
|
+
for (const oldKey of Object.keys(newNestedInstructions)) {
|
1394
|
+
if (oldKey !== "nameIdentifier" && oldKey !== "slugIdentifier") continue;
|
1395
|
+
const identifierName = oldKey === "nameIdentifier" ? "name" : "slug";
|
1396
|
+
const value = newNestedInstructions[oldKey];
|
1397
|
+
const newKey = identifiers[identifierName];
|
1398
|
+
newNestedInstructions[newKey] = value;
|
1399
|
+
delete newNestedInstructions[oldKey];
|
1400
|
+
}
|
1401
|
+
return {
|
1402
|
+
...queryInstructions,
|
1403
|
+
[type]: newNestedInstructions
|
1404
|
+
};
|
1405
|
+
};
|
1406
|
+
|
1407
|
+
// src/instructions/with.ts
|
1408
|
+
var getMatcher = (value, negative) => {
|
1409
|
+
if (negative) {
|
1410
|
+
if (value === null) return "IS NOT";
|
1411
|
+
return "!=";
|
1412
|
+
}
|
1413
|
+
if (value === null) return "IS";
|
1414
|
+
return "=";
|
1415
|
+
};
|
1416
|
+
var WITH_CONDITIONS = {
|
1417
|
+
being: (value) => [getMatcher(value, false), value],
|
1418
|
+
notBeing: (value) => [getMatcher(value, true), value],
|
1419
|
+
startingWith: (value) => ["LIKE", `${value}%`],
|
1420
|
+
notStartingWith: (value) => ["NOT LIKE", `${value}%`],
|
1421
|
+
endingWith: (value) => ["LIKE", `%${value}`],
|
1422
|
+
notEndingWith: (value) => ["NOT LIKE", `%${value}`],
|
1423
|
+
containing: (value) => ["LIKE", `%${value}%`],
|
1424
|
+
notContaining: (value) => ["NOT LIKE", `%${value}%`],
|
1425
|
+
greaterThan: (value) => [">", value],
|
1426
|
+
greaterOrEqual: (value) => [">=", value],
|
1427
|
+
lessThan: (value) => ["<", value],
|
1428
|
+
lessOrEqual: (value) => ["<=", value]
|
1429
|
+
};
|
1430
|
+
var handleWith = (models, model, statementParams, instruction, parentModel) => {
|
1431
|
+
return composeConditions(
|
1432
|
+
models,
|
1433
|
+
model,
|
1434
|
+
statementParams,
|
1435
|
+
"with",
|
1436
|
+
instruction,
|
1437
|
+
{
|
1438
|
+
parentModel
|
1439
|
+
}
|
1440
|
+
);
|
1441
1441
|
};
|
1442
1442
|
|
1443
1443
|
// src/model/index.ts
|
@@ -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",
|
@@ -1714,7 +1719,7 @@ var transformMetaQuery = (models, dependencyStatements, statementParams, query,
|
|
1714
1719
|
const action = subAltering && query.alter ? Object.keys(query.alter).filter((key) => key !== "model")[0] : queryType;
|
1715
1720
|
const actionReadable = action === "create" ? "creating" : action === "alter" ? "altering" : "dropping";
|
1716
1721
|
const entity = subAltering && query.alter ? Object.keys(query.alter[action])[0] : "model";
|
1717
|
-
let slug = entity === "model" && action === "create" ? null : query[queryType].model;
|
1722
|
+
let slug = entity === "model" && action === "create" ? null : query[queryType] && "model" in query[queryType] ? query[queryType].model : null;
|
1718
1723
|
let modelSlug = slug;
|
1719
1724
|
let jsonValue;
|
1720
1725
|
if ("create" in query && query.create) {
|
@@ -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];
|
@@ -2093,9 +2098,19 @@ var Transaction = class {
|
|
2093
2098
|
const expandedQueries = queries.flatMap((query, expansionIndex) => {
|
2094
2099
|
const { queryType, queryModel, queryInstructions } = splitQuery(query);
|
2095
2100
|
if (queryModel === "all") {
|
2096
|
-
|
2101
|
+
const { for: forInstruction, ...restInstructions } = queryInstructions || {};
|
2102
|
+
let modelList = modelsWithAttributes;
|
2103
|
+
if (forInstruction) {
|
2104
|
+
const mainModel = getModelBySlug(modelsWithAttributes, forInstruction);
|
2105
|
+
modelList = Object.values(mainModel.fields || {}).filter((field) => field.type === "link").map((field) => {
|
2106
|
+
return modelsWithAttributes.find(
|
2107
|
+
(model) => model.slug === field.target
|
2108
|
+
);
|
2109
|
+
});
|
2110
|
+
}
|
2111
|
+
return modelList.map((model) => {
|
2097
2112
|
const query2 = {
|
2098
|
-
[queryType]: { [model.pluralSlug]:
|
2113
|
+
[queryType]: { [model.pluralSlug]: restInstructions }
|
2099
2114
|
};
|
2100
2115
|
return { query: query2, expansionIndex };
|
2101
2116
|
});
|