@web-ts-toolkit/access-router 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +507 -0
- package/advanced.d.mts +49 -13
- package/advanced.d.ts +49 -13
- package/advanced.js +394 -12
- package/advanced.mjs +396 -13
- package/index.d.mts +134 -45
- package/index.d.ts +134 -45
- package/index.js +8707 -4826
- package/index.mjs +8705 -4814
- package/llms.txt +136 -1
- package/package.json +3 -3
- package/{parsers-D6iK8khR.d.mts → parsers-DhUuvj9B.d.mts} +810 -54
- package/{parsers-D6iK8khR.d.ts → parsers-DhUuvj9B.d.ts} +810 -54
package/advanced.mjs
CHANGED
|
@@ -57,7 +57,8 @@ import {
|
|
|
57
57
|
isPlainObject as isPlainObject9,
|
|
58
58
|
isString as isString3,
|
|
59
59
|
keys,
|
|
60
|
-
normalizeUrlPath
|
|
60
|
+
normalizeUrlPath,
|
|
61
|
+
set as set2
|
|
61
62
|
} from "@web-ts-toolkit/utils";
|
|
62
63
|
|
|
63
64
|
// src/helpers/query.ts
|
|
@@ -843,6 +844,286 @@ var refreshPermissionMetadata = (target) => {
|
|
|
843
844
|
target._globalPermissionKeys = globalPermissionKeys;
|
|
844
845
|
target._modelPermissionKeys = modelPermissionKeys;
|
|
845
846
|
};
|
|
847
|
+
var VIRTUAL_SUPPORTED_ACCESSES = ["default", "list", "create", "read", "update"];
|
|
848
|
+
var VIRTUAL_UNSUPPORTED_ACCESSES = ["delete", "distinct", "count"];
|
|
849
|
+
var VIRTUAL_RESERVED_NAMES = /* @__PURE__ */ new Set(["_id", "__v", "id"]);
|
|
850
|
+
var VIRTUAL_DANGEROUS_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
851
|
+
var VIRTUAL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
852
|
+
var isVirtualContainerValue = (value) => isPlainObject9(value) && "sub" in value;
|
|
853
|
+
var virtualFullPath = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
854
|
+
var virtualPathsOverlap = (virtualPath, permissionField) => {
|
|
855
|
+
if (!virtualPath || !permissionField) return false;
|
|
856
|
+
try {
|
|
857
|
+
if (get2(set2({}, virtualPath, true), permissionField) !== void 0) return true;
|
|
858
|
+
if (get2(set2({}, permissionField, true), virtualPath) !== void 0) return true;
|
|
859
|
+
} catch {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
return false;
|
|
863
|
+
};
|
|
864
|
+
var collectStoredTopLevel = (schema) => {
|
|
865
|
+
const fields = /* @__PURE__ */ new Set();
|
|
866
|
+
if (!schema) return fields;
|
|
867
|
+
const paths = schema.paths;
|
|
868
|
+
if (paths) {
|
|
869
|
+
for (const p of Object.keys(paths)) {
|
|
870
|
+
const top = p.split(".")[0];
|
|
871
|
+
if (top) fields.add(top);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const obj = schema.obj;
|
|
875
|
+
if (obj) {
|
|
876
|
+
for (const k of Object.keys(obj)) fields.add(k);
|
|
877
|
+
}
|
|
878
|
+
return fields;
|
|
879
|
+
};
|
|
880
|
+
var getChildSchemaForField = (parentSchema, fieldName) => {
|
|
881
|
+
if (!parentSchema) return null;
|
|
882
|
+
try {
|
|
883
|
+
const pathType = parentSchema.path?.(fieldName);
|
|
884
|
+
if (pathType && typeof pathType === "object") {
|
|
885
|
+
if (pathType.schema) return pathType.schema;
|
|
886
|
+
if (pathType.caster?.schema) return pathType.caster.schema;
|
|
887
|
+
}
|
|
888
|
+
} catch {
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
return null;
|
|
892
|
+
};
|
|
893
|
+
var getNestedChildFields = (parentSchema, fieldName) => {
|
|
894
|
+
const fields = /* @__PURE__ */ new Set();
|
|
895
|
+
if (!parentSchema) return fields;
|
|
896
|
+
const paths = parentSchema.paths;
|
|
897
|
+
if (paths) {
|
|
898
|
+
const prefix = `${fieldName}.`;
|
|
899
|
+
for (const p of Object.keys(paths)) {
|
|
900
|
+
if (p.startsWith(prefix)) {
|
|
901
|
+
const rest = p.slice(prefix.length);
|
|
902
|
+
const top = rest.split(".")[0];
|
|
903
|
+
if (top) fields.add(top);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return fields;
|
|
908
|
+
};
|
|
909
|
+
var cloneVirtualsForValidation = (value) => {
|
|
910
|
+
if (Array.isArray(value)) return value.map((item) => cloneVirtualsForValidation(item));
|
|
911
|
+
if (!value || typeof value !== "object") return value;
|
|
912
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) return value;
|
|
913
|
+
return Object.fromEntries(
|
|
914
|
+
Object.entries(value).map(([k, v]) => [k, cloneVirtualsForValidation(v)])
|
|
915
|
+
);
|
|
916
|
+
};
|
|
917
|
+
var validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirtualNames) => {
|
|
918
|
+
if (dependsOn === void 0) return [];
|
|
919
|
+
if (!isArray4(dependsOn)) {
|
|
920
|
+
throw new Error(`Virtual ${entryLabel}: dependsOn must be an array of top-level persisted field names`);
|
|
921
|
+
}
|
|
922
|
+
const list = dependsOn;
|
|
923
|
+
for (const dep of list) {
|
|
924
|
+
if (!isString3(dep) || dep.length === 0) {
|
|
925
|
+
throw new Error(`Virtual ${entryLabel}: dependsOn entries must be non-empty strings`);
|
|
926
|
+
}
|
|
927
|
+
if (dep.includes(".")) {
|
|
928
|
+
throw new Error(
|
|
929
|
+
`Virtual ${entryLabel}: dependsOn "${dep}" must be a top-level persisted name relative to its scope (dotted paths rejected)`
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
if (siblingVirtualNames.has(dep)) {
|
|
933
|
+
throw new Error(
|
|
934
|
+
`Virtual ${entryLabel}: dependsOn "${dep}" must not reference another virtual (virtual-to-virtual rejected)`
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
if (!storedFields.has(dep)) {
|
|
938
|
+
throw new Error(
|
|
939
|
+
`Virtual ${entryLabel}: dependsOn "${dep}" is not a persisted field in its scope (validated against the receiving Mongoose schema/child scope)`
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return list;
|
|
944
|
+
};
|
|
945
|
+
var validateVirtualLeafValue = (value, entryLabel, storedFields, siblingVirtualNames, sharedDependsOn) => {
|
|
946
|
+
if (isFunction(value)) return;
|
|
947
|
+
if (!isPlainObject9(value)) {
|
|
948
|
+
throw new Error(`Virtual ${entryLabel}: descriptor must be a getter function or { get, dependsOn }`);
|
|
949
|
+
}
|
|
950
|
+
const record = value;
|
|
951
|
+
if (!isFunction(record.get)) {
|
|
952
|
+
throw new Error(`Virtual ${entryLabel}: descriptor.get must be a callable getter function`);
|
|
953
|
+
}
|
|
954
|
+
validateVirtualDependsOn(record.dependsOn ?? sharedDependsOn, entryLabel, storedFields, siblingVirtualNames);
|
|
955
|
+
for (const k of Object.keys(record)) {
|
|
956
|
+
if (k !== "get" && k !== "dependsOn") {
|
|
957
|
+
throw new Error(`Virtual ${entryLabel}: unsupported descriptor key "${k}" (expected only get/dependsOn)`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
962
|
+
const { scopeVirtuals, storedFields, scopePrefix, scopeLabel } = scope;
|
|
963
|
+
const leafNames = /* @__PURE__ */ new Set();
|
|
964
|
+
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
965
|
+
if (raw === void 0) continue;
|
|
966
|
+
if (isVirtualContainerValue(raw)) continue;
|
|
967
|
+
leafNames.add(name);
|
|
968
|
+
}
|
|
969
|
+
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
970
|
+
if (raw === void 0) continue;
|
|
971
|
+
if (name.includes(".")) {
|
|
972
|
+
throw new Error(`Virtual ${scopeLabel}${name}: dotted definition names are rejected (use sub scopes)`);
|
|
973
|
+
}
|
|
974
|
+
if (VIRTUAL_DANGEROUS_SEGMENTS.has(name) || VIRTUAL_RESERVED_NAMES.has(name) || name.startsWith("$") || !VIRTUAL_NAME_PATTERN.test(name)) {
|
|
975
|
+
throw new Error(`Virtual ${scopeLabel}${name}: "_id"/reserved internal paths are rejected`);
|
|
976
|
+
}
|
|
977
|
+
const fullPath = virtualFullPath(scopePrefix, name);
|
|
978
|
+
if (virtualPathsOverlap(fullPath, documentPermissionField)) {
|
|
979
|
+
throw new Error(
|
|
980
|
+
`Virtual ${scopeLabel}${name}: overlaps document permission field "${documentPermissionField}" (equal/ancestor/descendant rejected)`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
if (isVirtualContainerValue(raw)) {
|
|
984
|
+
const record2 = raw;
|
|
985
|
+
const extra = Object.keys(record2).filter((k) => k !== "sub");
|
|
986
|
+
if (extra.length > 0) {
|
|
987
|
+
throw new Error(
|
|
988
|
+
`Virtual ${scopeLabel}${name}: scope-container must only hold "sub" (embedded container "${name}" is not a virtual-leaf collision)`
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
if (!isPlainObject9(record2.sub)) {
|
|
992
|
+
throw new Error(`Virtual ${scopeLabel}${name}: scope-container "sub" must be a plain object`);
|
|
993
|
+
}
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
if (storedFields.has(name)) {
|
|
997
|
+
throw new Error(
|
|
998
|
+
`Virtual ${scopeLabel}${name}: collides with a stored path (inspected actual Mongoose schema/child scope; getModelAtt alone is insufficient)`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
if (isFunction(raw)) continue;
|
|
1002
|
+
if (!isPlainObject9(raw)) {
|
|
1003
|
+
throw new Error(
|
|
1004
|
+
`Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record)`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
const record = raw;
|
|
1008
|
+
const hasGet = "get" in record;
|
|
1009
|
+
const accessKeys = Object.keys(record).filter((k) => VIRTUAL_SUPPORTED_ACCESSES.includes(k));
|
|
1010
|
+
const unsupported = Object.keys(record).filter(
|
|
1011
|
+
(k) => k !== "get" && k !== "dependsOn" && k !== "sub" && !VIRTUAL_SUPPORTED_ACCESSES.includes(k)
|
|
1012
|
+
);
|
|
1013
|
+
for (const key of Object.keys(record)) {
|
|
1014
|
+
if (VIRTUAL_UNSUPPORTED_ACCESSES.includes(key)) {
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
`Virtual ${scopeLabel}${name}: unsupported access "${key}" (supported: default + list/create/read/update; NOT delete/distinct/count)`
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (unsupported.length > 0) {
|
|
1021
|
+
throw new Error(
|
|
1022
|
+
`Virtual ${scopeLabel}${name}: unsupported access/key "${unsupported[0]}" (supported: default + list/create/read/update)`
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
if (hasGet) {
|
|
1026
|
+
if (accessKeys.length > 0) {
|
|
1027
|
+
throw new Error(
|
|
1028
|
+
`Virtual ${scopeLabel}${name}: cannot mix "get" with per-access keys (use either bare { get, dependsOn } or a per-access record)`
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
if ("sub" in record) {
|
|
1032
|
+
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix "get" with "sub" (leaf vs embedded container)`);
|
|
1033
|
+
}
|
|
1034
|
+
validateVirtualLeafValue(raw, `${scopeLabel}${name}`, storedFields, leafNames, void 0);
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (accessKeys.length > 0) {
|
|
1038
|
+
if ("sub" in record) {
|
|
1039
|
+
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix per-access keys with "sub"`);
|
|
1040
|
+
}
|
|
1041
|
+
validateVirtualDependsOn(record.dependsOn, `${scopeLabel}${name}`, storedFields, leafNames);
|
|
1042
|
+
for (const access of accessKeys) {
|
|
1043
|
+
const leaf = record[access];
|
|
1044
|
+
if (leaf === void 0) continue;
|
|
1045
|
+
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames, record.dependsOn);
|
|
1046
|
+
}
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
throw new Error(
|
|
1050
|
+
`Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record with default/list/create/read/update)`
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName) => {
|
|
1055
|
+
if (virtuals === void 0 || virtuals === null) return;
|
|
1056
|
+
if (!isPlainObject9(virtuals)) {
|
|
1057
|
+
throw new Error(`Virtuals for model "${modelName}": virtuals must be a plain object mapping names to descriptors`);
|
|
1058
|
+
}
|
|
1059
|
+
const rootSchema = model?.schema ?? null;
|
|
1060
|
+
const rootStored = collectStoredTopLevel(rootSchema);
|
|
1061
|
+
const rootVirtuals = virtuals;
|
|
1062
|
+
validateVirtualsScope(
|
|
1063
|
+
{ scopeVirtuals: rootVirtuals, storedFields: rootStored, scopePrefix: "", scopeLabel: "" },
|
|
1064
|
+
documentPermissionField
|
|
1065
|
+
);
|
|
1066
|
+
const visitContainers = (currentVirtuals, currentSchema, currentPrefix) => {
|
|
1067
|
+
for (const [containerName, raw] of Object.entries(currentVirtuals)) {
|
|
1068
|
+
if (raw === void 0 || !isVirtualContainerValue(raw)) continue;
|
|
1069
|
+
const record = raw;
|
|
1070
|
+
const childSchema = getChildSchemaForField(currentSchema, containerName);
|
|
1071
|
+
const nestedFields = getNestedChildFields(currentSchema, containerName);
|
|
1072
|
+
const currentStored = collectStoredTopLevel(currentSchema);
|
|
1073
|
+
if (!currentStored.has(containerName)) {
|
|
1074
|
+
throw new Error(
|
|
1075
|
+
`Virtual ${containerName}: scope-container "${containerName}" is not a persisted field in its scope`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
const isEmbedded = childSchema !== null || nestedFields.size > 0;
|
|
1079
|
+
if (!isEmbedded) {
|
|
1080
|
+
throw new Error(
|
|
1081
|
+
`Virtual ${containerName}: scope-container "${containerName}" must be an embedded field with a child scope`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
const subStored = childSchema ? collectStoredTopLevel(childSchema) : nestedFields;
|
|
1085
|
+
const subPrefix = currentPrefix ? `${currentPrefix}.${containerName}` : containerName;
|
|
1086
|
+
const subVirtuals = record.sub ?? {};
|
|
1087
|
+
validateVirtualsScope(
|
|
1088
|
+
{
|
|
1089
|
+
scopeVirtuals: subVirtuals,
|
|
1090
|
+
storedFields: subStored,
|
|
1091
|
+
scopePrefix: subPrefix,
|
|
1092
|
+
scopeLabel: `${subPrefix}.sub.`
|
|
1093
|
+
},
|
|
1094
|
+
documentPermissionField
|
|
1095
|
+
);
|
|
1096
|
+
const nextSchema = childSchema;
|
|
1097
|
+
visitContainers(subVirtuals, nextSchema, subPrefix);
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
visitContainers(rootVirtuals, rootSchema, "");
|
|
1101
|
+
};
|
|
1102
|
+
var getScopeVirtualsObject = (rootVirtuals, scopePath) => {
|
|
1103
|
+
if (!rootVirtuals || !isPlainObject9(rootVirtuals)) return null;
|
|
1104
|
+
if (!scopePath || scopePath.length === 0) return rootVirtuals;
|
|
1105
|
+
let current = rootVirtuals;
|
|
1106
|
+
for (let i = 0; i < scopePath.length; i += 2) {
|
|
1107
|
+
const field = scopePath[i];
|
|
1108
|
+
const subMarker = scopePath[i + 1];
|
|
1109
|
+
if (!field || subMarker !== "sub") return null;
|
|
1110
|
+
if (!isPlainObject9(current)) return null;
|
|
1111
|
+
const container = current[field];
|
|
1112
|
+
if (!isVirtualContainerValue(container)) return null;
|
|
1113
|
+
current = container.sub;
|
|
1114
|
+
if (!isPlainObject9(current)) return null;
|
|
1115
|
+
}
|
|
1116
|
+
return current;
|
|
1117
|
+
};
|
|
1118
|
+
var toVirtualDescriptor = (leaf, sharedDependsOn) => {
|
|
1119
|
+
if (isFunction(leaf))
|
|
1120
|
+
return { get: leaf, dependsOn: sharedDependsOn ?? [] };
|
|
1121
|
+
if (!isPlainObject9(leaf)) return null;
|
|
1122
|
+
const record = leaf;
|
|
1123
|
+
if (!isFunction(record.get)) return null;
|
|
1124
|
+
const dependsOn = record.dependsOn ?? sharedDependsOn ?? [];
|
|
1125
|
+
return { get: record.get, dependsOn };
|
|
1126
|
+
};
|
|
846
1127
|
var AccessRuntime = class {
|
|
847
1128
|
constructor(options = {}) {
|
|
848
1129
|
this.globalOptions = new OptionsManager(
|
|
@@ -1080,10 +1361,46 @@ var AccessRuntime = class {
|
|
|
1080
1361
|
const manager = this.getOrCreateModelOptions(modelName);
|
|
1081
1362
|
const defaultOptions = this.getDefaultModelOptions();
|
|
1082
1363
|
const currentModelOptions = manager.fetch();
|
|
1364
|
+
const merged = { ...defaultOptions, ...currentModelOptions, ...options };
|
|
1365
|
+
const model = this.getModelInstance(modelName);
|
|
1366
|
+
const permissionField = merged.documentPermissionField ?? currentModelOptions.documentPermissionField ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1367
|
+
validateVirtualsTree(model, merged.virtuals, permissionField, modelName);
|
|
1083
1368
|
manager.assign({ ...defaultOptions, ...currentModelOptions, ...options });
|
|
1084
1369
|
}
|
|
1085
1370
|
setModelOption(modelName, key, value) {
|
|
1086
1371
|
const manager = this.getOrCreateModelOptions(modelName);
|
|
1372
|
+
const keyStr = String(key);
|
|
1373
|
+
if (keyStr === "virtuals" || keyStr.startsWith("virtuals.") || keyStr === "documentPermissionField") {
|
|
1374
|
+
const model = this.getModelInstance(modelName);
|
|
1375
|
+
const currentVirtuals = manager.get("virtuals");
|
|
1376
|
+
const currentPermissionField = manager.get("documentPermissionField") ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1377
|
+
let proposedVirtuals = currentVirtuals;
|
|
1378
|
+
let proposedPermissionField = currentPermissionField;
|
|
1379
|
+
if (keyStr === "documentPermissionField") {
|
|
1380
|
+
proposedPermissionField = value;
|
|
1381
|
+
} else if (keyStr === "virtuals") {
|
|
1382
|
+
proposedVirtuals = value;
|
|
1383
|
+
} else {
|
|
1384
|
+
const cloned = cloneVirtualsForValidation(currentVirtuals ?? {});
|
|
1385
|
+
const nestedPath = keyStr.slice("virtuals.".length);
|
|
1386
|
+
if (value === void 0) {
|
|
1387
|
+
const segments = nestedPath.split(".");
|
|
1388
|
+
let cursor = cloned;
|
|
1389
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1390
|
+
if (!isPlainObject9(cursor)) break;
|
|
1391
|
+
cursor = cursor[segments[i]];
|
|
1392
|
+
}
|
|
1393
|
+
if (isPlainObject9(cursor)) {
|
|
1394
|
+
delete cursor[segments[segments.length - 1]];
|
|
1395
|
+
}
|
|
1396
|
+
proposedVirtuals = cloned;
|
|
1397
|
+
} else {
|
|
1398
|
+
set2(cloned, nestedPath, value);
|
|
1399
|
+
proposedVirtuals = cloned;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
validateVirtualsTree(model, proposedVirtuals, proposedPermissionField, modelName);
|
|
1403
|
+
}
|
|
1087
1404
|
manager.set(key, value);
|
|
1088
1405
|
}
|
|
1089
1406
|
getModelOptions(modelName) {
|
|
@@ -1103,6 +1420,71 @@ var AccessRuntime = class {
|
|
|
1103
1420
|
const defaultModelValue = this.getDefaultModelOption(key);
|
|
1104
1421
|
return manager.get(key, defaultModelValue);
|
|
1105
1422
|
}
|
|
1423
|
+
/**
|
|
1424
|
+
* List registered virtual leaf names for a scope (VIRT-01).
|
|
1425
|
+
* Scope containers (`{ sub }`) are not leaves. Inapplicable names are
|
|
1426
|
+
* still listed here; applicability is decided per-access by
|
|
1427
|
+
* {@link resolveVirtualDescriptor}. Request-controlled include collisions
|
|
1428
|
+
* are validated by VIRT-05, not guessed here.
|
|
1429
|
+
*/
|
|
1430
|
+
getVirtualNames(modelName, scopePath) {
|
|
1431
|
+
const manager = this.getOrCreateModelOptions(modelName);
|
|
1432
|
+
const rootVirtuals = manager.get("virtuals");
|
|
1433
|
+
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1434
|
+
if (!scopeVirtuals) return [];
|
|
1435
|
+
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !isVirtualContainerValue(v)).map(([k]) => k);
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
1439
|
+
* Inapplicable registered names stay virtual (never persisted), even when
|
|
1440
|
+
* no getter applies for the current access. Containers and persisted
|
|
1441
|
+
* fields return false.
|
|
1442
|
+
*/
|
|
1443
|
+
isVirtualField(modelName, fieldName, scopePath) {
|
|
1444
|
+
const manager = this.getOrCreateModelOptions(modelName);
|
|
1445
|
+
const rootVirtuals = manager.get("virtuals");
|
|
1446
|
+
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1447
|
+
if (!scopeVirtuals || !(fieldName in scopeVirtuals)) return false;
|
|
1448
|
+
const entry = scopeVirtuals[fieldName];
|
|
1449
|
+
if (entry === void 0 || isVirtualContainerValue(entry)) return false;
|
|
1450
|
+
return true;
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Resolve an applicable virtual descriptor with exact → `.default` → bare
|
|
1454
|
+
* semantics (VIRT-01, VIRT-00A D1/D5). Returns `undefined` when no getter
|
|
1455
|
+
* applies for `access`. Crucially, an access-record object is NEVER
|
|
1456
|
+
* interpreted as a getter descriptor when the access is absent: an
|
|
1457
|
+
* inapplicable registered name stays virtual (excluded from persisted
|
|
1458
|
+
* projections, write admission, and DB sort/filter/distinct).
|
|
1459
|
+
* Shared record-level `dependsOn` is inherited when a leaf lacks its own.
|
|
1460
|
+
*/
|
|
1461
|
+
resolveVirtualDescriptor(modelName, virtualName, access, scopePath) {
|
|
1462
|
+
const manager = this.getOrCreateModelOptions(modelName);
|
|
1463
|
+
const rootVirtuals = manager.get("virtuals");
|
|
1464
|
+
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1465
|
+
if (!scopeVirtuals || !(virtualName in scopeVirtuals)) return void 0;
|
|
1466
|
+
const entry = scopeVirtuals[virtualName];
|
|
1467
|
+
if (entry === void 0 || isVirtualContainerValue(entry)) return void 0;
|
|
1468
|
+
if (isFunction(entry)) return { get: entry, dependsOn: [] };
|
|
1469
|
+
if (!isPlainObject9(entry)) return void 0;
|
|
1470
|
+
const record = entry;
|
|
1471
|
+
if ("get" in record) {
|
|
1472
|
+
const descriptor = toVirtualDescriptor(entry, void 0);
|
|
1473
|
+
return descriptor ?? void 0;
|
|
1474
|
+
}
|
|
1475
|
+
const sharedDependsOn = record.dependsOn;
|
|
1476
|
+
const exact = record[access];
|
|
1477
|
+
if (exact !== void 0) {
|
|
1478
|
+
const descriptor = toVirtualDescriptor(exact, sharedDependsOn);
|
|
1479
|
+
if (descriptor) return descriptor;
|
|
1480
|
+
}
|
|
1481
|
+
const fallback = record.default;
|
|
1482
|
+
if (fallback !== void 0) {
|
|
1483
|
+
const descriptor = toVirtualDescriptor(fallback, sharedDependsOn);
|
|
1484
|
+
if (descriptor) return descriptor;
|
|
1485
|
+
}
|
|
1486
|
+
return void 0;
|
|
1487
|
+
}
|
|
1106
1488
|
getModelNames() {
|
|
1107
1489
|
return Object.keys(this.modelOptions);
|
|
1108
1490
|
}
|
|
@@ -1401,6 +1783,7 @@ var sortSchema = z2.union([
|
|
|
1401
1783
|
z2.array(z2.tuple([z2.string(), sortOrderSchema])),
|
|
1402
1784
|
z2.null()
|
|
1403
1785
|
]);
|
|
1786
|
+
var populateAccessSchema = z2.enum(["list", "read"]);
|
|
1404
1787
|
var populateSchema = z2.union([
|
|
1405
1788
|
z2.string(),
|
|
1406
1789
|
z2.array(
|
|
@@ -1410,7 +1793,7 @@ var populateSchema = z2.union([
|
|
|
1410
1793
|
path: z2.string().min(1),
|
|
1411
1794
|
select: projectionSchema.optional(),
|
|
1412
1795
|
match: z2.unknown().optional(),
|
|
1413
|
-
access:
|
|
1796
|
+
access: populateAccessSchema.optional()
|
|
1414
1797
|
}).passthrough()
|
|
1415
1798
|
])
|
|
1416
1799
|
),
|
|
@@ -1418,7 +1801,7 @@ var populateSchema = z2.union([
|
|
|
1418
1801
|
path: z2.string().min(1),
|
|
1419
1802
|
select: projectionSchema.optional(),
|
|
1420
1803
|
match: z2.unknown().optional(),
|
|
1421
|
-
access:
|
|
1804
|
+
access: populateAccessSchema.optional()
|
|
1422
1805
|
}).passthrough()
|
|
1423
1806
|
]);
|
|
1424
1807
|
var subPopulateSchema = z2.union([
|
|
@@ -2126,7 +2509,7 @@ var listBodySchema = z3.object({
|
|
|
2126
2509
|
includeFieldPermissions: z3.boolean().optional(),
|
|
2127
2510
|
includeCount: z3.boolean().optional(),
|
|
2128
2511
|
includeExtraHeaders: z3.boolean().optional(),
|
|
2129
|
-
populateAccess:
|
|
2512
|
+
populateAccess: populateAccessSchema.optional()
|
|
2130
2513
|
}).passthrough().optional()
|
|
2131
2514
|
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
|
|
2132
2515
|
var countBodySchema = z3.object({
|
|
@@ -2144,7 +2527,7 @@ var readFilterBodySchema = z3.object({
|
|
|
2144
2527
|
includePermissions: z3.boolean().optional(),
|
|
2145
2528
|
includeFieldPermissions: z3.boolean().optional(),
|
|
2146
2529
|
tryList: z3.boolean().optional(),
|
|
2147
|
-
populateAccess:
|
|
2530
|
+
populateAccess: populateAccessSchema.optional()
|
|
2148
2531
|
}).passthrough().optional()
|
|
2149
2532
|
}).passthrough();
|
|
2150
2533
|
var readByIdBodySchema = z3.object({
|
|
@@ -2157,7 +2540,7 @@ var readByIdBodySchema = z3.object({
|
|
|
2157
2540
|
includePermissions: z3.boolean().optional(),
|
|
2158
2541
|
includeFieldPermissions: z3.boolean().optional(),
|
|
2159
2542
|
tryList: z3.boolean().optional(),
|
|
2160
|
-
populateAccess:
|
|
2543
|
+
populateAccess: populateAccessSchema.optional()
|
|
2161
2544
|
}).passthrough().optional()
|
|
2162
2545
|
}).passthrough();
|
|
2163
2546
|
var advancedCreateBodySchema = z3.object({
|
|
@@ -2168,7 +2551,7 @@ var advancedCreateBodySchema = z3.object({
|
|
|
2168
2551
|
options: z3.object({
|
|
2169
2552
|
includePermissions: z3.boolean().optional(),
|
|
2170
2553
|
includeFieldPermissions: z3.boolean().optional(),
|
|
2171
|
-
populateAccess:
|
|
2554
|
+
populateAccess: populateAccessSchema.optional()
|
|
2172
2555
|
}).passthrough().optional()
|
|
2173
2556
|
}).passthrough();
|
|
2174
2557
|
var createBodySchema = z3.union([
|
|
@@ -2185,7 +2568,7 @@ var advancedUpdateBodySchema = z3.object({
|
|
|
2185
2568
|
returningAll: z3.boolean().optional(),
|
|
2186
2569
|
includePermissions: z3.boolean().optional(),
|
|
2187
2570
|
includeFieldPermissions: z3.boolean().optional(),
|
|
2188
|
-
populateAccess:
|
|
2571
|
+
populateAccess: populateAccessSchema.optional()
|
|
2189
2572
|
}).passthrough().optional()
|
|
2190
2573
|
}).passthrough();
|
|
2191
2574
|
var upsertBodySchema = z3.record(z3.string(), z3.unknown());
|
|
@@ -2198,7 +2581,7 @@ var advancedUpsertBodySchema = z3.object({
|
|
|
2198
2581
|
returningAll: z3.boolean().optional(),
|
|
2199
2582
|
includePermissions: z3.boolean().optional(),
|
|
2200
2583
|
includeFieldPermissions: z3.boolean().optional(),
|
|
2201
|
-
populateAccess:
|
|
2584
|
+
populateAccess: populateAccessSchema.optional()
|
|
2202
2585
|
}).passthrough().optional()
|
|
2203
2586
|
}).passthrough();
|
|
2204
2587
|
var distinctBodySchema = z3.object({
|
|
@@ -2270,7 +2653,7 @@ var rootModelListOptionsSchema = z5.object({
|
|
|
2270
2653
|
includePermissions: z5.boolean().optional(),
|
|
2271
2654
|
includeFieldPermissions: z5.boolean().optional(),
|
|
2272
2655
|
includeCount: z5.boolean().optional(),
|
|
2273
|
-
populateAccess:
|
|
2656
|
+
populateAccess: populateAccessSchema.optional(),
|
|
2274
2657
|
lean: z5.boolean().optional()
|
|
2275
2658
|
}).passthrough();
|
|
2276
2659
|
var rootModelReadArgsSchema = z5.object({
|
|
@@ -2287,7 +2670,7 @@ var rootModelReadOptionsSchema = z5.object({
|
|
|
2287
2670
|
includePermissions: z5.boolean().optional(),
|
|
2288
2671
|
includeFieldPermissions: z5.boolean().optional(),
|
|
2289
2672
|
tryList: z5.boolean().optional(),
|
|
2290
|
-
populateAccess:
|
|
2673
|
+
populateAccess: populateAccessSchema.optional(),
|
|
2291
2674
|
lean: z5.boolean().optional()
|
|
2292
2675
|
}).passthrough();
|
|
2293
2676
|
var rootModelCreateArgsSchema = z5.object({
|
|
@@ -2299,7 +2682,7 @@ var rootModelCreateOptionsSchema = z5.object({
|
|
|
2299
2682
|
skim: z5.boolean().optional(),
|
|
2300
2683
|
includePermissions: z5.boolean().optional(),
|
|
2301
2684
|
includeFieldPermissions: z5.boolean().optional(),
|
|
2302
|
-
populateAccess:
|
|
2685
|
+
populateAccess: populateAccessSchema.optional()
|
|
2303
2686
|
}).passthrough();
|
|
2304
2687
|
var rootModelUpdateArgsSchema = z5.object({
|
|
2305
2688
|
select: projectionSchema.optional(),
|
|
@@ -2311,7 +2694,7 @@ var rootModelUpdateOptionsSchema = z5.object({
|
|
|
2311
2694
|
returningAll: z5.boolean().optional(),
|
|
2312
2695
|
includePermissions: z5.boolean().optional(),
|
|
2313
2696
|
includeFieldPermissions: z5.boolean().optional(),
|
|
2314
|
-
populateAccess:
|
|
2697
|
+
populateAccess: populateAccessSchema.optional()
|
|
2315
2698
|
}).passthrough();
|
|
2316
2699
|
var rootModelUpsertArgsSchema = rootModelUpdateArgsSchema;
|
|
2317
2700
|
var rootModelUpsertOptionsSchema = rootModelUpdateOptionsSchema;
|