@web-ts-toolkit/access-router 0.56.0 → 0.58.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 +177 -120
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +95 -66
- package/advanced.mjs +62 -33
- package/index.d.mts +2 -2
- package/index.d.ts +2 -2
- package/index.js +965 -623
- package/index.mjs +703 -361
- package/llms.txt +2 -2
- package/package.json +3 -3
- package/{parsers-BXKoUKMa.d.mts → parsers-CngKnVjW.d.mts} +65 -37
- package/{parsers-BXKoUKMa.d.ts → parsers-CngKnVjW.d.ts} +65 -37
package/index.mjs
CHANGED
|
@@ -2140,10 +2140,34 @@ var init_field_catalog = __esm({
|
|
|
2140
2140
|
}
|
|
2141
2141
|
});
|
|
2142
2142
|
|
|
2143
|
+
// src/acl/populate-target-configuration.ts
|
|
2144
|
+
import { isPlainObject as isPlainObject12 } from "@web-ts-toolkit/utils";
|
|
2145
|
+
function capturePopulateTargetConfiguration(runtime, modelName, source, proposedOptions) {
|
|
2146
|
+
const label = `Populate target "${modelName}" for ${source}`;
|
|
2147
|
+
if (!proposedOptions && !runtime.hasModel(modelName))
|
|
2148
|
+
authorizationError(
|
|
2149
|
+
`${label} must be configured first in this runtime; register its Mongoose model and setModelOptions with an object permissionSchema`
|
|
2150
|
+
);
|
|
2151
|
+
const model = runtime.getModelInstance(modelName);
|
|
2152
|
+
if (!model) authorizationError(`${label} requires an actual registered Mongoose model in this runtime`);
|
|
2153
|
+
const options = proposedOptions ?? runtime.getModelOptions(modelName);
|
|
2154
|
+
const permissionSchema = options.permissionSchema === void 0 ? runtime.getDefaultModelOption("permissionSchema") : options.permissionSchema;
|
|
2155
|
+
if (!isPlainObject12(permissionSchema))
|
|
2156
|
+
authorizationError(
|
|
2157
|
+
`${label} requires an effective object permissionSchema; configure the model or runtime model defaults ({} explicitly closes business fields)`
|
|
2158
|
+
);
|
|
2159
|
+
return { model, options: { ...options, permissionSchema } };
|
|
2160
|
+
}
|
|
2161
|
+
var init_populate_target_configuration = __esm({
|
|
2162
|
+
"src/acl/populate-target-configuration.ts"() {
|
|
2163
|
+
init_authorization_snapshot();
|
|
2164
|
+
}
|
|
2165
|
+
});
|
|
2166
|
+
|
|
2143
2167
|
// src/acl/virtual-populate-dependencies.ts
|
|
2144
|
-
import { get as get4, isPlainObject as
|
|
2168
|
+
import { get as get4, isPlainObject as isPlainObject13 } from "@web-ts-toolkit/utils";
|
|
2145
2169
|
function record(value, label) {
|
|
2146
|
-
if (!
|
|
2170
|
+
if (!isPlainObject13(value)) return fail(label, "entries must be plain { path, select? } objects");
|
|
2147
2171
|
for (const key of Reflect.ownKeys(value)) {
|
|
2148
2172
|
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
2149
2173
|
if (typeof key !== "string" || !property.enumerable || !("value" in property))
|
|
@@ -2232,7 +2256,8 @@ function normalizeSelection(select, catalog, label) {
|
|
|
2232
2256
|
};
|
|
2233
2257
|
}
|
|
2234
2258
|
function captureVirtualPopulateDependencies(runtime, modelName, options, globalOptions = runtime.getGlobalOptions()) {
|
|
2235
|
-
const
|
|
2259
|
+
const requireOperationAccessForPopulateDependencies = globalOptions.requireOperationAccessForPopulateDependencies !== false;
|
|
2260
|
+
const requireOperationAccessForPopulateQueries = globalOptions.requireOperationAccessForPopulateQueries !== false;
|
|
2236
2261
|
const model = runtime.getModelInstance(modelName);
|
|
2237
2262
|
const targets = /* @__PURE__ */ new Map();
|
|
2238
2263
|
const scopes = [];
|
|
@@ -2272,7 +2297,12 @@ function captureVirtualPopulateDependencies(runtime, modelName, options, globalO
|
|
|
2272
2297
|
label,
|
|
2273
2298
|
`target "${ref}" requires a registered same-connection non-discriminator ObjectId model bound to the source ref`
|
|
2274
2299
|
);
|
|
2275
|
-
const
|
|
2300
|
+
const { options: targetOptions } = capturePopulateTargetConfiguration(
|
|
2301
|
+
runtime,
|
|
2302
|
+
ref,
|
|
2303
|
+
`dependsOnPopulate virtual "${label}"`,
|
|
2304
|
+
ref === modelName ? options : void 0
|
|
2305
|
+
);
|
|
2276
2306
|
const defaults = runtime.getDefaultModelOptions();
|
|
2277
2307
|
const readOption = (key) => resolveNestedOption((path2, fallback) => get4(targetOptions, path2, fallback), key, get4(defaults, key));
|
|
2278
2308
|
const documentPermissionField = targetOptions.documentPermissionField ?? defaults.documentPermissionField;
|
|
@@ -2281,7 +2311,9 @@ function captureVirtualPopulateDependencies(runtime, modelName, options, globalO
|
|
|
2281
2311
|
{
|
|
2282
2312
|
catalog,
|
|
2283
2313
|
documentPermissionField,
|
|
2284
|
-
|
|
2314
|
+
requireOperationAccessForPopulateDependencies,
|
|
2315
|
+
requireOperationAccessForPopulateQueries,
|
|
2316
|
+
permissionSchema: targetOptions.permissionSchema,
|
|
2285
2317
|
operationAccess: { read: readOption("operationAccess.read") },
|
|
2286
2318
|
baseFilter: { read: readOption("baseFilter.read") },
|
|
2287
2319
|
overrideFilter: { read: readOption("overrideFilter.read") },
|
|
@@ -2330,13 +2362,13 @@ function captureVirtualPopulateDependencies(runtime, modelName, options, globalO
|
|
|
2330
2362
|
);
|
|
2331
2363
|
continue;
|
|
2332
2364
|
}
|
|
2333
|
-
if (!
|
|
2365
|
+
if (!isPlainObject13(raw)) continue;
|
|
2334
2366
|
const entry = raw;
|
|
2335
2367
|
const label = join(sourcePrefix, name);
|
|
2336
2368
|
captureList(entry.dependsOnPopulate, label);
|
|
2337
2369
|
if (!("get" in entry))
|
|
2338
2370
|
for (const access of accesses) {
|
|
2339
|
-
if (
|
|
2371
|
+
if (isPlainObject13(entry[access]))
|
|
2340
2372
|
captureList(entry[access].dependsOnPopulate, `${label}.${access}`);
|
|
2341
2373
|
}
|
|
2342
2374
|
const byAccess = {};
|
|
@@ -2394,6 +2426,7 @@ var init_virtual_populate_dependencies = __esm({
|
|
|
2394
2426
|
init_authorization_snapshot();
|
|
2395
2427
|
init_field_catalog();
|
|
2396
2428
|
init_virtual_descriptor();
|
|
2429
|
+
init_populate_target_configuration();
|
|
2397
2430
|
elementType = (type) => type.$embeddedSchemaType ?? type.embeddedSchemaType ?? type.caster;
|
|
2398
2431
|
join = (prefix, path) => prefix ? `${prefix}.${path}` : path;
|
|
2399
2432
|
within = (path, parent) => path === parent || path.startsWith(`${parent}.`);
|
|
@@ -2418,7 +2451,7 @@ import {
|
|
|
2418
2451
|
isBoolean,
|
|
2419
2452
|
isFunction as isFunction2,
|
|
2420
2453
|
isNil as isNil2,
|
|
2421
|
-
isPlainObject as
|
|
2454
|
+
isPlainObject as isPlainObject14,
|
|
2422
2455
|
isString as isString4,
|
|
2423
2456
|
keys,
|
|
2424
2457
|
normalizeUrlPath,
|
|
@@ -2485,7 +2518,7 @@ var init_runtime = __esm({
|
|
|
2485
2518
|
const modelPermissionKeys = {};
|
|
2486
2519
|
forEach2(schemaKeys, (schemaKey) => {
|
|
2487
2520
|
const schemaRule = permissionSchema[schemaKey];
|
|
2488
|
-
const ruleEntries =
|
|
2521
|
+
const ruleEntries = isPlainObject14(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
|
|
2489
2522
|
for (let x = 0; x < ruleEntries.length; x++) {
|
|
2490
2523
|
const [accessKey, value] = ruleEntries[x];
|
|
2491
2524
|
if (!isArray5(globalPermissionKeys[accessKey])) {
|
|
@@ -2526,7 +2559,7 @@ var init_runtime = __esm({
|
|
|
2526
2559
|
VIRTUAL_RESERVED_NAMES = /* @__PURE__ */ new Set(["_id", "__v", "id"]);
|
|
2527
2560
|
VIRTUAL_DANGEROUS_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
2528
2561
|
VIRTUAL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
2529
|
-
isVirtualContainerValue2 = (value) =>
|
|
2562
|
+
isVirtualContainerValue2 = (value) => isPlainObject14(value) && "sub" in value;
|
|
2530
2563
|
virtualFullPath = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
2531
2564
|
virtualPathsOverlap = (virtualPath, permissionField) => {
|
|
2532
2565
|
if (!virtualPath || !permissionField) return false;
|
|
@@ -2621,7 +2654,7 @@ var init_runtime = __esm({
|
|
|
2621
2654
|
};
|
|
2622
2655
|
validateVirtualLeafValue = (value, entryLabel, storedFields, siblingVirtualNames, sharedDependsOn) => {
|
|
2623
2656
|
if (isFunction2(value)) return;
|
|
2624
|
-
if (!
|
|
2657
|
+
if (!isPlainObject14(value)) {
|
|
2625
2658
|
throw new Error(`Virtual ${entryLabel}: descriptor must be a getter function or { get, dependsOn }`);
|
|
2626
2659
|
}
|
|
2627
2660
|
const record3 = value;
|
|
@@ -2667,7 +2700,7 @@ var init_runtime = __esm({
|
|
|
2667
2700
|
`Virtual ${scopeLabel}${name}: scope-container must only hold "sub" (embedded container "${name}" is not a virtual-leaf collision)`
|
|
2668
2701
|
);
|
|
2669
2702
|
}
|
|
2670
|
-
if (!
|
|
2703
|
+
if (!isPlainObject14(record4.sub)) {
|
|
2671
2704
|
throw new Error(`Virtual ${scopeLabel}${name}: scope-container "sub" must be a plain object`);
|
|
2672
2705
|
}
|
|
2673
2706
|
continue;
|
|
@@ -2678,7 +2711,7 @@ var init_runtime = __esm({
|
|
|
2678
2711
|
);
|
|
2679
2712
|
}
|
|
2680
2713
|
if (isFunction2(raw)) continue;
|
|
2681
|
-
if (!
|
|
2714
|
+
if (!isPlainObject14(raw)) {
|
|
2682
2715
|
throw new Error(
|
|
2683
2716
|
`Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record)`
|
|
2684
2717
|
);
|
|
@@ -2732,7 +2765,7 @@ var init_runtime = __esm({
|
|
|
2732
2765
|
};
|
|
2733
2766
|
validateVirtualsTree = (model, virtuals, documentPermissionField, modelName) => {
|
|
2734
2767
|
if (virtuals === void 0 || virtuals === null) return;
|
|
2735
|
-
if (!
|
|
2768
|
+
if (!isPlainObject14(virtuals)) {
|
|
2736
2769
|
throw new Error(`Virtuals for model "${modelName}": virtuals must be a plain object mapping names to descriptors`);
|
|
2737
2770
|
}
|
|
2738
2771
|
const rootSchema = model?.schema ?? null;
|
|
@@ -2784,8 +2817,8 @@ var init_runtime = __esm({
|
|
|
2784
2817
|
{
|
|
2785
2818
|
requestPermissionField: "_permissions",
|
|
2786
2819
|
globalPermissions: () => ({}),
|
|
2787
|
-
|
|
2788
|
-
|
|
2820
|
+
requireOperationAccessForPopulateQueries: true,
|
|
2821
|
+
requireOperationAccessForPopulateDependencies: true,
|
|
2789
2822
|
logger: defaultLogger,
|
|
2790
2823
|
requestComplexity: defaultRequestComplexity
|
|
2791
2824
|
},
|
|
@@ -2959,13 +2992,15 @@ var init_runtime = __esm({
|
|
|
2959
2992
|
/** Shallowly replace supplied global values; nested objects such as requestComplexity are not deep-merged. */
|
|
2960
2993
|
setGlobalOptions(options) {
|
|
2961
2994
|
const next = { ...options };
|
|
2962
|
-
if ("
|
|
2963
|
-
next.
|
|
2995
|
+
if ("requireOperationAccessForPopulateQueries" in next)
|
|
2996
|
+
next.requireOperationAccessForPopulateQueries = next.requireOperationAccessForPopulateQueries !== false;
|
|
2997
|
+
if ("requireOperationAccessForPopulateDependencies" in next)
|
|
2998
|
+
next.requireOperationAccessForPopulateDependencies = next.requireOperationAccessForPopulateDependencies !== false;
|
|
2964
2999
|
this.globalOptions.assign(next);
|
|
2965
3000
|
}
|
|
2966
3001
|
setGlobalOption(key, value) {
|
|
2967
|
-
if (key === "
|
|
2968
|
-
this.globalOptions.set(
|
|
3002
|
+
if (key === "requireOperationAccessForPopulateQueries" || key === "requireOperationAccessForPopulateDependencies") {
|
|
3003
|
+
this.globalOptions.set(key, value !== false);
|
|
2969
3004
|
return;
|
|
2970
3005
|
}
|
|
2971
3006
|
this.globalOptions.set(key, value);
|
|
@@ -3088,10 +3123,10 @@ var init_runtime = __esm({
|
|
|
3088
3123
|
const segments = nestedPath.split(".");
|
|
3089
3124
|
let cursor = cloned;
|
|
3090
3125
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
3091
|
-
if (!
|
|
3126
|
+
if (!isPlainObject14(cursor)) break;
|
|
3092
3127
|
cursor = cursor[segments[i]];
|
|
3093
3128
|
}
|
|
3094
|
-
if (
|
|
3129
|
+
if (isPlainObject14(cursor)) {
|
|
3095
3130
|
delete cursor[segments[segments.length - 1]];
|
|
3096
3131
|
}
|
|
3097
3132
|
proposedVirtuals = cloned;
|
|
@@ -3499,6 +3534,47 @@ function serviceAuthorizationInput(policy, targets) {
|
|
|
3499
3534
|
}
|
|
3500
3535
|
return { rows: policy.rows, outputFields: policy.outputFields, related };
|
|
3501
3536
|
}
|
|
3537
|
+
function hasStableConfigurationCopies(value) {
|
|
3538
|
+
if (value === null || typeof value !== "object") return true;
|
|
3539
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return false;
|
|
3540
|
+
return Object.values(value).every(hasStableConfigurationCopies);
|
|
3541
|
+
}
|
|
3542
|
+
function captureAuthorizationAccess(value) {
|
|
3543
|
+
const input = authorizationRecord(value, "access");
|
|
3544
|
+
const access = {};
|
|
3545
|
+
for (const key of ["operation", "rowAccess", "virtualAccess", "outputAccess", "docPermissionsAccess"]) {
|
|
3546
|
+
if (key === "rowAccess" && input[key] === void 0) continue;
|
|
3547
|
+
if (typeof input[key] !== "string") authorizationError(`invalid access.${key}`);
|
|
3548
|
+
access[key] = input[key];
|
|
3549
|
+
}
|
|
3550
|
+
return Object.freeze(access);
|
|
3551
|
+
}
|
|
3552
|
+
function captureAuthorizationCatalog(value) {
|
|
3553
|
+
const input = authorizationRecord(value, "catalog");
|
|
3554
|
+
const catalog = {};
|
|
3555
|
+
for (const key of [
|
|
3556
|
+
"paths",
|
|
3557
|
+
"persistedPaths",
|
|
3558
|
+
"arrayPaths",
|
|
3559
|
+
"schemaVirtualPaths",
|
|
3560
|
+
"metadataPaths",
|
|
3561
|
+
"dynamicPaths",
|
|
3562
|
+
"containerPaths",
|
|
3563
|
+
"virtualPaths",
|
|
3564
|
+
"virtualScopePaths",
|
|
3565
|
+
"opaquePaths",
|
|
3566
|
+
"referencePaths",
|
|
3567
|
+
"protectedPaths"
|
|
3568
|
+
]) {
|
|
3569
|
+
const paths = input[key];
|
|
3570
|
+
if (key !== "paths" && paths === void 0) continue;
|
|
3571
|
+
if (!Array.isArray(paths) || Array.from(paths).some((path) => typeof path !== "string")) {
|
|
3572
|
+
authorizationError(`invalid catalog.${key}`);
|
|
3573
|
+
}
|
|
3574
|
+
catalog[key] = paths;
|
|
3575
|
+
}
|
|
3576
|
+
return Object.freeze(catalog);
|
|
3577
|
+
}
|
|
3502
3578
|
function assertAuthorizationPath(path) {
|
|
3503
3579
|
if (typeof path !== "string" || !isSafeOutputPath(path)) {
|
|
3504
3580
|
authorizationError(`invalid response path: ${String(path)}`);
|
|
@@ -3583,7 +3659,6 @@ function normalizePolicy(value, applicability, configuration, targets) {
|
|
|
3583
3659
|
const matches = targets.filter((target2) => target2.path === path);
|
|
3584
3660
|
if (matches.length !== 1) authorizationError(`unused or ambiguous related path: ${path}`);
|
|
3585
3661
|
const target = matches[0];
|
|
3586
|
-
if (target.kind === "passthrough") authorizationError(`related policy requires a registered target: ${path}`);
|
|
3587
3662
|
const child = authorizationRecord(entry, `related.${path}`);
|
|
3588
3663
|
assertAuthorizationKeys(
|
|
3589
3664
|
child,
|
|
@@ -3629,6 +3704,9 @@ function resolveServiceAuthorization(input) {
|
|
|
3629
3704
|
const raw = copyAuthorizationValue(input.authorization);
|
|
3630
3705
|
const configuration = copyAuthorizationValue(input.configuration, true);
|
|
3631
3706
|
const targets = copyAuthorizationValue(input.relatedTargets ?? [], true);
|
|
3707
|
+
const access = copyAuthorizationValue(input.access);
|
|
3708
|
+
const outputAccess = captureAuthorizationAccess(access);
|
|
3709
|
+
const catalog = captureAuthorizationCatalog(configuration.catalog);
|
|
3632
3710
|
const policy = copyAuthorizationValue(normalizePolicy(raw, input.applicability ?? "service", configuration, targets));
|
|
3633
3711
|
const scopePath = Array.from(scope(input.binding));
|
|
3634
3712
|
for (const part of scopePath) {
|
|
@@ -3641,17 +3719,21 @@ function resolveServiceAuthorization(input) {
|
|
|
3641
3719
|
authorizationError("derived call request/runtime mismatch");
|
|
3642
3720
|
}
|
|
3643
3721
|
}
|
|
3644
|
-
const permissions =
|
|
3645
|
-
input.requestPermissions.keys.map((key) => [key, input.requestPermissions.has(key)])
|
|
3722
|
+
const permissions = new permission_default(
|
|
3723
|
+
Object.fromEntries(input.requestPermissions.keys.map((key) => [key, input.requestPermissions.has(key) === true]))
|
|
3646
3724
|
);
|
|
3725
|
+
for (const value of Object.values(permissions)) if (value && typeof value === "object") Object.freeze(value);
|
|
3726
|
+
Object.freeze(permissions);
|
|
3647
3727
|
const call = Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
3648
3728
|
calls.set(call, {
|
|
3649
3729
|
binding: Object.freeze({ ...input.binding, scopePath: Object.freeze(scopePath) }),
|
|
3650
|
-
access
|
|
3730
|
+
access,
|
|
3651
3731
|
configuration,
|
|
3652
3732
|
policy,
|
|
3653
3733
|
relatedTargets: targets,
|
|
3654
|
-
|
|
3734
|
+
// normalizeFields constructs only mode strings and validated string paths.
|
|
3735
|
+
output: Object.freeze({ access: outputAccess, catalog, outputFields: policy.outputFields }),
|
|
3736
|
+
permissions,
|
|
3655
3737
|
provenance: input.parent ? callRecord(input.parent).provenance : call
|
|
3656
3738
|
});
|
|
3657
3739
|
return call;
|
|
@@ -3669,12 +3751,75 @@ function readServiceAuthorization(call, binding) {
|
|
|
3669
3751
|
true
|
|
3670
3752
|
);
|
|
3671
3753
|
}
|
|
3754
|
+
function readServiceAuthorizationAccess(call, binding) {
|
|
3755
|
+
assertAuthorizationBinding(call, binding);
|
|
3756
|
+
return callRecord(call).output.access;
|
|
3757
|
+
}
|
|
3758
|
+
function readServiceAuthorizationHydration(call, binding) {
|
|
3759
|
+
assertAuthorizationBinding(call, binding);
|
|
3760
|
+
return copyAuthorizationValue({ hydration: callRecord(call).configuration.hydration }, true);
|
|
3761
|
+
}
|
|
3762
|
+
function outputMetadata(configuration) {
|
|
3763
|
+
return {
|
|
3764
|
+
documentPermissionField: configuration.documentPermissionField,
|
|
3765
|
+
exposedDocPermissionKeys: configuration.exposedDocPermissionKeys,
|
|
3766
|
+
stripPermissionsField: configuration.stripPermissionsField,
|
|
3767
|
+
disableFieldPermissions: configuration.disableFieldPermissions
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3770
|
+
function readServiceOutputMetadata(call, binding) {
|
|
3771
|
+
assertAuthorizationBinding(call, binding);
|
|
3772
|
+
return copyAuthorizationValue(outputMetadata(callRecord(call).configuration), true);
|
|
3773
|
+
}
|
|
3774
|
+
function readServiceDocumentPermissions(call, binding) {
|
|
3775
|
+
assertAuthorizationBinding(call, binding);
|
|
3776
|
+
const record3 = callRecord(call);
|
|
3777
|
+
return Object.freeze({
|
|
3778
|
+
catalog: record3.output.catalog,
|
|
3779
|
+
...copyAuthorizationValue({ docPermissions: record3.configuration.docPermissions }, true)
|
|
3780
|
+
});
|
|
3781
|
+
}
|
|
3782
|
+
function readServiceFinalizerConfiguration(call, binding) {
|
|
3783
|
+
assertAuthorizationBinding(call, binding);
|
|
3784
|
+
const record3 = callRecord(call);
|
|
3785
|
+
return Object.freeze({
|
|
3786
|
+
catalog: record3.output.catalog,
|
|
3787
|
+
...copyAuthorizationValue(
|
|
3788
|
+
{
|
|
3789
|
+
...outputMetadata(record3.configuration),
|
|
3790
|
+
virtuals: record3.configuration.virtuals,
|
|
3791
|
+
docPermissions: record3.configuration.docPermissions
|
|
3792
|
+
},
|
|
3793
|
+
true
|
|
3794
|
+
)
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3797
|
+
function readServiceOutputPolicy(call, binding) {
|
|
3798
|
+
assertAuthorizationBinding(call, binding);
|
|
3799
|
+
return callRecord(call).output;
|
|
3800
|
+
}
|
|
3801
|
+
function readServiceOutputFieldAuthorization(call, binding) {
|
|
3802
|
+
assertAuthorizationBinding(call, binding);
|
|
3803
|
+
const record3 = callRecord(call);
|
|
3804
|
+
const reusable = record3.reusableFieldConfiguration ??= hasStableConfigurationCopies(record3.configuration.permissionSchema) && // Coercion methods can observe an object's identity even without native
|
|
3805
|
+
// leaves. Keep those receivers local to the original resolution boundary.
|
|
3806
|
+
(record3.configuration.modelPermissionPrefix === null || typeof record3.configuration.modelPermissionPrefix !== "object");
|
|
3807
|
+
return Object.freeze({
|
|
3808
|
+
access: record3.output.access,
|
|
3809
|
+
outputFields: record3.output.outputFields,
|
|
3810
|
+
reusable,
|
|
3811
|
+
...copyAuthorizationValue(
|
|
3812
|
+
{
|
|
3813
|
+
permissionSchema: record3.configuration.permissionSchema,
|
|
3814
|
+
modelPermissionPrefix: record3.configuration.modelPermissionPrefix
|
|
3815
|
+
},
|
|
3816
|
+
true
|
|
3817
|
+
)
|
|
3818
|
+
});
|
|
3819
|
+
}
|
|
3672
3820
|
function getAuthorizationPermissions(call, binding) {
|
|
3673
3821
|
assertAuthorizationBinding(call, binding);
|
|
3674
|
-
|
|
3675
|
-
for (const value of Object.values(permission)) if (value && typeof value === "object") Object.freeze(value);
|
|
3676
|
-
Object.freeze(permission);
|
|
3677
|
-
return permission;
|
|
3822
|
+
return callRecord(call).permissions;
|
|
3678
3823
|
}
|
|
3679
3824
|
function getAuthorizationProvenance(call) {
|
|
3680
3825
|
return callRecord(call).provenance;
|
|
@@ -3714,7 +3859,7 @@ function normalizeOutputAuthorizationOptions(input, options, purpose) {
|
|
|
3714
3859
|
const stage = stageRecord(documentRecord(handle).stage);
|
|
3715
3860
|
const binding = Object.freeze({ ...input.binding, scopePath: stage.binding.scopePath });
|
|
3716
3861
|
assertAuthorizationBinding(stage.call, binding);
|
|
3717
|
-
const access =
|
|
3862
|
+
const access = readServiceAuthorizationAccess(stage.call, binding);
|
|
3718
3863
|
if (input.access.outputAccess !== access.outputAccess) authorizationError("document access mismatch");
|
|
3719
3864
|
return Object.freeze({ kind: "document", documentAuthorization: handle, binding });
|
|
3720
3865
|
}
|
|
@@ -3792,7 +3937,16 @@ function readDocumentAuthorization(handle, binding) {
|
|
|
3792
3937
|
authorization: stage.call,
|
|
3793
3938
|
stage: record3.stage,
|
|
3794
3939
|
grants: copyAuthorizationValue(record3.grants),
|
|
3795
|
-
...
|
|
3940
|
+
...readServiceAuthorizationAccess(stage.call, binding)
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
function readDocumentAuthorizationAccess(handle, binding) {
|
|
3944
|
+
const record3 = documentRecord(handle);
|
|
3945
|
+
const stage = stageRecord(record3.stage);
|
|
3946
|
+
return Object.freeze({
|
|
3947
|
+
authorization: stage.call,
|
|
3948
|
+
stage: record3.stage,
|
|
3949
|
+
...readServiceAuthorizationAccess(stage.call, binding)
|
|
3796
3950
|
});
|
|
3797
3951
|
}
|
|
3798
3952
|
function memoizeDocumentFieldDecision(handle, binding, field, evaluate) {
|
|
@@ -3800,15 +3954,23 @@ function memoizeDocumentFieldDecision(handle, binding, field, evaluate) {
|
|
|
3800
3954
|
const stage = stageRecord(record3.stage);
|
|
3801
3955
|
assertAuthorizationBinding(stage.call, binding);
|
|
3802
3956
|
let decision = record3.decisions.get(field);
|
|
3803
|
-
if (
|
|
3957
|
+
if (decision) {
|
|
3958
|
+
return decision.then((result2) => {
|
|
3959
|
+
stageRecord(record3.stage);
|
|
3960
|
+
assertAuthorizationBinding(stage.call, binding);
|
|
3961
|
+
return result2;
|
|
3962
|
+
});
|
|
3963
|
+
} else {
|
|
3804
3964
|
decision = Promise.resolve().then(async () => {
|
|
3805
3965
|
stageRecord(record3.stage);
|
|
3966
|
+
assertAuthorizationBinding(stage.call, binding);
|
|
3806
3967
|
const result2 = await evaluate.call(
|
|
3807
3968
|
stage.binding.request,
|
|
3808
3969
|
getAuthorizationPermissions(stage.call, binding),
|
|
3809
3970
|
copyAuthorizationValue(record3.grants)
|
|
3810
3971
|
);
|
|
3811
3972
|
stageRecord(record3.stage);
|
|
3973
|
+
assertAuthorizationBinding(stage.call, binding);
|
|
3812
3974
|
if (typeof result2 !== "boolean") authorizationError("field decision must be boolean");
|
|
3813
3975
|
return result2;
|
|
3814
3976
|
});
|
|
@@ -3825,7 +3987,7 @@ function assertDocumentAuthorization(handle, use) {
|
|
|
3825
3987
|
const record3 = documentRecord(handle);
|
|
3826
3988
|
const stage = stageRecord(record3.stage);
|
|
3827
3989
|
assertAuthorizationBinding(stage.call, use.binding);
|
|
3828
|
-
if (use.authorization !== void 0 && stage.call !== use.authorization || use.stage !== void 0 && record3.stage !== use.stage ||
|
|
3990
|
+
if (use.authorization !== void 0 && stage.call !== use.authorization || use.stage !== void 0 && record3.stage !== use.stage || readServiceAuthorizationAccess(stage.call, use.binding).outputAccess !== use.outputAccess) {
|
|
3829
3991
|
authorizationError("document call/access/stage mismatch");
|
|
3830
3992
|
}
|
|
3831
3993
|
const association = outputs.get(use.output);
|
|
@@ -3880,7 +4042,7 @@ function transferDocumentAuthorizationGraph(source, copy, copyOrigins) {
|
|
|
3880
4042
|
{
|
|
3881
4043
|
binding: stage.binding,
|
|
3882
4044
|
output: original,
|
|
3883
|
-
outputAccess:
|
|
4045
|
+
outputAccess: readServiceAuthorizationAccess(stage.call, stage.binding).outputAccess,
|
|
3884
4046
|
authorization: stage.call,
|
|
3885
4047
|
stage: documentRecord(association.handle).stage
|
|
3886
4048
|
},
|
|
@@ -4044,12 +4206,20 @@ function classifyRequestFieldRule(rule, permissions, modelPermissionPrefix = "")
|
|
|
4044
4206
|
);
|
|
4045
4207
|
return (typeof rule === "string" ? possible.stringHandler(rule) : possible.arrayHandler(rule)) ? "deferred" : "denied";
|
|
4046
4208
|
}
|
|
4209
|
+
function prepareRequestFieldRule(rule, permissions, modelPermissionPrefix = "") {
|
|
4210
|
+
const state = classifyRequestFieldRule(rule, permissions, modelPermissionPrefix);
|
|
4211
|
+
const invariant = state === "denied" && modelPermissionPrefix ? classifyRequestFieldRule(rule, permissions) : state;
|
|
4212
|
+
return Object.freeze({ state, decision: invariant === "deferred" ? void 0 : invariant === "granted" });
|
|
4213
|
+
}
|
|
4214
|
+
function combineFieldAuthorizationStates(states2) {
|
|
4215
|
+
return states2.includes("denied") ? "denied" : states2.includes("deferred") ? "deferred" : "granted";
|
|
4216
|
+
}
|
|
4047
4217
|
function classifyEffectiveFieldAuthorization(args) {
|
|
4048
4218
|
if (hasExplicitFieldGrant(args.outputFields, args.path)) return "granted";
|
|
4049
4219
|
const states2 = args.rules.map(
|
|
4050
4220
|
(rule) => classifyRequestFieldRule(rule, args.globalPermissions, args.modelPermissionPrefix)
|
|
4051
4221
|
);
|
|
4052
|
-
return
|
|
4222
|
+
return combineFieldAuthorizationStates(states2);
|
|
4053
4223
|
}
|
|
4054
4224
|
async function evaluateScopedAccessRule(args) {
|
|
4055
4225
|
const { req, rule, globalPermissions, docPermissions, modelPermissionPrefix } = args;
|
|
@@ -4093,7 +4263,7 @@ var init_field_authorization = __esm({
|
|
|
4093
4263
|
});
|
|
4094
4264
|
|
|
4095
4265
|
// src/acl/virtual-projection.ts
|
|
4096
|
-
import { get as get6, isArray as isArray7, isPlainObject as
|
|
4266
|
+
import { get as get6, isArray as isArray7, isPlainObject as isPlainObject16 } from "@web-ts-toolkit/utils";
|
|
4097
4267
|
function planVirtualProjection(args) {
|
|
4098
4268
|
if (args.snapshot.catalog) return planCatalogProjection(args);
|
|
4099
4269
|
const {
|
|
@@ -4327,15 +4497,15 @@ var init_virtual_projection = __esm({
|
|
|
4327
4497
|
init_virtual_populate_dependencies();
|
|
4328
4498
|
init_virtual_descriptor();
|
|
4329
4499
|
getScopedPermissionSchema = (root, scopePath) => {
|
|
4330
|
-
if (!root || !
|
|
4500
|
+
if (!root || !isPlainObject16(root)) return null;
|
|
4331
4501
|
if (scopePath.length === 0) return root;
|
|
4332
4502
|
const scoped = get6(root, scopePath.join("."));
|
|
4333
|
-
return
|
|
4503
|
+
return isPlainObject16(scoped) ? scoped : null;
|
|
4334
4504
|
};
|
|
4335
4505
|
resolveAlwaysSelectForAccess = (raw, access) => {
|
|
4336
4506
|
if (!raw) return [];
|
|
4337
4507
|
if (isArray7(raw)) return [...raw];
|
|
4338
|
-
if (
|
|
4508
|
+
if (isPlainObject16(raw)) {
|
|
4339
4509
|
const record3 = raw;
|
|
4340
4510
|
const exact = record3[access];
|
|
4341
4511
|
if (isArray7(exact)) return [...exact];
|
|
@@ -4622,9 +4792,9 @@ function captureModelAuthorizationConfiguration(runtime, modelName, options = ru
|
|
|
4622
4792
|
options,
|
|
4623
4793
|
globalOptions
|
|
4624
4794
|
),
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
permissionSchema: options.permissionSchema,
|
|
4795
|
+
requireOperationAccessForPopulateDependencies: globalOptions.requireOperationAccessForPopulateDependencies !== false,
|
|
4796
|
+
requireOperationAccessForPopulateQueries: globalOptions.requireOperationAccessForPopulateQueries !== false,
|
|
4797
|
+
permissionSchema: options.permissionSchema === void 0 ? runtime.getDefaultModelOption("permissionSchema") : options.permissionSchema,
|
|
4628
4798
|
virtuals,
|
|
4629
4799
|
alwaysSelectFields: options.alwaysSelectFields,
|
|
4630
4800
|
modelPermissionPrefix: options.modelPermissionPrefix,
|
|
@@ -4690,15 +4860,15 @@ import {
|
|
|
4690
4860
|
isBoolean as isBoolean2,
|
|
4691
4861
|
isEmpty as isEmpty2,
|
|
4692
4862
|
isFunction as isFunction3,
|
|
4693
|
-
isPlainObject as
|
|
4863
|
+
isPlainObject as isPlainObject17,
|
|
4694
4864
|
isString as isString5
|
|
4695
4865
|
} from "@web-ts-toolkit/utils";
|
|
4696
4866
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
4697
4867
|
function isAndFilter(filter2) {
|
|
4698
|
-
return !!filter2 &&
|
|
4868
|
+
return !!filter2 && isPlainObject17(filter2) && Object.keys(filter2).length === 1 && isArray8(filter2.$and);
|
|
4699
4869
|
}
|
|
4700
4870
|
function isMergeableClause(filter2) {
|
|
4701
|
-
return !!filter2 &&
|
|
4871
|
+
return !!filter2 && isPlainObject17(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
|
|
4702
4872
|
}
|
|
4703
4873
|
function optimizeAndFilter(clausesInput) {
|
|
4704
4874
|
const clauses = [];
|
|
@@ -4719,7 +4889,7 @@ function optimizeAndFilter(clausesInput) {
|
|
|
4719
4889
|
const entries = Array.from(value, dedupeKey);
|
|
4720
4890
|
return entries.every((entry) => entry !== void 0) ? `array:${JSON.stringify(entries)}` : void 0;
|
|
4721
4891
|
}
|
|
4722
|
-
if (
|
|
4892
|
+
if (isPlainObject17(value)) {
|
|
4723
4893
|
const entries = Object.keys(value).sort().map((key) => [key, dedupeKey(value[key])]);
|
|
4724
4894
|
return entries.every(([, entry]) => entry !== void 0) ? `object:${JSON.stringify(entries)}` : void 0;
|
|
4725
4895
|
}
|
|
@@ -4767,7 +4937,7 @@ function optimizeAndFilter(clausesInput) {
|
|
|
4767
4937
|
}
|
|
4768
4938
|
function normalizeFilter(filter2) {
|
|
4769
4939
|
if (filter2 === false) return false;
|
|
4770
|
-
if (!filter2 || !
|
|
4940
|
+
if (!filter2 || !isPlainObject17(filter2) || isEmpty2(filter2)) return null;
|
|
4771
4941
|
if (!isAndFilter(filter2)) {
|
|
4772
4942
|
return filter2;
|
|
4773
4943
|
}
|
|
@@ -4845,7 +5015,7 @@ async function setRequestPermissions(req) {
|
|
|
4845
5015
|
if (hasFieldValue && owner === void 0) return;
|
|
4846
5016
|
const globalPermissions = getGlobalOption("globalPermissions");
|
|
4847
5017
|
const permissions = isFunction3(globalPermissions) ? await globalPermissions.call(req, req) : void 0;
|
|
4848
|
-
if (
|
|
5018
|
+
if (isPlainObject17(permissions)) req[requestPermissionField] = permissions;
|
|
4849
5019
|
else if (isArray8(permissions)) req[requestPermissionField] = arrayToRecord(permissions);
|
|
4850
5020
|
else if (isString5(permissions)) req[requestPermissionField] = { [permissions]: true };
|
|
4851
5021
|
else req[requestPermissionField] = {};
|
|
@@ -5244,7 +5414,7 @@ var init_filter_resolution = __esm({
|
|
|
5244
5414
|
// src/acl/virtual-populate-execution.ts
|
|
5245
5415
|
import { Document as Document4, mongo as mongo4 } from "mongoose";
|
|
5246
5416
|
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
5247
|
-
import { isPlainObject as
|
|
5417
|
+
import { isPlainObject as isPlainObject20 } from "@web-ts-toolkit/utils";
|
|
5248
5418
|
function projectVirtualPopulateView(source, selection, structurePaths = []) {
|
|
5249
5419
|
const root = { children: /* @__PURE__ */ new Map() };
|
|
5250
5420
|
for (const path of compactPersistedProjection(selection)) {
|
|
@@ -5276,8 +5446,8 @@ function equivalentVirtualPopulatePredicate(left, right) {
|
|
|
5276
5446
|
}
|
|
5277
5447
|
if (Array.isArray(left) || Array.isArray(right))
|
|
5278
5448
|
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((entry, index) => equivalentVirtualPopulatePredicate(entry, right[index]));
|
|
5279
|
-
if (
|
|
5280
|
-
if (!
|
|
5449
|
+
if (isPlainObject20(left) || isPlainObject20(right)) {
|
|
5450
|
+
if (!isPlainObject20(left) || !isPlainObject20(right)) return false;
|
|
5281
5451
|
const a = Object.keys(left), b = Object.keys(right);
|
|
5282
5452
|
return a.length === b.length && a.every(
|
|
5283
5453
|
(key, index) => key === b[index] && equivalentVirtualPopulatePredicate(
|
|
@@ -5348,7 +5518,6 @@ function captureVirtualPopulateRows(batch, inputs, plan, publicPopulate = [], so
|
|
|
5348
5518
|
address,
|
|
5349
5519
|
plan: currentPlan,
|
|
5350
5520
|
refs: /* @__PURE__ */ new Map(),
|
|
5351
|
-
referenceTargets: /* @__PURE__ */ new Map(),
|
|
5352
5521
|
dependencies: /* @__PURE__ */ new Map()
|
|
5353
5522
|
};
|
|
5354
5523
|
row.occurrences.set(addressKey(address), occurrence);
|
|
@@ -5356,7 +5525,6 @@ function captureVirtualPopulateRows(batch, inputs, plan, publicPopulate = [], so
|
|
|
5356
5525
|
const dependencies = (candidate.populateDependencies ?? []).map((dependency) => {
|
|
5357
5526
|
if (!occurrence.refs.has(dependency.path))
|
|
5358
5527
|
occurrence.refs.set(dependency.path, originalRef(value, dependency.path));
|
|
5359
|
-
occurrence.referenceTargets.set(dependency.path, dependency.target);
|
|
5360
5528
|
const request = {
|
|
5361
5529
|
binding: dependency.target,
|
|
5362
5530
|
target: dependency.target,
|
|
@@ -5454,7 +5622,7 @@ async function execute(state) {
|
|
|
5454
5622
|
if (!pending) {
|
|
5455
5623
|
pending = (async () => {
|
|
5456
5624
|
try {
|
|
5457
|
-
if (target.configuration.
|
|
5625
|
+
if (target.configuration.requireOperationAccessForPopulateDependencies !== false && !await targetReadAllowed(state, target))
|
|
5458
5626
|
return void 0;
|
|
5459
5627
|
const parts = await work.hookGate.run(
|
|
5460
5628
|
() => resolveAccessFilterParts({
|
|
@@ -5540,12 +5708,6 @@ function readVirtualPopulateOriginalReference(row, address, path) {
|
|
|
5540
5708
|
function hasVirtualPopulatePublicReference(row, address, path) {
|
|
5541
5709
|
return !!rows.get(row)?.publicRequests.some((request) => addressKey(request.address) === addressKey([...address, path]));
|
|
5542
5710
|
}
|
|
5543
|
-
function isVirtualPopulateReferenceVisible(row, address, path) {
|
|
5544
|
-
const record3 = rows.get(row);
|
|
5545
|
-
const occurrence = record3?.occurrences.get(addressKey(address));
|
|
5546
|
-
const target = occurrence?.refs.get(path)?.present ? occurrence.referenceTargets.get(path) : void 0;
|
|
5547
|
-
return record3 && target ? targetReadAllowed(record3.batch, target) : Promise.resolve(true);
|
|
5548
|
-
}
|
|
5549
5711
|
function readVirtualPopulateInputs(row, address, virtualName) {
|
|
5550
5712
|
const record3 = rows.get(row);
|
|
5551
5713
|
if (!record3?.batch.completed) return { available: false };
|
|
@@ -5688,7 +5850,7 @@ var init_virtual_populate_execution = __esm({
|
|
|
5688
5850
|
publicViewIdentities = /* @__PURE__ */ new WeakMap();
|
|
5689
5851
|
familyRawFetches = /* @__PURE__ */ new WeakMap();
|
|
5690
5852
|
addressKey = (address) => JSON.stringify(address);
|
|
5691
|
-
storage = (value) => value instanceof Document4 ? value._doc :
|
|
5853
|
+
storage = (value) => value instanceof Document4 ? value._doc : isPlainObject20(value) ? value : void 0;
|
|
5692
5854
|
originalRef = (source, key) => {
|
|
5693
5855
|
const record3 = storage(source);
|
|
5694
5856
|
return {
|
|
@@ -5701,7 +5863,7 @@ var init_virtual_populate_execution = __esm({
|
|
|
5701
5863
|
const option = getCapturedAuthorizationOption(configuration, `${key}.${access}`);
|
|
5702
5864
|
return key === "operationAccess" || typeof option === "function" ? option : void 0;
|
|
5703
5865
|
});
|
|
5704
|
-
compatible = (a, b) => a.binding.runtime === b.binding.runtime && a.binding.model === b.binding.model && a.binding.schema === b.binding.schema && a.access === b.access && a.requestRead === b.requestRead && a.binding.configuration.
|
|
5866
|
+
compatible = (a, b) => a.binding.runtime === b.binding.runtime && a.binding.model === b.binding.model && a.binding.schema === b.binding.schema && a.access === b.access && a.requestRead === b.requestRead && a.binding.configuration.requireOperationAccessForPopulateDependencies === b.binding.configuration.requireOperationAccessForPopulateDependencies && // Different captures may share only an equivalent terminal persisted binding/policy.
|
|
5705
5867
|
equivalentVirtualPopulatePredicate(
|
|
5706
5868
|
a.binding.configuration.catalog.persistedPaths,
|
|
5707
5869
|
b.binding.configuration.catalog.persistedPaths
|
|
@@ -5715,21 +5877,116 @@ var init_virtual_populate_execution = __esm({
|
|
|
5715
5877
|
});
|
|
5716
5878
|
|
|
5717
5879
|
// src/acl/output-field-filter.ts
|
|
5718
|
-
import { isPlainObject as
|
|
5880
|
+
import { isPlainObject as isPlainObject21 } from "@web-ts-toolkit/utils";
|
|
5881
|
+
function callFieldPolicy(authorization, binding) {
|
|
5882
|
+
assertAuthorizationBinding(authorization, binding);
|
|
5883
|
+
let policy = fieldPolicies.get(authorization);
|
|
5884
|
+
if (!policy) {
|
|
5885
|
+
const view = readServiceOutputFieldAuthorization(authorization, binding);
|
|
5886
|
+
policy = {
|
|
5887
|
+
schema: view.permissionSchema,
|
|
5888
|
+
requestPaths: view.permissionSchema == null || isPlainObject21(view.permissionSchema) && Object.isFrozen(view.permissionSchema) ? Object.freeze(Object.keys(view.permissionSchema ?? {})) : void 0,
|
|
5889
|
+
prefix: view.modelPermissionPrefix ?? "",
|
|
5890
|
+
outputFields: view.outputFields,
|
|
5891
|
+
accesses: /* @__PURE__ */ new Map()
|
|
5892
|
+
};
|
|
5893
|
+
if (view.reusable) fieldPolicies.set(authorization, policy);
|
|
5894
|
+
}
|
|
5895
|
+
return policy;
|
|
5896
|
+
}
|
|
5897
|
+
function requestRuleClassification(entry, authorization, permissions, prefix) {
|
|
5898
|
+
if (typeof prefix !== "string") return void 0;
|
|
5899
|
+
let classification = entry.classifications.get(authorization);
|
|
5900
|
+
if (!classification) {
|
|
5901
|
+
classification = prepareRequestFieldRule(entry.rule, permissions, prefix);
|
|
5902
|
+
entry.classifications.set(authorization, classification);
|
|
5903
|
+
}
|
|
5904
|
+
return classification;
|
|
5905
|
+
}
|
|
5906
|
+
function invariantRuleLookup(schema, path) {
|
|
5907
|
+
if (schema == null) return true;
|
|
5908
|
+
if (!isPlainObject21(schema) || !Object.isFrozen(schema)) return false;
|
|
5909
|
+
const exact = Object.getOwnPropertyDescriptor(schema, path);
|
|
5910
|
+
if (exact) {
|
|
5911
|
+
const raw2 = exact.value;
|
|
5912
|
+
return !raw2 || typeof raw2 !== "object" || Array.isArray(raw2) || isPlainObject21(raw2) && Object.isFrozen(raw2);
|
|
5913
|
+
}
|
|
5914
|
+
const [head, ...rest] = path.split(".");
|
|
5915
|
+
const raw = Object.getOwnPropertyDescriptor(schema, head)?.value;
|
|
5916
|
+
if (raw == null) return true;
|
|
5917
|
+
if (!isPlainObject21(raw) || !Object.isFrozen(raw)) return false;
|
|
5918
|
+
const sub = Object.getOwnPropertyDescriptor(raw, "sub");
|
|
5919
|
+
return !rest.length || !sub || invariantRuleLookup(sub.value, rest.join("."));
|
|
5920
|
+
}
|
|
5921
|
+
function prepareOutputPath(policy, authorization, binding, path, access) {
|
|
5922
|
+
let cache = policy.accesses.get(access);
|
|
5923
|
+
if (!cache) {
|
|
5924
|
+
cache = /* @__PURE__ */ new Map();
|
|
5925
|
+
policy.accesses.set(access, cache);
|
|
5926
|
+
}
|
|
5927
|
+
let prepared = cache.get(path);
|
|
5928
|
+
if (!prepared) {
|
|
5929
|
+
const invariant = invariantRuleLookup(policy.schema, path);
|
|
5930
|
+
const permissions = getAuthorizationPermissions(authorization, binding);
|
|
5931
|
+
const entries = extractOutputFieldRuleEntries(policy.schema, path, access).map((entry) => ({
|
|
5932
|
+
...entry,
|
|
5933
|
+
classifications: /* @__PURE__ */ new WeakMap()
|
|
5934
|
+
}));
|
|
5935
|
+
const classifications = entries.map(
|
|
5936
|
+
(entry) => requestRuleClassification(entry, authorization, permissions, policy.prefix)
|
|
5937
|
+
);
|
|
5938
|
+
const backendGrant = hasExplicitFieldGrant(policy.outputFields, path);
|
|
5939
|
+
prepared = {
|
|
5940
|
+
entries,
|
|
5941
|
+
backendGrant,
|
|
5942
|
+
state: backendGrant ? "granted" : classifications.every((value) => value !== void 0) ? combineFieldAuthorizationStates(classifications.map((value) => value.state)) : void 0
|
|
5943
|
+
};
|
|
5944
|
+
if (invariant) cache.set(path, prepared);
|
|
5945
|
+
}
|
|
5946
|
+
return prepared;
|
|
5947
|
+
}
|
|
5948
|
+
function assertOwningOutputDocuments(handle) {
|
|
5949
|
+
const owner = owningDocuments.get(handle);
|
|
5950
|
+
if (owner) {
|
|
5951
|
+
readDocumentAuthorizationAccess(owner.handle, owner.binding);
|
|
5952
|
+
assertOwningOutputDocuments(owner.handle);
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
function prepareOutputDocumentAuthorization(authorization, binding, configuration) {
|
|
5956
|
+
const access = readServiceAuthorizationAccess(authorization, binding);
|
|
5957
|
+
const key = `docPermissions.${access.docPermissionsAccess}`;
|
|
5958
|
+
const docPermissions = configuration.docPermissions;
|
|
5959
|
+
const invariant = /^[A-Za-z0-9_-]+$/.test(access.docPermissionsAccess) && (docPermissions == null || isPlainObject21(docPermissions));
|
|
5960
|
+
const option = invariant ? getCapturedAuthorizationOption(configuration, key) : void 0;
|
|
5961
|
+
const hook = typeof option === "function" ? option : void 0;
|
|
5962
|
+
const resolveHook = invariant ? () => hook : () => {
|
|
5963
|
+
const slice = { catalog: configuration.catalog, ...copyAuthorizationValue({ docPermissions }, true) };
|
|
5964
|
+
const option2 = getCapturedAuthorizationOption(slice, key);
|
|
5965
|
+
return typeof option2 === "function" ? option2 : void 0;
|
|
5966
|
+
};
|
|
5967
|
+
return async (args) => {
|
|
5968
|
+
if (args.authorization !== authorization) authorizationError("document resolver call mismatch");
|
|
5969
|
+
assertAuthorizationBinding(authorization, args.binding);
|
|
5970
|
+
return resolveOutputDocumentAuthorizationWithHook(args, access, resolveHook);
|
|
5971
|
+
};
|
|
5972
|
+
}
|
|
5719
5973
|
async function resolveOutputDocumentAuthorization(args) {
|
|
5974
|
+
const configuration = readServiceDocumentPermissions(args.authorization, args.binding);
|
|
5975
|
+
return prepareOutputDocumentAuthorization(args.authorization, args.binding, configuration)(args);
|
|
5976
|
+
}
|
|
5977
|
+
async function resolveOutputDocumentAuthorizationWithHook(args, access, resolveHook) {
|
|
5720
5978
|
const { authorization, binding } = args;
|
|
5721
|
-
const { configuration, access } = readServiceAuthorization(authorization, binding);
|
|
5722
5979
|
const stage = args.stage ?? createDocumentAuthorizationStage(authorization, binding, args.snapshot);
|
|
5723
5980
|
if (args.stage) assertDocumentAuthorizationStage(stage, authorization, binding, args.snapshot);
|
|
5724
5981
|
const handle = await resolveDocumentAuthorization(stage, async function(doc, permissions) {
|
|
5725
5982
|
if (args.owningDocument) return readDocumentAuthorization(args.owningDocument, args.owningBinding).grants;
|
|
5726
5983
|
if (args.knownOutput) return {};
|
|
5727
|
-
const hook =
|
|
5728
|
-
if (
|
|
5984
|
+
const hook = resolveHook();
|
|
5985
|
+
if (!hook) return {};
|
|
5729
5986
|
try {
|
|
5730
5987
|
const run = () => hook.call(this, doc, permissions, args.context);
|
|
5731
5988
|
const grants = await getRequestWorkState(binding.request, binding.runtime).hookGate.run(run);
|
|
5732
|
-
return
|
|
5989
|
+
return isPlainObject21(grants) ? grants : {};
|
|
5733
5990
|
} catch (error2) {
|
|
5734
5991
|
warn("docPermissions hook failed; applying empty document permissions", {
|
|
5735
5992
|
modelName: binding.modelName,
|
|
@@ -5746,6 +6003,7 @@ async function resolveOutputDocumentAuthorization(args) {
|
|
|
5746
6003
|
return handle;
|
|
5747
6004
|
}
|
|
5748
6005
|
function evaluateRequestRule(handle, binding, entry, prefix, access) {
|
|
6006
|
+
assertOwningOutputDocuments(handle);
|
|
5749
6007
|
const owner = owningDocuments.get(handle);
|
|
5750
6008
|
if (owner && !entry.path.startsWith(`${(binding.scopePath ?? []).join(".")}.`)) {
|
|
5751
6009
|
return evaluateRequestRule(owner.handle, owner.binding, entry, prefix, access);
|
|
@@ -5754,65 +6012,78 @@ function evaluateRequestRule(handle, binding, entry, prefix, access) {
|
|
|
5754
6012
|
handle,
|
|
5755
6013
|
binding,
|
|
5756
6014
|
`request:${access}:${entry.path}`,
|
|
5757
|
-
function(permissions, grants) {
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
6015
|
+
async function(permissions, grants) {
|
|
6016
|
+
const { authorization } = readDocumentAuthorizationAccess(handle, binding);
|
|
6017
|
+
const classification = requestRuleClassification(entry, authorization, permissions, prefix);
|
|
6018
|
+
let allowed = classification?.decision;
|
|
6019
|
+
if (allowed === void 0) {
|
|
6020
|
+
allowed = await evaluateScopedAccessRule({
|
|
6021
|
+
req: this,
|
|
6022
|
+
rule: entry.rule,
|
|
6023
|
+
globalPermissions: permissions,
|
|
6024
|
+
docPermissions: grants,
|
|
6025
|
+
modelPermissionPrefix: prefix
|
|
6026
|
+
});
|
|
6027
|
+
}
|
|
6028
|
+
assertOwningOutputDocuments(handle);
|
|
6029
|
+
return allowed;
|
|
5765
6030
|
}
|
|
5766
6031
|
);
|
|
5767
6032
|
}
|
|
5768
6033
|
function evaluateAuthorizedOutputField(handle, binding, field) {
|
|
5769
|
-
const { authorization } =
|
|
5770
|
-
|
|
6034
|
+
const { authorization } = readDocumentAuthorizationAccess(handle, binding);
|
|
6035
|
+
assertOwningOutputDocuments(handle);
|
|
5771
6036
|
const path = [...binding.scopePath ?? [], field].join(".");
|
|
5772
|
-
const
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
const args = {
|
|
6037
|
+
const decision = memoizeDocumentFieldDecision(handle, binding, `output:${path}`, async function(permissions) {
|
|
6038
|
+
const policy = callFieldPolicy(authorization, binding);
|
|
6039
|
+
const { outputAccess } = readDocumentAuthorizationAccess(handle, binding);
|
|
6040
|
+
const prepared = prepareOutputPath(policy, authorization, binding, path, outputAccess);
|
|
6041
|
+
const unresolved = unresolvedDocuments.has(handle);
|
|
6042
|
+
const state = unresolved ? prepared.state ?? classifyEffectiveFieldAuthorization({
|
|
5779
6043
|
path,
|
|
5780
|
-
rules: entries.map((entry) => entry.rule),
|
|
6044
|
+
rules: prepared.entries.map((entry) => entry.rule),
|
|
6045
|
+
modelPermissionPrefix: policy.prefix,
|
|
5781
6046
|
outputFields: policy.outputFields,
|
|
5782
|
-
globalPermissions: permissions
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
if (
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
6047
|
+
globalPermissions: permissions
|
|
6048
|
+
}) : void 0;
|
|
6049
|
+
let allowed;
|
|
6050
|
+
if (unresolved && state !== "granted") allowed = false;
|
|
6051
|
+
else if (prepared.backendGrant) allowed = true;
|
|
6052
|
+
else {
|
|
6053
|
+
allowed = true;
|
|
6054
|
+
for (const entry of prepared.entries) {
|
|
6055
|
+
if (!await evaluateRequestRule(handle, binding, entry, policy.prefix, outputAccess)) {
|
|
6056
|
+
allowed = false;
|
|
6057
|
+
break;
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
assertOwningOutputDocuments(handle);
|
|
6062
|
+
return allowed;
|
|
5792
6063
|
});
|
|
6064
|
+
return owningDocuments.has(handle) ? decision.then((allowed) => {
|
|
6065
|
+
assertOwningOutputDocuments(handle);
|
|
6066
|
+
return allowed;
|
|
6067
|
+
}) : decision;
|
|
5793
6068
|
}
|
|
5794
6069
|
async function requestOutputFieldPaths(handle, binding, access) {
|
|
5795
|
-
const { authorization } =
|
|
5796
|
-
|
|
5797
|
-
const
|
|
6070
|
+
const { authorization } = readDocumentAuthorizationAccess(handle, binding);
|
|
6071
|
+
assertOwningOutputDocuments(handle);
|
|
6072
|
+
const policy = callFieldPolicy(authorization, binding);
|
|
5798
6073
|
const fields = [];
|
|
5799
|
-
for (const path of Object.keys(schema ?? {})) {
|
|
5800
|
-
const entries =
|
|
6074
|
+
for (const path of policy.requestPaths ?? Object.keys(policy.schema ?? {})) {
|
|
6075
|
+
const { entries } = prepareOutputPath(policy, authorization, binding, path, access);
|
|
5801
6076
|
let allowed = true;
|
|
5802
6077
|
for (const entry of entries) {
|
|
5803
|
-
if (!await evaluateRequestRule(
|
|
5804
|
-
handle,
|
|
5805
|
-
binding,
|
|
5806
|
-
entry,
|
|
5807
|
-
configuration.modelPermissionPrefix ?? "",
|
|
5808
|
-
access
|
|
5809
|
-
)) {
|
|
6078
|
+
if (!await evaluateRequestRule(handle, binding, entry, policy.prefix, access)) {
|
|
5810
6079
|
allowed = false;
|
|
5811
6080
|
break;
|
|
5812
6081
|
}
|
|
5813
6082
|
}
|
|
5814
6083
|
if (allowed && entries.length) fields.push(path);
|
|
5815
6084
|
}
|
|
6085
|
+
assertOwningOutputDocuments(handle);
|
|
6086
|
+
readDocumentAuthorizationAccess(handle, binding);
|
|
5816
6087
|
return fields;
|
|
5817
6088
|
}
|
|
5818
6089
|
function authorizeEmbeddedOutputPlan(parent, child) {
|
|
@@ -5833,24 +6104,24 @@ function authorizeEmbeddedOutputPlan(parent, child) {
|
|
|
5833
6104
|
function hasWholeOutputValueGrant(plan, field) {
|
|
5834
6105
|
if (plan.fieldStates?.[field] !== "granted") return false;
|
|
5835
6106
|
const binding = plan.authorizationBinding;
|
|
5836
|
-
const {
|
|
5837
|
-
return hasExplicitFieldGrant(
|
|
6107
|
+
const { outputFields } = readServiceOutputPolicy(plan.authorization, binding);
|
|
6108
|
+
return hasExplicitFieldGrant(outputFields, [...binding.scopePath ?? [], field].join("."));
|
|
5838
6109
|
}
|
|
5839
6110
|
function copyOutputValue(value, plan, path) {
|
|
5840
|
-
const {
|
|
5841
|
-
const virtualValue =
|
|
5842
|
-
const opaque =
|
|
6111
|
+
const { catalog } = readServiceOutputPolicy(plan.authorization, plan.authorizationBinding);
|
|
6112
|
+
const virtualValue = catalog.virtualPaths?.some((p) => matchesCatalogPattern(p, path));
|
|
6113
|
+
const opaque = catalog.opaquePaths?.some((p) => matchesCatalogPattern(p, path));
|
|
5843
6114
|
const excluded = plan.effectiveSelectNorm.filter((token) => token.startsWith("-")).map((token) => [...plan.authorizationBinding.scopePath ?? [], token.slice(1)].join("."));
|
|
5844
6115
|
const visit = (input, current) => {
|
|
5845
6116
|
if (Array.isArray(input)) return input.map((entry) => visit(entry, current));
|
|
5846
6117
|
if (input instanceof Map) return visit(Object.fromEntries(input), current);
|
|
5847
|
-
if (!
|
|
6118
|
+
if (!isPlainObject21(input)) return deepIsolateValue(input);
|
|
5848
6119
|
const output = {};
|
|
5849
6120
|
for (const [key, entry] of Object.entries(input)) {
|
|
5850
6121
|
const child = `${current}.${key}`;
|
|
5851
6122
|
if (excluded.some((entry2) => child === entry2 || child.startsWith(`${entry2}.`))) continue;
|
|
5852
6123
|
const safeValueKey = !["__proto__", "constructor", "prototype", "_doc", "$__", "__v"].includes(key);
|
|
5853
|
-
if (!(opaque ? safeValueKey : isSafeOutputPath(key)) ||
|
|
6124
|
+
if (!(opaque ? safeValueKey : isSafeOutputPath(key)) || catalog.protectedPaths?.some((p) => matchesCatalogPattern(p, child, true)) || !opaque && !virtualValue && !isCatalogOutputPath(catalog, child))
|
|
5854
6125
|
continue;
|
|
5855
6126
|
output[key] = visit(entry, child);
|
|
5856
6127
|
}
|
|
@@ -5860,25 +6131,21 @@ function copyOutputValue(value, plan, path) {
|
|
|
5860
6131
|
}
|
|
5861
6132
|
function copyAuthorizedReference(value, plan, path) {
|
|
5862
6133
|
if (Array.isArray(value)) return value.map((entry) => copyAuthorizedReference(entry, plan, path));
|
|
5863
|
-
if (!
|
|
6134
|
+
if (!isPlainObject21(value)) return deepIsolateValue(value);
|
|
5864
6135
|
const handle = getAssociatedDocumentAuthorization(value, plan.authorization);
|
|
5865
6136
|
if (handle) {
|
|
5866
6137
|
const copy = deepIsolateValue(value);
|
|
5867
6138
|
transferDocumentAuthorizationGraph(value, copy);
|
|
5868
6139
|
return copy;
|
|
5869
6140
|
}
|
|
5870
|
-
const { relatedTargets } = readServiceAuthorization(plan.authorization, plan.authorizationBinding);
|
|
5871
|
-
if (relatedTargets.some((target) => target.path === path && target.kind === "passthrough")) {
|
|
5872
|
-
return deepIsolateValue(value);
|
|
5873
|
-
}
|
|
5874
6141
|
return Object.prototype.hasOwnProperty.call(value, "_id") ? deepIsolateValue(value._id) : void 0;
|
|
5875
6142
|
}
|
|
5876
6143
|
function retainOutputMetadata(output, source, configuration, scope2) {
|
|
5877
6144
|
if (scope2.length || configuration.stripPermissionsField) return;
|
|
5878
6145
|
const parts = (configuration.documentPermissionField ?? "_permissions").split(".");
|
|
5879
6146
|
let value = source;
|
|
5880
|
-
for (const part of parts) value =
|
|
5881
|
-
if (!
|
|
6147
|
+
for (const part of parts) value = isPlainObject21(value) ? value[part] : void 0;
|
|
6148
|
+
if (!isPlainObject21(value)) return;
|
|
5882
6149
|
const keys2 = configuration.exposedDocPermissionKeys;
|
|
5883
6150
|
const metadata = Object.fromEntries(
|
|
5884
6151
|
Object.entries(value).filter(
|
|
@@ -5888,7 +6155,7 @@ function retainOutputMetadata(output, source, configuration, scope2) {
|
|
|
5888
6155
|
const copy = { ...output };
|
|
5889
6156
|
let target = copy;
|
|
5890
6157
|
for (const part of parts.slice(0, -1)) {
|
|
5891
|
-
target[part] =
|
|
6158
|
+
target[part] = isPlainObject21(target[part]) ? { ...target[part] } : {};
|
|
5892
6159
|
target = target[part];
|
|
5893
6160
|
}
|
|
5894
6161
|
target[parts[parts.length - 1]] = deepIsolateValue(metadata);
|
|
@@ -5930,11 +6197,11 @@ async function filterExistingOutput(source, plan, handle, associated) {
|
|
|
5930
6197
|
for (const entry of value2) values.push(await one(entry) ?? null);
|
|
5931
6198
|
return values;
|
|
5932
6199
|
}
|
|
5933
|
-
if (!
|
|
6200
|
+
if (!isPlainObject21(value2)) return value2 === null ? null : void 0;
|
|
5934
6201
|
const childHandle = associated ? getAssociatedDocumentAuthorization(value2) : void 0;
|
|
5935
6202
|
let child = scoped;
|
|
5936
6203
|
if (childHandle) {
|
|
5937
|
-
const state =
|
|
6204
|
+
const state = readDocumentAuthorizationAccess(childHandle, scoped.authorizationBinding);
|
|
5938
6205
|
if (getAuthorizationProvenance(state.authorization) !== getAuthorizationProvenance(plan.authorization))
|
|
5939
6206
|
authorizationError("child call mismatch");
|
|
5940
6207
|
child = helperPlan(state.authorization, scoped.authorizationBinding);
|
|
@@ -5998,7 +6265,7 @@ async function filterModelOutputForHelper(args) {
|
|
|
5998
6265
|
if (normalized.kind === "document") {
|
|
5999
6266
|
const { documentAuthorization: handle2, binding: binding2 } = normalized;
|
|
6000
6267
|
assertDocumentAuthorization(handle2, { binding: binding2, output: args.doc, outputAccess: access });
|
|
6001
|
-
const state =
|
|
6268
|
+
const state = readDocumentAuthorizationAccess(handle2, binding2);
|
|
6002
6269
|
return filterExistingOutput(
|
|
6003
6270
|
args.doc,
|
|
6004
6271
|
helperPlan(state.authorization, binding2),
|
|
@@ -6010,7 +6277,7 @@ async function filterModelOutputForHelper(args) {
|
|
|
6010
6277
|
const plan = helperPlan(normalized.authorization, binding);
|
|
6011
6278
|
if (args.doc == null) return { output: args.doc, fields: [] };
|
|
6012
6279
|
const source = isolateDocForOutput(args.doc);
|
|
6013
|
-
if (!
|
|
6280
|
+
if (!isPlainObject21(source)) return authorizationError("output helper requires one plain or hydrated document");
|
|
6014
6281
|
const knownOutput = !!args.doc && typeof args.doc === "object" && isKnownAuthorizationOutput(args.doc);
|
|
6015
6282
|
const handle = await resolveOutputDocumentAuthorization({
|
|
6016
6283
|
authorization: normalized.authorization,
|
|
@@ -6021,7 +6288,7 @@ async function filterModelOutputForHelper(args) {
|
|
|
6021
6288
|
});
|
|
6022
6289
|
return filterExistingOutput(source, plan, handle, false);
|
|
6023
6290
|
}
|
|
6024
|
-
var unresolvedDocuments, owningDocuments;
|
|
6291
|
+
var unresolvedDocuments, owningDocuments, fieldPolicies;
|
|
6025
6292
|
var init_output_field_filter = __esm({
|
|
6026
6293
|
"src/acl/output-field-filter.ts"() {
|
|
6027
6294
|
init_permissions_alias();
|
|
@@ -6037,6 +6304,7 @@ var init_output_field_filter = __esm({
|
|
|
6037
6304
|
init_output_projection();
|
|
6038
6305
|
unresolvedDocuments = /* @__PURE__ */ new WeakSet();
|
|
6039
6306
|
owningDocuments = /* @__PURE__ */ new WeakMap();
|
|
6307
|
+
fieldPolicies = /* @__PURE__ */ new WeakMap();
|
|
6040
6308
|
}
|
|
6041
6309
|
});
|
|
6042
6310
|
|
|
@@ -6105,28 +6373,54 @@ __export(finalize_model_output_exports, {
|
|
|
6105
6373
|
finalizeModelOutput: () => finalizeModelOutput,
|
|
6106
6374
|
finalizeModelOutputs: () => finalizeModelOutputs
|
|
6107
6375
|
});
|
|
6108
|
-
import { get as get9, isPlainObject as
|
|
6109
|
-
|
|
6110
|
-
const scopePath = args.scopePath;
|
|
6376
|
+
import { get as get9, isPlainObject as isPlainObject22, set as set7 } from "@web-ts-toolkit/utils";
|
|
6377
|
+
function assertFinalizerCall(args, prepared) {
|
|
6111
6378
|
const plan = args.plan;
|
|
6112
6379
|
const binding = plan.authorizationBinding;
|
|
6113
|
-
|
|
6114
|
-
if (plan.authorization &&
|
|
6380
|
+
if (plan.authorization !== prepared.authorization) authorizationError("finalizer call changed during pass");
|
|
6381
|
+
if (plan.authorization && binding) assertAuthorizationBinding(plan.authorization, binding);
|
|
6382
|
+
const access = prepared.policy?.access;
|
|
6383
|
+
if (plan.authorization && (!binding || binding.request !== args.request || binding.modelName !== args.receivingModelName || access?.virtualAccess !== args.virtualAccess || access.outputAccess !== args.outputAccess || access.docPermissionsAccess !== args.docPermissionsAccess || (binding.scopePath ?? []).join(".") !== args.scopePath.filter((p) => p !== "sub").join("."))) {
|
|
6115
6384
|
authorizationError("finalizer call/model/scope/access mismatch");
|
|
6116
6385
|
}
|
|
6117
|
-
|
|
6386
|
+
}
|
|
6387
|
+
function prepareFinalizer(args, sharedContext = false) {
|
|
6388
|
+
const { authorization, authorizationBinding: binding } = args.plan;
|
|
6389
|
+
const policy = authorization && binding ? readServiceOutputPolicy(authorization, binding) : void 0;
|
|
6390
|
+
assertFinalizerCall(args, { authorization, policy });
|
|
6391
|
+
const configuration = authorization ? readServiceFinalizerConfiguration(authorization, binding) : void 0;
|
|
6392
|
+
const snapshot = configuration ? configuration : deepIsolateValue(args.snapshot);
|
|
6393
|
+
return {
|
|
6394
|
+
authorization,
|
|
6395
|
+
policy,
|
|
6396
|
+
snapshot,
|
|
6397
|
+
baseContext: deepIsolateValue(args.context ?? {}),
|
|
6398
|
+
sharedContext,
|
|
6399
|
+
scopeVirtualNames: getScopeVirtualNames(snapshot, args.scopePath),
|
|
6400
|
+
scopedSchema: configuration ? null : getScopedPermissionSchema2(snapshot.permissionSchema, args.scopePath),
|
|
6401
|
+
permissions: authorization ? getAuthorizationPermissions(authorization, binding) : void 0,
|
|
6402
|
+
resolveDocument: authorization ? prepareOutputDocumentAuthorization(authorization, binding, configuration) : void 0
|
|
6403
|
+
};
|
|
6404
|
+
}
|
|
6405
|
+
async function finalizeSingleDoc(args, input, prepared) {
|
|
6406
|
+
assertFinalizerCall(args, prepared);
|
|
6407
|
+
const scopePath = args.scopePath;
|
|
6408
|
+
const plan = args.plan;
|
|
6409
|
+
const binding = plan.authorizationBinding;
|
|
6410
|
+
const captured = prepared.policy;
|
|
6411
|
+
const snapshot = prepared.snapshot;
|
|
6118
6412
|
const limit = args.resolvedLimit;
|
|
6119
6413
|
const gate = args.sharedGate;
|
|
6120
6414
|
const original = getIncludeOriginal(input);
|
|
6121
6415
|
const privateRow = args.virtualPopulateRow ?? (original && typeof original === "object" ? getVirtualPopulateRow(original) : void 0);
|
|
6122
6416
|
const privateAddress = args.virtualPopulateAddress ?? [];
|
|
6123
6417
|
const isolated = isolateDocForOutput(input);
|
|
6124
|
-
const output =
|
|
6125
|
-
if (captured &&
|
|
6418
|
+
const output = isPlainObject22(isolated) ? isolated : {};
|
|
6419
|
+
if (captured && isPlainObject22(input))
|
|
6126
6420
|
for (const key of Object.keys(output)) {
|
|
6127
6421
|
transferDocumentAuthorizationGraph(input[key], output[key]);
|
|
6128
6422
|
}
|
|
6129
|
-
const baseContext =
|
|
6423
|
+
const baseContext = prepared.sharedContext ? deepIsolateValue(prepared.baseContext) : prepared.baseContext;
|
|
6130
6424
|
const operation = captured?.access.operation ?? baseContext.operation ?? args.operation ?? args.virtualAccess;
|
|
6131
6425
|
const scopeContext = {
|
|
6132
6426
|
...baseContext,
|
|
@@ -6144,7 +6438,7 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6144
6438
|
if (captured) {
|
|
6145
6439
|
const projected = args.documentAuthorizationStage ? input : deepIsolateValue(output);
|
|
6146
6440
|
metadataSource = deepIsolateValue(output);
|
|
6147
|
-
documentAuthorization = await
|
|
6441
|
+
documentAuthorization = await prepared.resolveDocument({
|
|
6148
6442
|
authorization: plan.authorization,
|
|
6149
6443
|
binding,
|
|
6150
6444
|
snapshot: projected,
|
|
@@ -6157,13 +6451,10 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6157
6451
|
}
|
|
6158
6452
|
const docGrants = documentAuthorization ? readDocumentAuthorization(documentAuthorization, binding).grants : await resolveDocPermissions(args, output, scopeContext);
|
|
6159
6453
|
scopeContext.docPermissions = deepIsolateValue(docGrants);
|
|
6160
|
-
const globalPerms =
|
|
6161
|
-
const modelPermissionPrefix = snapshot.modelPermissionPrefix ?? "";
|
|
6162
|
-
const documentPermissionField = snapshot.documentPermissionField ?? "_permissions";
|
|
6163
|
-
const scopedSchema =
|
|
6164
|
-
snapshot.permissionSchema ?? null,
|
|
6165
|
-
scopePath
|
|
6166
|
-
);
|
|
6454
|
+
const globalPerms = prepared.permissions ?? resolveGlobalPermissions(args);
|
|
6455
|
+
const modelPermissionPrefix = captured ? "" : deepIsolateValue(snapshot.modelPermissionPrefix ?? "");
|
|
6456
|
+
const documentPermissionField = captured ? "_permissions" : deepIsolateValue(snapshot.documentPermissionField ?? "_permissions");
|
|
6457
|
+
const scopedSchema = deepIsolateValue(prepared.scopedSchema);
|
|
6167
6458
|
const childPlans = plan.childPlans ?? {};
|
|
6168
6459
|
for (const [containerName, childPlan] of Object.entries(childPlans)) {
|
|
6169
6460
|
if (!(containerName in output)) continue;
|
|
@@ -6177,17 +6468,18 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6177
6468
|
);
|
|
6178
6469
|
continue;
|
|
6179
6470
|
}
|
|
6180
|
-
if (captured && !
|
|
6471
|
+
if (captured && !isPlainObject22(rawContainer) && !Array.isArray(rawContainer)) {
|
|
6181
6472
|
delete output[containerName];
|
|
6182
6473
|
continue;
|
|
6183
6474
|
}
|
|
6184
6475
|
retainRawAssociation(output, containerName, rawContainer);
|
|
6185
|
-
const childSnapshot = snapshot;
|
|
6186
6476
|
const childBase = {
|
|
6187
6477
|
...args,
|
|
6188
6478
|
scopePath: [...childPlan.scopePath ?? [...scopePath, containerName, "sub"]],
|
|
6189
6479
|
plan: captured ? authorizeEmbeddedOutputPlan(plan, childPlan) : childPlan,
|
|
6190
|
-
|
|
6480
|
+
// Authenticated preparation reads its own authority. Do not clone an
|
|
6481
|
+
// otherwise unused public configuration graph on default traversal.
|
|
6482
|
+
snapshot: args.snapshot,
|
|
6191
6483
|
// Embedded reuses the owning parent's grants + accesses (VIRT-00A D2).
|
|
6192
6484
|
docPermissions: docGrants,
|
|
6193
6485
|
context: scopeContext,
|
|
@@ -6198,10 +6490,11 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6198
6490
|
related: [],
|
|
6199
6491
|
virtualPopulateRow: privateRow
|
|
6200
6492
|
};
|
|
6493
|
+
let childPreparation;
|
|
6201
6494
|
const finalizeOneChild = async (rawChild, address) => {
|
|
6202
6495
|
if (captured && Array.isArray(rawChild))
|
|
6203
6496
|
return mapWithConcurrencyLimit(rawChild, limit, (entry, index) => finalizeOneChild(entry, [...address, index]));
|
|
6204
|
-
if (!
|
|
6497
|
+
if (!isPlainObject22(rawChild) && !Array.isArray(rawChild)) return captured ? null : deepIsolateValue(rawChild);
|
|
6205
6498
|
if (args.traversal?.finalizeEmbeddedChildren) {
|
|
6206
6499
|
return args.traversal.finalizeEmbeddedChildren({
|
|
6207
6500
|
containerName,
|
|
@@ -6218,7 +6511,14 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6218
6511
|
} : { ...args, virtualPopulateRow: privateRow, virtualPopulateAddress: address }
|
|
6219
6512
|
});
|
|
6220
6513
|
}
|
|
6221
|
-
return finalizeSingleDoc(
|
|
6514
|
+
return finalizeSingleDoc(
|
|
6515
|
+
{ ...childBase, virtualPopulateAddress: address },
|
|
6516
|
+
rawChild,
|
|
6517
|
+
childPreparation ??= prepareFinalizer(
|
|
6518
|
+
childBase,
|
|
6519
|
+
Array.isArray(rawContainer) && (rawContainer.length > 1 || Array.isArray(rawContainer[0]))
|
|
6520
|
+
)
|
|
6521
|
+
);
|
|
6222
6522
|
};
|
|
6223
6523
|
if (Array.isArray(rawContainer)) {
|
|
6224
6524
|
const finalized = await mapWithConcurrencyLimit(
|
|
@@ -6227,7 +6527,7 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6227
6527
|
(entry, index) => finalizeOneChild(entry, [...privateAddress, containerName, index])
|
|
6228
6528
|
);
|
|
6229
6529
|
output[containerName] = finalized;
|
|
6230
|
-
} else if (
|
|
6530
|
+
} else if (isPlainObject22(rawContainer)) {
|
|
6231
6531
|
output[containerName] = await finalizeOneChild(rawContainer, [
|
|
6232
6532
|
...privateAddress,
|
|
6233
6533
|
containerName
|
|
@@ -6270,8 +6570,9 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6270
6570
|
context: scopeContext,
|
|
6271
6571
|
related: target.plan.relatedPlans?.length ? (args.related ?? []).filter((entry) => entry.path.startsWith(`${target.path}.`)) : []
|
|
6272
6572
|
};
|
|
6573
|
+
let relatedPreparation;
|
|
6273
6574
|
const finalizeOneRelated = async (rawChild, originalChild = rawChild) => {
|
|
6274
|
-
if (!
|
|
6575
|
+
if (!isPlainObject22(rawChild) && !Array.isArray(rawChild)) return rawChild;
|
|
6275
6576
|
if (typeof rawChild?._bsontype === "string") return rawChild;
|
|
6276
6577
|
const childOriginal = getIncludeOriginal(originalChild);
|
|
6277
6578
|
const childRow = childOriginal && typeof childOriginal === "object" ? getVirtualPopulateRow(childOriginal) : void 0;
|
|
@@ -6283,7 +6584,11 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6283
6584
|
parentArgs: { ...args, virtualPopulateRow: childRow, virtualPopulateAddress: [] }
|
|
6284
6585
|
});
|
|
6285
6586
|
}
|
|
6286
|
-
return finalizeSingleDoc(
|
|
6587
|
+
return finalizeSingleDoc(
|
|
6588
|
+
{ ...relatedBase, virtualPopulateRow: childRow },
|
|
6589
|
+
rawChild,
|
|
6590
|
+
relatedPreparation ??= prepareFinalizer(relatedBase, Array.isArray(rawRelated) && rawRelated.length > 1)
|
|
6591
|
+
);
|
|
6287
6592
|
};
|
|
6288
6593
|
let finalized;
|
|
6289
6594
|
if (Array.isArray(rawRelated)) {
|
|
@@ -6292,7 +6597,7 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6292
6597
|
limit,
|
|
6293
6598
|
(entry, index) => finalizeOneRelated(entry, Array.isArray(originalRelated) ? originalRelated[index] : void 0)
|
|
6294
6599
|
);
|
|
6295
|
-
} else if (
|
|
6600
|
+
} else if (isPlainObject22(rawRelated)) {
|
|
6296
6601
|
finalized = await finalizeOneRelated(rawRelated, originalRelated);
|
|
6297
6602
|
} else {
|
|
6298
6603
|
continue;
|
|
@@ -6303,18 +6608,24 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6303
6608
|
} catch {
|
|
6304
6609
|
}
|
|
6305
6610
|
}
|
|
6306
|
-
const
|
|
6307
|
-
for (const virtualName of scopeVirtualNames) {
|
|
6611
|
+
for (const virtualName of prepared.scopeVirtualNames) {
|
|
6308
6612
|
if (virtualName in output) delete output[virtualName];
|
|
6309
6613
|
}
|
|
6310
|
-
const stableBase = deepIsolateValue(output);
|
|
6311
|
-
const scopeLabel = scopePath.length > 0 ? scopePath.join(".") : "";
|
|
6312
6614
|
const candidates = plan.candidates ?? [];
|
|
6615
|
+
const needsGetterView = candidates.some((candidate) => {
|
|
6616
|
+
const descriptor = candidate.descriptor;
|
|
6617
|
+
if (typeof descriptor?.get !== "function") return false;
|
|
6618
|
+
if (!privateRow && descriptor.dependsOnPopulate?.length) return false;
|
|
6619
|
+
const dependsOn = Array.isArray(descriptor.dependsOn) ? descriptor.dependsOn : [];
|
|
6620
|
+
return dependsOn.every((dep) => typeof dep === "string" && !isAbsentDependency(output, dep));
|
|
6621
|
+
});
|
|
6622
|
+
let stableBase = needsGetterView ? deepIsolateValue(output) : void 0;
|
|
6623
|
+
const scopeLabel = scopePath.length > 0 ? scopePath.join(".") : "";
|
|
6313
6624
|
const staged = /* @__PURE__ */ new Map();
|
|
6314
6625
|
const runCandidate = async (candidate) => {
|
|
6315
6626
|
const dependsOn = Array.isArray(candidate.descriptor?.dependsOn) ? candidate.descriptor.dependsOn : [];
|
|
6316
6627
|
for (const dep of dependsOn) {
|
|
6317
|
-
if (typeof dep !== "string" || isAbsentDependency(stableBase, dep)) return;
|
|
6628
|
+
if (typeof dep !== "string" || isAbsentDependency(stableBase ?? output, dep)) return;
|
|
6318
6629
|
}
|
|
6319
6630
|
const allowed = documentAuthorization ? await evaluateAuthorizedOutputField(documentAuthorization, binding, candidate.name) : await evaluateEffectiveFieldAuthorization({
|
|
6320
6631
|
req: args.request,
|
|
@@ -6329,9 +6640,9 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6329
6640
|
if (typeof getter !== "function") return;
|
|
6330
6641
|
const privateInputs = privateRow ? readVirtualPopulateInputs(privateRow, privateAddress, candidate.name) : candidate.descriptor.dependsOnPopulate?.length ? { available: false } : { available: true, populated: {} };
|
|
6331
6642
|
if (!privateInputs.available) return;
|
|
6332
|
-
const isolatedView = deepIsolateValue(stableBase);
|
|
6643
|
+
const isolatedView = deepIsolateValue(stableBase ??= deepIsolateValue(output));
|
|
6333
6644
|
const virtualContext = {
|
|
6334
|
-
...scopeContext,
|
|
6645
|
+
...deepIsolateValue(scopeContext),
|
|
6335
6646
|
docPermissions: deepIsolateValue(docGrants)
|
|
6336
6647
|
};
|
|
6337
6648
|
const outcome = await runVirtualGetter({
|
|
@@ -6412,7 +6723,6 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6412
6723
|
if (!captured && documentPermissionField in output) keep.add(documentPermissionField);
|
|
6413
6724
|
const trimmed = {};
|
|
6414
6725
|
for (const key of keep) {
|
|
6415
|
-
if (privateRow && !await isVirtualPopulateReferenceVisible(privateRow, privateAddress, key)) continue;
|
|
6416
6726
|
if (privateRow && !hasVirtualPopulatePublicReference(privateRow, privateAddress, key)) {
|
|
6417
6727
|
const ref = readVirtualPopulateOriginalReference(privateRow, privateAddress, key);
|
|
6418
6728
|
if (ref) {
|
|
@@ -6423,12 +6733,25 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6423
6733
|
if (!(key in output)) continue;
|
|
6424
6734
|
if (captured && !plan.childPlans[key]) {
|
|
6425
6735
|
const path = [...binding.scopePath ?? [], key].join(".");
|
|
6426
|
-
trimmed[key] = captured.
|
|
6736
|
+
trimmed[key] = captured.catalog.referencePaths?.some((p) => matchesCatalogPattern(p, path)) ? copyAuthorizedReference(output[key], plan, path) : copyOutputValue(output[key], plan, path);
|
|
6427
6737
|
if (trimmed[key] === void 0) delete trimmed[key];
|
|
6428
6738
|
} else trimmed[key] = output[key];
|
|
6429
6739
|
}
|
|
6430
6740
|
if (documentAuthorization) {
|
|
6431
|
-
retainOutputMetadata(
|
|
6741
|
+
retainOutputMetadata(
|
|
6742
|
+
trimmed,
|
|
6743
|
+
metadataSource,
|
|
6744
|
+
copyAuthorizationValue(
|
|
6745
|
+
{
|
|
6746
|
+
documentPermissionField: snapshot.documentPermissionField,
|
|
6747
|
+
stripPermissionsField: snapshot.stripPermissionsField,
|
|
6748
|
+
exposedDocPermissionKeys: snapshot.exposedDocPermissionKeys,
|
|
6749
|
+
disableFieldPermissions: snapshot.disableFieldPermissions
|
|
6750
|
+
},
|
|
6751
|
+
true
|
|
6752
|
+
),
|
|
6753
|
+
binding.scopePath ?? []
|
|
6754
|
+
);
|
|
6432
6755
|
associateDocumentAuthorization(documentAuthorization, binding, trimmed);
|
|
6433
6756
|
args.onFinalized?.(
|
|
6434
6757
|
trimmed,
|
|
@@ -6437,7 +6760,7 @@ async function finalizeSingleDoc(args, input) {
|
|
|
6437
6760
|
output: trimmed,
|
|
6438
6761
|
outputAccess: args.outputAccess,
|
|
6439
6762
|
authorization: plan.authorization,
|
|
6440
|
-
stage:
|
|
6763
|
+
stage: readDocumentAuthorizationAccess(documentAuthorization, binding).stage
|
|
6441
6764
|
})
|
|
6442
6765
|
);
|
|
6443
6766
|
}
|
|
@@ -6458,7 +6781,7 @@ async function finalizeModelOutput(args) {
|
|
|
6458
6781
|
sharedGate: gate,
|
|
6459
6782
|
resolvedLimit: limit
|
|
6460
6783
|
};
|
|
6461
|
-
return finalizeSingleDoc(internal, args.input);
|
|
6784
|
+
return finalizeSingleDoc(internal, args.input, prepareFinalizer(internal));
|
|
6462
6785
|
}
|
|
6463
6786
|
async function finalizeModelOutputs(args) {
|
|
6464
6787
|
const gate = finalizerGate(args);
|
|
@@ -6467,16 +6790,24 @@ async function finalizeModelOutputs(args) {
|
|
|
6467
6790
|
if ("documentAuthorizationStage" in args || documentAuthorizationStages && (documentAuthorizationStages.length !== inputs.length || new Set(documentAuthorizationStages).size !== inputs.length)) {
|
|
6468
6791
|
authorizationError("list finalization requires a distinct document stage per row");
|
|
6469
6792
|
}
|
|
6793
|
+
if (!inputs.length) return [];
|
|
6794
|
+
const internal = {
|
|
6795
|
+
...rest,
|
|
6796
|
+
scopePath: [...rest.scopePath ?? rest.plan.scopePath ?? []],
|
|
6797
|
+
sharedGate: gate,
|
|
6798
|
+
resolvedLimit: limit
|
|
6799
|
+
};
|
|
6800
|
+
const prepared = prepareFinalizer(internal, inputs.length > 1);
|
|
6470
6801
|
return mapWithConcurrencyLimit(inputs, limit, (input, index) => {
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6802
|
+
return finalizeSingleDoc(
|
|
6803
|
+
{
|
|
6804
|
+
...internal,
|
|
6805
|
+
onFinalized: rest.onFinalized ? (output, context) => rest.onFinalized(output, context, index) : void 0,
|
|
6806
|
+
documentAuthorizationStage: documentAuthorizationStages?.[index]
|
|
6807
|
+
},
|
|
6808
|
+
input,
|
|
6809
|
+
prepared
|
|
6810
|
+
);
|
|
6480
6811
|
});
|
|
6481
6812
|
}
|
|
6482
6813
|
var isVirtualContainerValue3, getScopeVirtualsObject2, getScopeVirtualNames, getScopedPermissionSchema2, resolveGlobalPermissions, resolveDocPermissions, isAbsentDependency, __finalizeInternals;
|
|
@@ -6494,20 +6825,20 @@ var init_finalize_model_output = __esm({
|
|
|
6494
6825
|
init_field_catalog();
|
|
6495
6826
|
init_virtual_populate_execution();
|
|
6496
6827
|
init_include_output();
|
|
6497
|
-
isVirtualContainerValue3 = (value) =>
|
|
6828
|
+
isVirtualContainerValue3 = (value) => isPlainObject22(value) && "sub" in value;
|
|
6498
6829
|
getScopeVirtualsObject2 = (rootVirtuals, scopePath) => {
|
|
6499
|
-
if (!rootVirtuals || !
|
|
6830
|
+
if (!rootVirtuals || !isPlainObject22(rootVirtuals)) return null;
|
|
6500
6831
|
if (scopePath.length === 0) return rootVirtuals;
|
|
6501
6832
|
let current = rootVirtuals;
|
|
6502
6833
|
for (let i = 0; i < scopePath.length; i += 2) {
|
|
6503
6834
|
const field = scopePath[i];
|
|
6504
6835
|
const subMarker = scopePath[i + 1];
|
|
6505
6836
|
if (!field || subMarker !== "sub") return null;
|
|
6506
|
-
if (!
|
|
6837
|
+
if (!isPlainObject22(current)) return null;
|
|
6507
6838
|
const container = current[field];
|
|
6508
6839
|
if (!isVirtualContainerValue3(container)) return null;
|
|
6509
6840
|
current = container.sub;
|
|
6510
|
-
if (!
|
|
6841
|
+
if (!isPlainObject22(current)) return null;
|
|
6511
6842
|
}
|
|
6512
6843
|
return current;
|
|
6513
6844
|
};
|
|
@@ -6520,11 +6851,11 @@ var init_finalize_model_output = __esm({
|
|
|
6520
6851
|
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !isVirtualContainerValue3(v)).map(([k]) => k);
|
|
6521
6852
|
};
|
|
6522
6853
|
getScopedPermissionSchema2 = (root, scopePath) => {
|
|
6523
|
-
if (!root || !
|
|
6854
|
+
if (!root || !isPlainObject22(root)) return null;
|
|
6524
6855
|
if (scopePath.length === 0) return root;
|
|
6525
6856
|
try {
|
|
6526
6857
|
const scoped = get9(root, scopePath.join("."));
|
|
6527
|
-
return
|
|
6858
|
+
return isPlainObject22(scoped) ? scoped : null;
|
|
6528
6859
|
} catch {
|
|
6529
6860
|
return null;
|
|
6530
6861
|
}
|
|
@@ -6545,7 +6876,7 @@ var init_finalize_model_output = __esm({
|
|
|
6545
6876
|
};
|
|
6546
6877
|
resolveDocPermissions = async (args, isolatedForHook, scopeContext) => {
|
|
6547
6878
|
if (args.docPermissions != null && typeof args.docPermissions === "object" && !Array.isArray(args.docPermissions)) {
|
|
6548
|
-
const grants = args.docPermissions;
|
|
6879
|
+
const grants = deepIsolateValue(args.docPermissions);
|
|
6549
6880
|
scopeContext.docPermissions = grants;
|
|
6550
6881
|
return grants;
|
|
6551
6882
|
}
|
|
@@ -6583,7 +6914,7 @@ var init_finalize_model_output = __esm({
|
|
|
6583
6914
|
const grants = await args.sharedGate.run(
|
|
6584
6915
|
() => hook.call(args.request, isolatedForHook, globalPerms, scopeContext)
|
|
6585
6916
|
);
|
|
6586
|
-
const normalized =
|
|
6917
|
+
const normalized = isPlainObject22(grants) ? grants : {};
|
|
6587
6918
|
scopeContext.docPermissions = normalized;
|
|
6588
6919
|
return normalized;
|
|
6589
6920
|
}
|
|
@@ -6605,19 +6936,21 @@ var init_finalize_model_output = __esm({
|
|
|
6605
6936
|
});
|
|
6606
6937
|
|
|
6607
6938
|
// src/index.ts
|
|
6608
|
-
import { isPlainObject as
|
|
6939
|
+
import { isPlainObject as isPlainObject31, isString as isString10, isUndefined as isUndefined4 } from "@web-ts-toolkit/utils";
|
|
6609
6940
|
|
|
6610
6941
|
// src/middleware.ts
|
|
6611
6942
|
import JsonRouter5 from "@web-ts-toolkit/express-json-router";
|
|
6612
|
-
import { isArray as isArray13, isFunction as isFunction7, isPlainObject as
|
|
6943
|
+
import { isArray as isArray13, isFunction as isFunction7, isPlainObject as isPlainObject27, isString as isString7 } from "@web-ts-toolkit/utils";
|
|
6613
6944
|
|
|
6614
6945
|
// src/core.ts
|
|
6615
6946
|
init_public_admission();
|
|
6616
6947
|
init_options();
|
|
6948
|
+
init_permission();
|
|
6617
6949
|
import {
|
|
6618
6950
|
castArray as castArray4,
|
|
6619
6951
|
compact as compact3,
|
|
6620
6952
|
forEach as forEach6,
|
|
6953
|
+
get as get15,
|
|
6621
6954
|
isArray as isArray12,
|
|
6622
6955
|
isBoolean as isBoolean5,
|
|
6623
6956
|
isFunction as isFunction6,
|
|
@@ -6627,27 +6960,6 @@ import {
|
|
|
6627
6960
|
uniq as uniq3
|
|
6628
6961
|
} from "@web-ts-toolkit/utils";
|
|
6629
6962
|
|
|
6630
|
-
// src/meta.ts
|
|
6631
|
-
init_runtime();
|
|
6632
|
-
init_runtime_context();
|
|
6633
|
-
var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
|
|
6634
|
-
var getModelRef = (modelName, refPath) => {
|
|
6635
|
-
const direct = getRuntime5().getModelRef(modelName, refPath);
|
|
6636
|
-
if (direct) return direct;
|
|
6637
|
-
if (!refPath.includes(".")) return null;
|
|
6638
|
-
try {
|
|
6639
|
-
const runtime = getRuntime5();
|
|
6640
|
-
const instance = runtime.getModelInstance?.(
|
|
6641
|
-
modelName
|
|
6642
|
-
);
|
|
6643
|
-
const schemaPath = instance?.schema?.path?.(refPath);
|
|
6644
|
-
const ref = schemaPath?.options?.ref ?? schemaPath?.caster?.options?.ref ?? null;
|
|
6645
|
-
return typeof ref === "string" && ref.length > 0 ? ref : null;
|
|
6646
|
-
} catch {
|
|
6647
|
-
return null;
|
|
6648
|
-
}
|
|
6649
|
-
};
|
|
6650
|
-
|
|
6651
6963
|
// src/services/base.ts
|
|
6652
6964
|
init_options();
|
|
6653
6965
|
init_helpers();
|
|
@@ -6663,7 +6975,7 @@ import {
|
|
|
6663
6975
|
forEach as forEach3,
|
|
6664
6976
|
get as get8,
|
|
6665
6977
|
isArray as isArray9,
|
|
6666
|
-
isPlainObject as
|
|
6978
|
+
isPlainObject as isPlainObject18,
|
|
6667
6979
|
set as set6,
|
|
6668
6980
|
uniq
|
|
6669
6981
|
} from "@web-ts-toolkit/utils";
|
|
@@ -6788,7 +7100,7 @@ init_public_admission();
|
|
|
6788
7100
|
// src/correlated-includes.ts
|
|
6789
7101
|
init_request_complexity();
|
|
6790
7102
|
init_sort_policy();
|
|
6791
|
-
import { cloneDeep, isArray as isArray6, isPlainObject as
|
|
7103
|
+
import { cloneDeep, isArray as isArray6, isPlainObject as isPlainObject15 } from "@web-ts-toolkit/utils";
|
|
6792
7104
|
var PARENT_MARKER_KEY = "$parent";
|
|
6793
7105
|
var ESCAPE_WRAPPER_KEY = "$escape";
|
|
6794
7106
|
var SUBQUERY_PAYLOAD_KEYS = /* @__PURE__ */ new Set(["$$sq", "$$date"]);
|
|
@@ -6799,10 +7111,10 @@ var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
|
6799
7111
|
var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
|
|
6800
7112
|
var ownKeys = (value) => Object.keys(value);
|
|
6801
7113
|
function isParentRefMarker(value) {
|
|
6802
|
-
return
|
|
7114
|
+
return isPlainObject15(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
|
|
6803
7115
|
}
|
|
6804
7116
|
function isEscapedParentRef(value) {
|
|
6805
|
-
return
|
|
7117
|
+
return isPlainObject15(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
|
|
6806
7118
|
}
|
|
6807
7119
|
function hasMarkerKey(value) {
|
|
6808
7120
|
return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
|
|
@@ -6826,7 +7138,7 @@ function containsParentMarkerShape(value) {
|
|
|
6826
7138
|
seen.add(current);
|
|
6827
7139
|
return current.some(visit);
|
|
6828
7140
|
}
|
|
6829
|
-
if (
|
|
7141
|
+
if (isPlainObject15(current)) {
|
|
6830
7142
|
if (seen.has(current)) return false;
|
|
6831
7143
|
seen.add(current);
|
|
6832
7144
|
if (hasMarkerKey(current)) return true;
|
|
@@ -6856,7 +7168,7 @@ function scanTemplateValueNode(value, path, issues) {
|
|
|
6856
7168
|
}
|
|
6857
7169
|
return;
|
|
6858
7170
|
}
|
|
6859
|
-
if (
|
|
7171
|
+
if (isPlainObject15(value)) {
|
|
6860
7172
|
if (hasMarkerKey(value)) {
|
|
6861
7173
|
issues.push({
|
|
6862
7174
|
message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
|
|
@@ -6921,7 +7233,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
6921
7233
|
});
|
|
6922
7234
|
return;
|
|
6923
7235
|
}
|
|
6924
|
-
if (
|
|
7236
|
+
if (isPlainObject15(clause)) {
|
|
6925
7237
|
scanTemplateObjectEntries(clause, clausePath, issues);
|
|
6926
7238
|
}
|
|
6927
7239
|
});
|
|
@@ -6932,7 +7244,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
6932
7244
|
}
|
|
6933
7245
|
function validateCorrelatedFilterTemplate(filter2) {
|
|
6934
7246
|
const issues = [];
|
|
6935
|
-
if (!
|
|
7247
|
+
if (!isPlainObject15(filter2)) {
|
|
6936
7248
|
issues.push({ message: "Correlated include filter must be an object", path: [] });
|
|
6937
7249
|
return issues;
|
|
6938
7250
|
}
|
|
@@ -6981,7 +7293,7 @@ function collectFilterReferencePaths(filter2) {
|
|
|
6981
7293
|
value.forEach(visitValue);
|
|
6982
7294
|
return;
|
|
6983
7295
|
}
|
|
6984
|
-
if (
|
|
7296
|
+
if (isPlainObject15(value)) {
|
|
6985
7297
|
if (seen.has(value)) return;
|
|
6986
7298
|
seen.add(value);
|
|
6987
7299
|
for (const [key, child] of Object.entries(value)) {
|
|
@@ -7027,7 +7339,7 @@ function getOwnPathValue(parent, path) {
|
|
|
7027
7339
|
return current;
|
|
7028
7340
|
}
|
|
7029
7341
|
function cloneSubstitutedValue(value) {
|
|
7030
|
-
if (
|
|
7342
|
+
if (isPlainObject15(value) || isArray6(value)) return cloneDeep(value);
|
|
7031
7343
|
return value;
|
|
7032
7344
|
}
|
|
7033
7345
|
function literalParentRefObject(path) {
|
|
@@ -7076,7 +7388,7 @@ function resolveObjectEntries(obj, parent) {
|
|
|
7076
7388
|
if (isParentRefMarker(clause) || isEscapedParentRef(clause)) {
|
|
7077
7389
|
throw new CorrelatedReferenceError(`Unsupported $parent reference as a direct ${key} clause`);
|
|
7078
7390
|
}
|
|
7079
|
-
if (!
|
|
7391
|
+
if (!isPlainObject15(clause)) return cloneSubstitutedValue(clause);
|
|
7080
7392
|
return resolveObjectEntries(clause, parent);
|
|
7081
7393
|
});
|
|
7082
7394
|
continue;
|
|
@@ -7105,10 +7417,10 @@ function resolveValueNode(template, parent, bare) {
|
|
|
7105
7417
|
}
|
|
7106
7418
|
const value = getOwnPathValue(parent, path);
|
|
7107
7419
|
if (value === void 0 || value === null) throw UNRESOLVABLE;
|
|
7108
|
-
if (bare &&
|
|
7420
|
+
if (bare && isPlainObject15(value)) return { $eq: cloneDeep(value) };
|
|
7109
7421
|
return cloneSubstitutedValue(value);
|
|
7110
7422
|
}
|
|
7111
|
-
if (
|
|
7423
|
+
if (isPlainObject15(template)) {
|
|
7112
7424
|
if (hasMarkerKey(template)) {
|
|
7113
7425
|
throw new CorrelatedReferenceError(
|
|
7114
7426
|
'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }'
|
|
@@ -7150,7 +7462,7 @@ function resolveCorrelatedIdTemplate(id, parent) {
|
|
|
7150
7462
|
}
|
|
7151
7463
|
function resolveCorrelatedFilterTemplate(filter2, parent) {
|
|
7152
7464
|
try {
|
|
7153
|
-
if (!
|
|
7465
|
+
if (!isPlainObject15(filter2) || isParentRefMarker(filter2) || isEscapedParentRef(filter2)) {
|
|
7154
7466
|
throw new CorrelatedReferenceError("Correlated include filter must be an object");
|
|
7155
7467
|
}
|
|
7156
7468
|
return { status: "resolved", filter: resolveObjectEntries(filter2, parent) };
|
|
@@ -7165,7 +7477,7 @@ function validateExpandedCorrelatedOperands(expanded, limits) {
|
|
|
7165
7477
|
var MAX_NESTED_INCLUDE_DEPTH = 10;
|
|
7166
7478
|
function validateCorrelatedArgsShape(args, op, depth) {
|
|
7167
7479
|
if (args === void 0) return [];
|
|
7168
|
-
if (!
|
|
7480
|
+
if (!isPlainObject15(args)) return ["Correlated include args must be an object"];
|
|
7169
7481
|
const record3 = args;
|
|
7170
7482
|
const allowed = op === "list" ? /* @__PURE__ */ new Set(["select", "sort", "skip", "limit", "page", "pageSize", "include"]) : /* @__PURE__ */ new Set(["select", "sort", "include"]);
|
|
7171
7483
|
for (const key of Object.keys(record3)) {
|
|
@@ -7187,7 +7499,7 @@ function validateCorrelatedArgsShape(args, op, depth) {
|
|
|
7187
7499
|
return [];
|
|
7188
7500
|
}
|
|
7189
7501
|
function validateCorrelatedIncludeShape(entry, depth = 0) {
|
|
7190
|
-
if (!
|
|
7502
|
+
if (!isPlainObject15(entry)) return ["Correlated include must be an object"];
|
|
7191
7503
|
const record3 = entry;
|
|
7192
7504
|
if (record3.mode !== "correlated") return ['Correlated include mode must be "correlated"'];
|
|
7193
7505
|
if (typeof record3.model !== "string" || record3.model.length === 0)
|
|
@@ -7200,7 +7512,7 @@ function validateCorrelatedIncludeShape(entry, depth = 0) {
|
|
|
7200
7512
|
if ("localField" in record3 || "foreignField" in record3) {
|
|
7201
7513
|
return ["Correlated includes must not carry localField or foreignField"];
|
|
7202
7514
|
}
|
|
7203
|
-
if (record3.options !== void 0 && !(
|
|
7515
|
+
if (record3.options !== void 0 && !(isPlainObject15(record3.options) && Object.keys(record3.options).length === 0)) {
|
|
7204
7516
|
return ["Correlated include options must be absent or empty"];
|
|
7205
7517
|
}
|
|
7206
7518
|
const hasId = record3.id !== void 0;
|
|
@@ -7222,7 +7534,7 @@ function validateCorrelatedIncludeShape(entry, depth = 0) {
|
|
|
7222
7534
|
return validateCorrelatedArgsShape(record3.args, record3.op, depth);
|
|
7223
7535
|
}
|
|
7224
7536
|
function isCorrelatedInclude(value) {
|
|
7225
|
-
return
|
|
7537
|
+
return isPlainObject15(value) && value.mode === "correlated";
|
|
7226
7538
|
}
|
|
7227
7539
|
|
|
7228
7540
|
// src/services/base.ts
|
|
@@ -7446,9 +7758,31 @@ function castCountGroups(input, values) {
|
|
|
7446
7758
|
// src/services/base.ts
|
|
7447
7759
|
init_service_authorization();
|
|
7448
7760
|
|
|
7761
|
+
// src/meta.ts
|
|
7762
|
+
init_runtime();
|
|
7763
|
+
init_runtime_context();
|
|
7764
|
+
var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
|
|
7765
|
+
var getModelRef = (modelName, refPath, runtime = getRuntime5()) => {
|
|
7766
|
+
const direct = runtime.getModelRef(modelName, refPath);
|
|
7767
|
+
if (direct) return direct;
|
|
7768
|
+
if (!refPath.includes(".")) return null;
|
|
7769
|
+
try {
|
|
7770
|
+
const instance = runtime.getModelInstance?.(
|
|
7771
|
+
modelName
|
|
7772
|
+
);
|
|
7773
|
+
const schemaPath = instance?.schema?.path?.(refPath);
|
|
7774
|
+
const element = schemaPath?.$embeddedSchemaType ?? schemaPath?.embeddedSchemaType ?? schemaPath?.caster;
|
|
7775
|
+
const ref = schemaPath?.options?.ref ?? element?.options?.ref ?? null;
|
|
7776
|
+
return typeof ref === "string" && ref.length > 0 ? ref : null;
|
|
7777
|
+
} catch {
|
|
7778
|
+
return null;
|
|
7779
|
+
}
|
|
7780
|
+
};
|
|
7781
|
+
|
|
7449
7782
|
// src/acl/related-execution.ts
|
|
7450
7783
|
init_public_admission();
|
|
7451
7784
|
init_model_output_planning();
|
|
7785
|
+
init_populate_target_configuration();
|
|
7452
7786
|
init_authorization_snapshot();
|
|
7453
7787
|
|
|
7454
7788
|
// src/acl/service-execution.ts
|
|
@@ -7486,9 +7820,9 @@ function createServiceExecution(receiver, access, authorization, applicability =
|
|
|
7486
7820
|
receiver,
|
|
7487
7821
|
binding,
|
|
7488
7822
|
authorization: call,
|
|
7489
|
-
access:
|
|
7823
|
+
access: readServiceAuthorizationAccess(call, binding),
|
|
7490
7824
|
family: execution,
|
|
7491
|
-
hydration: resolveHydration(
|
|
7825
|
+
hydration: resolveHydration(readServiceAuthorizationHydration(call, binding), hydration)
|
|
7492
7826
|
})
|
|
7493
7827
|
);
|
|
7494
7828
|
return execution;
|
|
@@ -7499,7 +7833,7 @@ function resolveHydration(configuration, source) {
|
|
|
7499
7833
|
}
|
|
7500
7834
|
function withExecutionHydration(receiver, parent, source) {
|
|
7501
7835
|
const record3 = readServiceExecution(receiver, parent);
|
|
7502
|
-
const
|
|
7836
|
+
const configuration = readServiceAuthorizationHydration(record3.authorization, record3.binding);
|
|
7503
7837
|
const execution = Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
7504
7838
|
executions.set(execution, Object.freeze({ ...record3, hydration: resolveHydration(configuration, source) }));
|
|
7505
7839
|
return execution;
|
|
@@ -7536,7 +7870,7 @@ function deriveServiceExecution(receiver, parent, access, applicability = "servi
|
|
|
7536
7870
|
const execution = Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
7537
7871
|
executions.set(
|
|
7538
7872
|
execution,
|
|
7539
|
-
Object.freeze({ ...record3, authorization: call, access:
|
|
7873
|
+
Object.freeze({ ...record3, authorization: call, access: readServiceAuthorizationAccess(call, record3.binding) })
|
|
7540
7874
|
);
|
|
7541
7875
|
return execution;
|
|
7542
7876
|
}
|
|
@@ -7743,16 +8077,15 @@ function captureRelatedTargets(runtime, modelName, args = {}, depth = 0, globalO
|
|
|
7743
8077
|
for (const entry of entries(args.populate)) {
|
|
7744
8078
|
const path = typeof entry === "string" ? entry : entry.path;
|
|
7745
8079
|
if (typeof entry !== "string") assertPopulateAccess(entry.access);
|
|
7746
|
-
const model = getModelRef(modelName, path);
|
|
7747
|
-
if (model
|
|
8080
|
+
const model = getModelRef(modelName, path, runtime);
|
|
8081
|
+
if (model) {
|
|
8082
|
+
const target = capturePopulateTargetConfiguration(runtime, model, `${modelName}.${path}`);
|
|
7748
8083
|
targets.push({
|
|
7749
8084
|
path,
|
|
7750
8085
|
modelName: model,
|
|
7751
|
-
configuration: captureModelAuthorizationConfiguration(runtime, model,
|
|
8086
|
+
configuration: captureModelAuthorizationConfiguration(runtime, model, target.options, globalOptions),
|
|
7752
8087
|
staticObjectIdReference: staticObjectIdReference(runtime, modelName, path, model)
|
|
7753
8088
|
});
|
|
7754
|
-
else if (model && !(globalOptions.requireRegisteredPopulateModels === void 0 ? true : globalOptions.requireRegisteredPopulateModels)) {
|
|
7755
|
-
targets.push({ path, modelName: model, kind: "passthrough", configuration: { catalog: { paths: [] } } });
|
|
7756
8089
|
}
|
|
7757
8090
|
}
|
|
7758
8091
|
for (const entry of entries(args.include)) {
|
|
@@ -7938,20 +8271,20 @@ var copyAuthorizedOutput = (value) => {
|
|
|
7938
8271
|
transferDocumentAuthorizationGraph(value, copy);
|
|
7939
8272
|
return copy;
|
|
7940
8273
|
};
|
|
7941
|
-
var isVirtualContainerValueForGuard = (value) =>
|
|
8274
|
+
var isVirtualContainerValueForGuard = (value) => isPlainObject18(value) && "sub" in value;
|
|
7942
8275
|
var getScopeVirtualsForGuard = (rootVirtuals, scopePath) => {
|
|
7943
|
-
if (!rootVirtuals || !
|
|
8276
|
+
if (!rootVirtuals || !isPlainObject18(rootVirtuals)) return null;
|
|
7944
8277
|
if (scopePath.length === 0) return rootVirtuals;
|
|
7945
8278
|
let current = rootVirtuals;
|
|
7946
8279
|
for (let i = 0; i < scopePath.length; i += 2) {
|
|
7947
8280
|
const field = scopePath[i];
|
|
7948
8281
|
const subMarker = scopePath[i + 1];
|
|
7949
8282
|
if (!field || subMarker !== "sub") return null;
|
|
7950
|
-
if (!
|
|
8283
|
+
if (!isPlainObject18(current)) return null;
|
|
7951
8284
|
const container = current[field];
|
|
7952
8285
|
if (!isVirtualContainerValueForGuard(container)) return null;
|
|
7953
8286
|
current = container.sub;
|
|
7954
|
-
if (!
|
|
8287
|
+
if (!isPlainObject18(current)) return null;
|
|
7955
8288
|
}
|
|
7956
8289
|
return current;
|
|
7957
8290
|
};
|
|
@@ -8017,7 +8350,7 @@ var sanitizeSubScopeVirtualKeys = (data, snapshot, sub) => {
|
|
|
8017
8350
|
for (const entry of value) stripScope(entry, currentScope);
|
|
8018
8351
|
return;
|
|
8019
8352
|
}
|
|
8020
|
-
if (!
|
|
8353
|
+
if (!isPlainObject18(value)) return;
|
|
8021
8354
|
const record3 = value;
|
|
8022
8355
|
const leaves = getScopeVirtualLeafNames(snapshot, currentScope);
|
|
8023
8356
|
for (const leaf of leaves) {
|
|
@@ -8030,7 +8363,7 @@ var sanitizeSubScopeVirtualKeys = (data, snapshot, sub) => {
|
|
|
8030
8363
|
for (const key of Object.keys(record3)) {
|
|
8031
8364
|
if (leaves.has(key) || containers.has(key)) continue;
|
|
8032
8365
|
const child = record3[key];
|
|
8033
|
-
if (Array.isArray(child) ||
|
|
8366
|
+
if (Array.isArray(child) || isPlainObject18(child)) {
|
|
8034
8367
|
stripScope(child, currentScope);
|
|
8035
8368
|
}
|
|
8036
8369
|
}
|
|
@@ -8042,7 +8375,7 @@ var LOGICAL_FILTER_KEYS = /* @__PURE__ */ new Set(["$and", "$or", "$nor"]);
|
|
|
8042
8375
|
var isOperatorKey = (key) => key.startsWith("$");
|
|
8043
8376
|
var REMOVED_VIRTUAL_FILTER = /* @__PURE__ */ Symbol("removed virtual filter clause");
|
|
8044
8377
|
var rewriteFilterObject = (value, visit) => {
|
|
8045
|
-
if (!
|
|
8378
|
+
if (!isPlainObject18(value)) return value;
|
|
8046
8379
|
let changed = false;
|
|
8047
8380
|
const entries = [];
|
|
8048
8381
|
for (const [key, child] of Object.entries(value)) {
|
|
@@ -8073,7 +8406,7 @@ var resolveVirtualFilterField = (path, scope2) => {
|
|
|
8073
8406
|
if (!Object.hasOwn(scope2, segment) || scope2[segment] === void 0) return null;
|
|
8074
8407
|
const definition = scope2[segment];
|
|
8075
8408
|
if (!isVirtualContainerValueForGuard(definition)) return REMOVED_VIRTUAL_FILTER;
|
|
8076
|
-
scope2 =
|
|
8409
|
+
scope2 = isPlainObject18(definition.sub) ? definition.sub : null;
|
|
8077
8410
|
inContainer = true;
|
|
8078
8411
|
}
|
|
8079
8412
|
return scope2;
|
|
@@ -8085,7 +8418,7 @@ var stripVirtualFilterOperator = (key, value, scope2) => {
|
|
|
8085
8418
|
}
|
|
8086
8419
|
if (key === "$all") {
|
|
8087
8420
|
return rewriteFilterClauses(value, (entry) => {
|
|
8088
|
-
if (!
|
|
8421
|
+
if (!isPlainObject18(entry) || Object.keys(entry).length !== 1 || !Object.hasOwn(entry, "$elemMatch")) return entry;
|
|
8089
8422
|
return rewriteFilterObject(entry, (operator, operand) => stripVirtualFilterOperator(operator, operand, scope2));
|
|
8090
8423
|
});
|
|
8091
8424
|
}
|
|
@@ -8100,7 +8433,7 @@ var stripVirtualFilterQuery = (value, scope2, element = false) => {
|
|
|
8100
8433
|
if (isOperatorKey(key)) return element ? stripVirtualFilterOperator(key, child, scope2) : child;
|
|
8101
8434
|
const fieldScope = resolveVirtualFilterField(key, scope2);
|
|
8102
8435
|
if (fieldScope === REMOVED_VIRTUAL_FILTER) return REMOVED_VIRTUAL_FILTER;
|
|
8103
|
-
if (!fieldScope || !
|
|
8436
|
+
if (!fieldScope || !isPlainObject18(child)) return child;
|
|
8104
8437
|
if (Object.keys(child).some(isOperatorKey)) {
|
|
8105
8438
|
return rewriteFilterObject(
|
|
8106
8439
|
child,
|
|
@@ -8124,7 +8457,7 @@ function getCorrelatedTemplateDepth(includes) {
|
|
|
8124
8457
|
const visit = (entries, depth) => {
|
|
8125
8458
|
const list = castArray2(entries).filter(Boolean);
|
|
8126
8459
|
for (const entry of list) {
|
|
8127
|
-
if (!
|
|
8460
|
+
if (!isPlainObject18(entry)) continue;
|
|
8128
8461
|
max = Math.max(max, depth);
|
|
8129
8462
|
const nested = entry.args?.include;
|
|
8130
8463
|
if (nested !== void 0) {
|
|
@@ -8153,7 +8486,7 @@ function validateClientFilter(filter2) {
|
|
|
8153
8486
|
value.forEach((item, index) => visit(item, `${path}[${index}]`));
|
|
8154
8487
|
return;
|
|
8155
8488
|
}
|
|
8156
|
-
if (!
|
|
8489
|
+
if (!isPlainObject18(value)) return;
|
|
8157
8490
|
Object.entries(value).forEach(([key, child]) => {
|
|
8158
8491
|
const nextPath = path ? `${path}.${key}` : key;
|
|
8159
8492
|
if (blockedOperators.has(key)) {
|
|
@@ -8435,9 +8768,9 @@ var Base = class {
|
|
|
8435
8768
|
const walk = (node, prefix) => {
|
|
8436
8769
|
for (const [key, value] of Object.entries(node)) {
|
|
8437
8770
|
if (value === void 0) continue;
|
|
8438
|
-
if (
|
|
8771
|
+
if (isPlainObject18(value) && "sub" in value) {
|
|
8439
8772
|
const sub = value.sub;
|
|
8440
|
-
if (
|
|
8773
|
+
if (isPlainObject18(sub)) walk(sub, prefix ? `${prefix}.${key}` : key);
|
|
8441
8774
|
continue;
|
|
8442
8775
|
}
|
|
8443
8776
|
out.push(prefix ? `${prefix}.${key}` : key);
|
|
@@ -8490,7 +8823,7 @@ var Base = class {
|
|
|
8490
8823
|
this.throwClientRequestError("bad_request" /* BadRequest */, `Model ${modelName} not found`);
|
|
8491
8824
|
}
|
|
8492
8825
|
for (const entry of entries) {
|
|
8493
|
-
if (!
|
|
8826
|
+
if (!isPlainObject18(entry)) continue;
|
|
8494
8827
|
const models = visited.get(entry) ?? /* @__PURE__ */ new Set();
|
|
8495
8828
|
if (models.has(modelName)) continue;
|
|
8496
8829
|
models.add(modelName);
|
|
@@ -9109,7 +9442,7 @@ import {
|
|
|
9109
9442
|
get as get13,
|
|
9110
9443
|
isArray as isArray10,
|
|
9111
9444
|
isBoolean as isBoolean4,
|
|
9112
|
-
isPlainObject as
|
|
9445
|
+
isPlainObject as isPlainObject25,
|
|
9113
9446
|
omit,
|
|
9114
9447
|
set as set10,
|
|
9115
9448
|
uniq as uniq2,
|
|
@@ -9210,7 +9543,7 @@ var model_default = Model;
|
|
|
9210
9543
|
// src/acl/distinct-field-authorization.ts
|
|
9211
9544
|
init_field_authorization();
|
|
9212
9545
|
init_field_catalog();
|
|
9213
|
-
import { isPlainObject as
|
|
9546
|
+
import { isPlainObject as isPlainObject19 } from "@web-ts-toolkit/utils";
|
|
9214
9547
|
var schemaVariants = (schema) => Object.values(schema.discriminators ?? {});
|
|
9215
9548
|
var scalarTypes = /* @__PURE__ */ new Set([
|
|
9216
9549
|
"String",
|
|
@@ -9290,8 +9623,8 @@ function isDistinctFieldAuthorized(args) {
|
|
|
9290
9623
|
if (path !== field && !path.startsWith(`${field}.`) && !field.startsWith(`${path}.`)) continue;
|
|
9291
9624
|
if (path.startsWith(`${field}.`) && (!eligible(path) || !schemaShape(schema, path) || !granted(path)))
|
|
9292
9625
|
return false;
|
|
9293
|
-
if (
|
|
9294
|
-
if (!
|
|
9626
|
+
if (isPlainObject19(raw) && raw.sub !== void 0) {
|
|
9627
|
+
if (!isPlainObject19(raw.sub) || !declaredRulesSafe(raw.sub, path, next)) return false;
|
|
9295
9628
|
}
|
|
9296
9629
|
}
|
|
9297
9630
|
return true;
|
|
@@ -9469,7 +9802,7 @@ init_populate_target();
|
|
|
9469
9802
|
|
|
9470
9803
|
// src/helpers/apply-update.ts
|
|
9471
9804
|
init_lib();
|
|
9472
|
-
import { isPlainObject as
|
|
9805
|
+
import { isPlainObject as isPlainObject23, set as set8 } from "@web-ts-toolkit/utils";
|
|
9473
9806
|
function applyUpdate(target, data, allowedFields) {
|
|
9474
9807
|
const wholeFields = /* @__PURE__ */ new Set();
|
|
9475
9808
|
const partialContainers = /* @__PURE__ */ new Set();
|
|
@@ -9489,7 +9822,7 @@ function applyUpdate(target, data, allowedFields) {
|
|
|
9489
9822
|
const apply = (values, prefix = "") => {
|
|
9490
9823
|
for (const [key, value] of Object.entries(values)) {
|
|
9491
9824
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
9492
|
-
if (partialContainers.has(path) && !wholeFields.has(path) && (
|
|
9825
|
+
if (partialContainers.has(path) && !wholeFields.has(path) && (isPlainObject23(value) || Array.isArray(value))) {
|
|
9493
9826
|
apply(value, path);
|
|
9494
9827
|
} else if (isDocument2(target)) {
|
|
9495
9828
|
target.set(path, value);
|
|
@@ -9506,7 +9839,7 @@ init_document();
|
|
|
9506
9839
|
init_request_work();
|
|
9507
9840
|
init_logger_helpers();
|
|
9508
9841
|
init_request_complexity();
|
|
9509
|
-
import { get as get10, isPlainObject as
|
|
9842
|
+
import { get as get10, isPlainObject as isPlainObject24, set as set9 } from "@web-ts-toolkit/utils";
|
|
9510
9843
|
import { mongo as mongo5 } from "mongoose";
|
|
9511
9844
|
|
|
9512
9845
|
// src/acl/select-resolution.ts
|
|
@@ -9623,7 +9956,7 @@ init_field_catalog();
|
|
|
9623
9956
|
init_service_authorization();
|
|
9624
9957
|
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
9625
9958
|
var inside = (path, root) => path === root || path.startsWith(`${root}.`);
|
|
9626
|
-
var record2 = (value) =>
|
|
9959
|
+
var record2 = (value) => isPlainObject24(value);
|
|
9627
9960
|
function inputValue(value) {
|
|
9628
9961
|
if (typeof value === "function" || typeof value === "symbol") {
|
|
9629
9962
|
authorizationError("input values must be persisted data, not functions or symbols");
|
|
@@ -10625,7 +10958,8 @@ var Service = class extends Base {
|
|
|
10625
10958
|
async finalizeDirectOutput(args) {
|
|
10626
10959
|
const { plan, context } = args;
|
|
10627
10960
|
const { authorization, authorizationBinding: binding } = plan;
|
|
10628
|
-
const
|
|
10961
|
+
const access = readServiceAuthorizationAccess(authorization, binding);
|
|
10962
|
+
const configuration = readServiceOutputMetadata(authorization, binding);
|
|
10629
10963
|
const source = getIncludeSource(args.doc);
|
|
10630
10964
|
const snapshot = isolateDocForOutput(source);
|
|
10631
10965
|
for (const key of Object.keys(snapshot))
|
|
@@ -10979,10 +11313,6 @@ var Service = class extends Base {
|
|
|
10979
11313
|
const target = captured.relatedTargets.find((target2) => target2.path === descriptor.path);
|
|
10980
11314
|
const modelName = target?.modelName;
|
|
10981
11315
|
if (!modelName) continue;
|
|
10982
|
-
if (target.kind === "passthrough") {
|
|
10983
|
-
result2.push({ path: descriptor.path, select: descriptor.select });
|
|
10984
|
-
continue;
|
|
10985
|
-
}
|
|
10986
11316
|
const policy = relatedPolicy(state.authorization, state.binding, descriptor.path);
|
|
10987
11317
|
const svc = this.requestCore.getPublicService(modelName);
|
|
10988
11318
|
const child = createRelatedExecution(
|
|
@@ -11001,7 +11331,7 @@ var Service = class extends Base {
|
|
|
11001
11331
|
if (isExecutionRowDenied(svc, child)) continue;
|
|
11002
11332
|
const childState = readServiceExecution(svc, child);
|
|
11003
11333
|
const targetConfiguration = readServiceAuthorization(childState.authorization, childState.binding).configuration;
|
|
11004
|
-
if (policy?.operation.mode !== "grant" && !await this.hookGate.run(
|
|
11334
|
+
if (targetConfiguration.requireOperationAccessForPopulateQueries !== false && policy?.operation.mode !== "grant" && !await this.hookGate.run(
|
|
11005
11335
|
() => evaluateRouteGuard(
|
|
11006
11336
|
this.req,
|
|
11007
11337
|
getAuthorizationPermissions(childState.authorization, childState.binding),
|
|
@@ -11053,7 +11383,7 @@ var Service = class extends Base {
|
|
|
11053
11383
|
if (this.isScalarPopulateValue(rawTarget)) return rawTarget;
|
|
11054
11384
|
const isDoc = (() => {
|
|
11055
11385
|
try {
|
|
11056
|
-
if (
|
|
11386
|
+
if (isPlainObject25(rawTarget)) return true;
|
|
11057
11387
|
const maybe = rawTarget;
|
|
11058
11388
|
if (maybe && typeof maybe === "object" && "_doc" in maybe) return true;
|
|
11059
11389
|
if (maybe && typeof maybe.toObject === "function") return true;
|
|
@@ -13456,7 +13786,7 @@ init_helpers();
|
|
|
13456
13786
|
|
|
13457
13787
|
// src/processors.ts
|
|
13458
13788
|
init_permissions_alias();
|
|
13459
|
-
import { cloneDeep as cloneDeep5, forEach as forEach5, get as get14, hasOwn as hasOwn3, isArray as isArray11, isPlainObject as
|
|
13789
|
+
import { cloneDeep as cloneDeep5, forEach as forEach5, get as get14, hasOwn as hasOwn3, isArray as isArray11, isPlainObject as isPlainObject26, map, set as set11 } from "@web-ts-toolkit/utils";
|
|
13460
13790
|
function cloneProcessorValue(value) {
|
|
13461
13791
|
const copy = cloneDeep5(value);
|
|
13462
13792
|
copyPermissionsAliasGraph(value, copy);
|
|
@@ -13496,7 +13826,7 @@ function resolveLeafParents(obj, src) {
|
|
|
13496
13826
|
for (let ind = 0; ind < segs.length - 1; ind += 1) {
|
|
13497
13827
|
const seg = segs[ind];
|
|
13498
13828
|
targets = targets.reduce((ret, target) => {
|
|
13499
|
-
if (!
|
|
13829
|
+
if (!isPlainObject26(target)) return ret;
|
|
13500
13830
|
const targetObject = target;
|
|
13501
13831
|
const next = targetObject[seg];
|
|
13502
13832
|
if (isArray11(next)) {
|
|
@@ -13510,7 +13840,7 @@ function resolveLeafParents(obj, src) {
|
|
|
13510
13840
|
const leaf = segs[segs.length - 1];
|
|
13511
13841
|
const pairs = [];
|
|
13512
13842
|
forEach5(targets, (target) => {
|
|
13513
|
-
if (!
|
|
13843
|
+
if (!isPlainObject26(target)) return;
|
|
13514
13844
|
pairs.push({ parent: target, leaf });
|
|
13515
13845
|
});
|
|
13516
13846
|
return pairs;
|
|
@@ -13531,7 +13861,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
13531
13861
|
forEach5(segs, (seg, ind) => {
|
|
13532
13862
|
if (segs.length === ind + 1) {
|
|
13533
13863
|
forEach5(targets, (target) => {
|
|
13534
|
-
if (!
|
|
13864
|
+
if (!isPlainObject26(target)) return;
|
|
13535
13865
|
const targetObject = target;
|
|
13536
13866
|
const leaf = targetObject[seg];
|
|
13537
13867
|
const sourceForDest = leaf;
|
|
@@ -13539,7 +13869,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
13539
13869
|
if (isArray11(leaf)) {
|
|
13540
13870
|
if (leaf.length === 0) {
|
|
13541
13871
|
depopulated = [];
|
|
13542
|
-
} else if (leaf.every((item) =>
|
|
13872
|
+
} else if (leaf.every((item) => isPlainObject26(item))) {
|
|
13543
13873
|
depopulated = map(leaf, idField).map((id, i) => {
|
|
13544
13874
|
if (id === void 0) {
|
|
13545
13875
|
failMissingId(`${src}[${i}]`);
|
|
@@ -13549,7 +13879,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
13549
13879
|
} else {
|
|
13550
13880
|
return;
|
|
13551
13881
|
}
|
|
13552
|
-
} else if (
|
|
13882
|
+
} else if (isPlainObject26(leaf)) {
|
|
13553
13883
|
const id = get14(leaf, idField);
|
|
13554
13884
|
if (id === void 0) {
|
|
13555
13885
|
failMissingId(src);
|
|
@@ -13563,7 +13893,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
13563
13893
|
});
|
|
13564
13894
|
} else {
|
|
13565
13895
|
targets = targets.reduce((ret, target) => {
|
|
13566
|
-
if (!
|
|
13896
|
+
if (!isPlainObject26(target)) return ret;
|
|
13567
13897
|
const targetObject = target;
|
|
13568
13898
|
const next = targetObject[seg];
|
|
13569
13899
|
if (isArray11(next)) {
|
|
@@ -13669,7 +13999,7 @@ var countPaths = (docObject, operations, options = { mutable: true }) => {
|
|
|
13669
13999
|
let count;
|
|
13670
14000
|
if (isArray11(leafValue)) {
|
|
13671
14001
|
count = leafValue.length;
|
|
13672
|
-
} else if (
|
|
14002
|
+
} else if (isPlainObject26(leafValue)) {
|
|
13673
14003
|
count = Object.keys(leafValue).length;
|
|
13674
14004
|
} else if (leafValue === null || typeof leafValue === "undefined") {
|
|
13675
14005
|
count = 0;
|
|
@@ -13747,6 +14077,8 @@ init_core_shared();
|
|
|
13747
14077
|
init_virtual_projection();
|
|
13748
14078
|
init_populate_target();
|
|
13749
14079
|
init_model_output_planning();
|
|
14080
|
+
init_authorization_snapshot();
|
|
14081
|
+
init_document();
|
|
13750
14082
|
init_output_field_filter();
|
|
13751
14083
|
init_request_work();
|
|
13752
14084
|
var emptyPermissionLeaf = () => ({ $: "_" });
|
|
@@ -13857,17 +14189,11 @@ var Core = class {
|
|
|
13857
14189
|
return this.genSelect(modelName, access, targetFields, options);
|
|
13858
14190
|
}
|
|
13859
14191
|
/** Persisted request-only populate admission; fetch planning is not field authority. */
|
|
13860
|
-
async requestPopulateSelect(
|
|
14192
|
+
async requestPopulateSelect(configuration, permissions, access, targetFields = null, scopePath = []) {
|
|
13861
14193
|
const subPaths = scopePath.flatMap((part) => [part, "sub"]);
|
|
13862
|
-
const permissionSchema =
|
|
13863
|
-
const
|
|
13864
|
-
|
|
13865
|
-
try {
|
|
13866
|
-
const runtime = getActiveRuntime() ?? defaultRuntime;
|
|
13867
|
-
virtualNames = runtime.getVirtualNames(modelName, subPaths);
|
|
13868
|
-
} catch {
|
|
13869
|
-
virtualNames = [];
|
|
13870
|
-
}
|
|
14194
|
+
const permissionSchema = get15(configuration, ["permissionSchema", ...subPaths]);
|
|
14195
|
+
const prefix = scopePath.length ? `${scopePath.join(".")}.` : "";
|
|
14196
|
+
const virtualNames = (configuration.catalog.virtualPaths ?? []).filter((path) => path.startsWith(prefix)).map((path) => path.slice(prefix.length)).filter((path) => !path.includes("."));
|
|
13871
14197
|
return resolveSelectForRequest({
|
|
13872
14198
|
req: this.req,
|
|
13873
14199
|
permissionSchema,
|
|
@@ -13886,13 +14212,21 @@ var Core = class {
|
|
|
13886
14212
|
assertPublicAdmission(_populate, "populate");
|
|
13887
14213
|
assertPopulateAccess(access);
|
|
13888
14214
|
if (!_populate) return [];
|
|
13889
|
-
|
|
13890
|
-
for (const p of
|
|
14215
|
+
const descriptors = copyAuthorizationValue(Array.isArray(_populate) ? _populate : [_populate], true);
|
|
14216
|
+
for (const p of descriptors) {
|
|
13891
14217
|
if (!isString6(p)) assertPopulateAccess(p.access);
|
|
13892
14218
|
}
|
|
13893
|
-
|
|
14219
|
+
const runtime = this.requestRuntime;
|
|
14220
|
+
const globalOptions = modelServiceGlobalOptions(this, runtime);
|
|
14221
|
+
const relatedTargets = captureRelatedTargets(runtime, modelName, { populate: descriptors }, 0, globalOptions);
|
|
14222
|
+
const sourceConfiguration = captureModelAuthorizationConfiguration(runtime, modelName, void 0, globalOptions);
|
|
14223
|
+
const originalPermissions = this.getGlobalPermissions();
|
|
14224
|
+
const permissions = new permission_default(
|
|
14225
|
+
Object.fromEntries(originalPermissions.keys.map((key) => [key, originalPermissions.has(key)]))
|
|
14226
|
+
);
|
|
14227
|
+
const populate = compact3(
|
|
13894
14228
|
await Promise.all(
|
|
13895
|
-
|
|
14229
|
+
descriptors.map(async (p) => {
|
|
13896
14230
|
const populateAccess = !isString6(p) && p.access ? p.access : access;
|
|
13897
14231
|
assertPopulateAccess(populateAccess);
|
|
13898
14232
|
const originalSelect = isString6(p) ? void 0 : p.select;
|
|
@@ -13904,78 +14238,86 @@ var Core = class {
|
|
|
13904
14238
|
const pathForSourcePermission = scopePrefix && ret.path.startsWith(`${scopePrefix}.`) ? ret.path.slice(scopePrefix.length + 1) : ret.path;
|
|
13905
14239
|
const parentPath = pathForSourcePermission.includes(".") ? pathForSourcePermission.split(".")[0] : pathForSourcePermission;
|
|
13906
14240
|
const allowedParentPaths = await this.requestPopulateSelect(
|
|
13907
|
-
|
|
14241
|
+
sourceConfiguration,
|
|
14242
|
+
permissions,
|
|
13908
14243
|
populateAccess,
|
|
13909
14244
|
[parentPath],
|
|
13910
14245
|
scopePath
|
|
13911
14246
|
);
|
|
13912
14247
|
if (!allowedParentPaths.includes(parentPath)) return null;
|
|
13913
|
-
const
|
|
13914
|
-
if (!
|
|
13915
|
-
const
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
14248
|
+
const target = relatedTargets.find((entry) => entry.path === ret.path);
|
|
14249
|
+
if (!target) return null;
|
|
14250
|
+
const refModelName = target.modelName;
|
|
14251
|
+
const targetConfiguration = target.configuration;
|
|
14252
|
+
if (targetConfiguration.requireOperationAccessForPopulateQueries !== false && !await withCapturedModelOperationGuard(
|
|
14253
|
+
this,
|
|
14254
|
+
refModelName,
|
|
14255
|
+
populateAccess,
|
|
14256
|
+
getCapturedAuthorizationOption(targetConfiguration, `operationAccess.${populateAccess}`),
|
|
14257
|
+
permissions,
|
|
14258
|
+
() => this.isAllowed(refModelName, populateAccess)
|
|
14259
|
+
)) {
|
|
13923
14260
|
return null;
|
|
13924
14261
|
}
|
|
13925
|
-
ret.select = await this.requestPopulateSelect(
|
|
13926
|
-
|
|
13927
|
-
|
|
13928
|
-
|
|
14262
|
+
ret.select = await this.requestPopulateSelect(
|
|
14263
|
+
targetConfiguration,
|
|
14264
|
+
permissions,
|
|
14265
|
+
populateAccess,
|
|
14266
|
+
ret.select
|
|
14267
|
+
);
|
|
14268
|
+
const hookInputs = resolveAlwaysSelectForAccess(
|
|
14269
|
+
targetConfiguration.alwaysSelectFields,
|
|
14270
|
+
populateAccess
|
|
14271
|
+
);
|
|
14272
|
+
ret.select = uniq3([
|
|
14273
|
+
...ret.select,
|
|
14274
|
+
...stripVirtuals(hookInputs, [...targetConfiguration.catalog.virtualPaths ?? []])
|
|
14275
|
+
]);
|
|
14276
|
+
const match = isString6(p) ? {} : p.match ?? {};
|
|
14277
|
+
const filter2 = await resolveAccessFilterForRequest({
|
|
14278
|
+
req: this.req,
|
|
14279
|
+
permissions,
|
|
14280
|
+
cache: this.caches.baseFilter,
|
|
14281
|
+
cacheKey: `${refModelName}_baseFilter_${populateAccess}`,
|
|
14282
|
+
access: populateAccess,
|
|
14283
|
+
filter: deepIsolateValue(match),
|
|
14284
|
+
getOption: (key) => getCapturedAuthorizationOption(targetConfiguration, key)
|
|
14285
|
+
});
|
|
13929
14286
|
if (filter2 === false) return null;
|
|
13930
|
-
ret.match = filter2;
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
(t) => t !== "-_id" && !t.includes(".sub.")
|
|
13965
|
-
);
|
|
13966
|
-
if (!merged.includes("_id")) merged.push("_id");
|
|
13967
|
-
ret.select = merged;
|
|
13968
|
-
setPopulateTargetMeta(ret, {
|
|
13969
|
-
targetModelName: refModelName,
|
|
13970
|
-
virtualAccess: planAccess,
|
|
13971
|
-
outputAccess: planAccess,
|
|
13972
|
-
docPermissionsAccess: planAccess,
|
|
13973
|
-
requestedSelect: originalSelect,
|
|
13974
|
-
plan: targetPlan,
|
|
13975
|
-
snapshot: targetSnapshot
|
|
13976
|
-
});
|
|
13977
|
-
} catch {
|
|
13978
|
-
}
|
|
14287
|
+
ret.match = deepIsolateValue(intersectFilters(match, filter2));
|
|
14288
|
+
const planAccess = populateAccess;
|
|
14289
|
+
const targetSnapshot = targetConfiguration;
|
|
14290
|
+
const targetPlan = planVirtualProjection({
|
|
14291
|
+
receivingModelName: refModelName,
|
|
14292
|
+
snapshot: {
|
|
14293
|
+
permissionSchema: targetSnapshot.permissionSchema,
|
|
14294
|
+
virtuals: targetSnapshot.virtuals,
|
|
14295
|
+
alwaysSelectFields: targetSnapshot.alwaysSelectFields,
|
|
14296
|
+
modelPermissionPrefix: targetSnapshot.modelPermissionPrefix,
|
|
14297
|
+
requireExplicitSelect: targetSnapshot.requireExplicitSelect,
|
|
14298
|
+
documentPermissionField: targetSnapshot.documentPermissionField
|
|
14299
|
+
},
|
|
14300
|
+
virtualAccess: planAccess,
|
|
14301
|
+
outputAccess: planAccess,
|
|
14302
|
+
docPermissionsAccess: planAccess,
|
|
14303
|
+
requestedSelect: originalSelect,
|
|
14304
|
+
internalFetch: { baseFields: ["_id"] },
|
|
14305
|
+
globalPermissions: permissions
|
|
14306
|
+
});
|
|
14307
|
+
const merged = uniq3([...normalizeSelect(ret.select), ...targetPlan.virtualOnlyDeps ?? []]).filter(
|
|
14308
|
+
(t) => t !== "-_id" && !t.includes(".sub.")
|
|
14309
|
+
);
|
|
14310
|
+
if (!merged.includes("_id")) merged.push("_id");
|
|
14311
|
+
ret.select = merged;
|
|
14312
|
+
setPopulateTargetMeta(ret, {
|
|
14313
|
+
targetModelName: refModelName,
|
|
14314
|
+
virtualAccess: planAccess,
|
|
14315
|
+
outputAccess: planAccess,
|
|
14316
|
+
docPermissionsAccess: planAccess,
|
|
14317
|
+
requestedSelect: originalSelect,
|
|
14318
|
+
plan: targetPlan,
|
|
14319
|
+
snapshot: targetSnapshot
|
|
14320
|
+
});
|
|
13979
14321
|
return ret;
|
|
13980
14322
|
})
|
|
13981
14323
|
)
|
|
@@ -14299,12 +14641,12 @@ function guard(condition) {
|
|
|
14299
14641
|
const permissions = req[PERMISSIONS];
|
|
14300
14642
|
let cond = condition;
|
|
14301
14643
|
let phas = (key) => permissions.has(key);
|
|
14302
|
-
if (
|
|
14644
|
+
if (isPlainObject27(condition)) {
|
|
14303
14645
|
const { modelName, id, condition: _cond } = condition;
|
|
14304
14646
|
const svc = req.macl.getPublicService(modelName);
|
|
14305
14647
|
const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
|
|
14306
14648
|
let _id = isString7(id) ? id : null;
|
|
14307
|
-
if (
|
|
14649
|
+
if (isPlainObject27(id)) {
|
|
14308
14650
|
const { type = "param", key } = id;
|
|
14309
14651
|
if (type === "param") {
|
|
14310
14652
|
const paramValue = req.params[key];
|
|
@@ -14346,7 +14688,7 @@ import express2 from "express";
|
|
|
14346
14688
|
|
|
14347
14689
|
// src/routers/model-router.ts
|
|
14348
14690
|
import JsonRouter8 from "@web-ts-toolkit/express-json-router";
|
|
14349
|
-
import { forEach as forEach7, isPlainObject as
|
|
14691
|
+
import { forEach as forEach7, isPlainObject as isPlainObject29, isString as isString8, isUndefined as isUndefined2, normalizeUrlPath as normalizeUrlPath3, padEnd } from "@web-ts-toolkit/utils";
|
|
14350
14692
|
init_logger_helpers();
|
|
14351
14693
|
init_runtime();
|
|
14352
14694
|
init_runtime_context();
|
|
@@ -14480,7 +14822,7 @@ import JsonRouter7 from "@web-ts-toolkit/express-json-router";
|
|
|
14480
14822
|
|
|
14481
14823
|
// src/validation/common.ts
|
|
14482
14824
|
import { z as z2 } from "zod";
|
|
14483
|
-
import { isPlainObject as
|
|
14825
|
+
import { isPlainObject as isPlainObject28 } from "@web-ts-toolkit/utils";
|
|
14484
14826
|
|
|
14485
14827
|
// src/validation/public-admission-schema.ts
|
|
14486
14828
|
init_public_admission();
|
|
@@ -14609,7 +14951,7 @@ var correlatedFilterSchema = z2.record(z2.string(), z2.unknown()).superRefine((f
|
|
|
14609
14951
|
}
|
|
14610
14952
|
});
|
|
14611
14953
|
var correlatedEmptyOptionsSchema = z2.unknown().refine(
|
|
14612
|
-
(value) => value === void 0 ||
|
|
14954
|
+
(value) => value === void 0 || isPlainObject28(value) && Object.keys(value).length === 0,
|
|
14613
14955
|
"Correlated include options must be absent or empty"
|
|
14614
14956
|
).optional();
|
|
14615
14957
|
var correlatedPathSchema = z2.string().min(1).superRefine((path, ctx) => {
|
|
@@ -14646,7 +14988,7 @@ var correlatedListArgsSchema = z2.object({
|
|
|
14646
14988
|
include: z2.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
14647
14989
|
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
14648
14990
|
var correlatedCountArgsSchema = z2.unknown().refine(
|
|
14649
|
-
(value) => value === void 0 ||
|
|
14991
|
+
(value) => value === void 0 || isPlainObject28(value) && Object.keys(value).length === 0,
|
|
14650
14992
|
"Count includes accept no args"
|
|
14651
14993
|
).optional();
|
|
14652
14994
|
var correlatedReadByIdSchema = z2.object({
|
|
@@ -14699,7 +15041,7 @@ var correlatedNestedIncludeSchema = z2.union([includeItemSchema, z2.array(includ
|
|
|
14699
15041
|
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
14700
15042
|
const seen = /* @__PURE__ */ new Set();
|
|
14701
15043
|
items.forEach((item, index) => {
|
|
14702
|
-
if (
|
|
15044
|
+
if (isPlainObject28(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
14703
15045
|
if (seen.has(item.path)) {
|
|
14704
15046
|
ctx.addIssue({
|
|
14705
15047
|
code: z2.ZodIssueCode.custom,
|
|
@@ -16700,7 +17042,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
16700
17042
|
value
|
|
16701
17043
|
);
|
|
16702
17044
|
}
|
|
16703
|
-
if (arguments.length === 1 &&
|
|
17045
|
+
if (arguments.length === 1 && isPlainObject29(keyOrOptions)) {
|
|
16704
17046
|
assertMutableRouterOptions("model", keyOrOptions);
|
|
16705
17047
|
this.runtime.setModelOptions(
|
|
16706
17048
|
this.modelName,
|
|
@@ -17234,7 +17576,7 @@ var RootRouter = class {
|
|
|
17234
17576
|
|
|
17235
17577
|
// src/routers/data-router.ts
|
|
17236
17578
|
import JsonRouter10 from "@web-ts-toolkit/express-json-router";
|
|
17237
|
-
import { isPlainObject as
|
|
17579
|
+
import { isPlainObject as isPlainObject30, isString as isString9, isUndefined as isUndefined3, normalizeUrlPath as normalizeUrlPath5 } from "@web-ts-toolkit/utils";
|
|
17238
17580
|
init_runtime();
|
|
17239
17581
|
init_helpers();
|
|
17240
17582
|
init_schemas();
|
|
@@ -17450,7 +17792,7 @@ var DataRouter = class {
|
|
|
17450
17792
|
value
|
|
17451
17793
|
);
|
|
17452
17794
|
}
|
|
17453
|
-
if (arguments.length === 1 &&
|
|
17795
|
+
if (arguments.length === 1 && isPlainObject30(keyOrOptions)) {
|
|
17454
17796
|
assertMutableRouterOptions("data", keyOrOptions);
|
|
17455
17797
|
this.runtime.setDataOptions(this.dataName, keyOrOptions);
|
|
17456
17798
|
}
|
|
@@ -17609,7 +17951,7 @@ function createRuntimeApi(runtime) {
|
|
|
17609
17951
|
if (arguments.length === 2 && isString10(keyOrOptions)) {
|
|
17610
17952
|
return runtime.setGlobalOption(keyOrOptions, value);
|
|
17611
17953
|
}
|
|
17612
|
-
if (arguments.length === 1 &&
|
|
17954
|
+
if (arguments.length === 1 && isPlainObject31(keyOrOptions)) {
|
|
17613
17955
|
return runtime.setGlobalOptions(keyOrOptions);
|
|
17614
17956
|
}
|
|
17615
17957
|
};
|