@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/index.js CHANGED
@@ -2161,9 +2161,34 @@ var init_field_catalog = __esm({
2161
2161
  }
2162
2162
  });
2163
2163
 
2164
+ // src/acl/populate-target-configuration.ts
2165
+ function capturePopulateTargetConfiguration(runtime, modelName, source, proposedOptions) {
2166
+ const label = `Populate target "${modelName}" for ${source}`;
2167
+ if (!proposedOptions && !runtime.hasModel(modelName))
2168
+ authorizationError(
2169
+ `${label} must be configured first in this runtime; register its Mongoose model and setModelOptions with an object permissionSchema`
2170
+ );
2171
+ const model = runtime.getModelInstance(modelName);
2172
+ if (!model) authorizationError(`${label} requires an actual registered Mongoose model in this runtime`);
2173
+ const options = proposedOptions ?? runtime.getModelOptions(modelName);
2174
+ const permissionSchema = options.permissionSchema === void 0 ? runtime.getDefaultModelOption("permissionSchema") : options.permissionSchema;
2175
+ if (!(0, import_utils15.isPlainObject)(permissionSchema))
2176
+ authorizationError(
2177
+ `${label} requires an effective object permissionSchema; configure the model or runtime model defaults ({} explicitly closes business fields)`
2178
+ );
2179
+ return { model, options: { ...options, permissionSchema } };
2180
+ }
2181
+ var import_utils15;
2182
+ var init_populate_target_configuration = __esm({
2183
+ "src/acl/populate-target-configuration.ts"() {
2184
+ import_utils15 = require("@web-ts-toolkit/utils");
2185
+ init_authorization_snapshot();
2186
+ }
2187
+ });
2188
+
2164
2189
  // src/acl/virtual-populate-dependencies.ts
2165
2190
  function record(value, label) {
2166
- if (!(0, import_utils15.isPlainObject)(value)) return fail(label, "entries must be plain { path, select? } objects");
2191
+ if (!(0, import_utils16.isPlainObject)(value)) return fail(label, "entries must be plain { path, select? } objects");
2167
2192
  for (const key of Reflect.ownKeys(value)) {
2168
2193
  const property = Object.getOwnPropertyDescriptor(value, key);
2169
2194
  if (typeof key !== "string" || !property.enumerable || !("value" in property))
@@ -2252,7 +2277,8 @@ function normalizeSelection(select, catalog, label) {
2252
2277
  };
2253
2278
  }
2254
2279
  function captureVirtualPopulateDependencies(runtime, modelName, options, globalOptions = runtime.getGlobalOptions()) {
2255
- const requireReadOperationAccessForPopulateDependencies = globalOptions.requireReadOperationAccessForPopulateDependencies !== false;
2280
+ const requireOperationAccessForPopulateDependencies = globalOptions.requireOperationAccessForPopulateDependencies !== false;
2281
+ const requireOperationAccessForPopulateQueries = globalOptions.requireOperationAccessForPopulateQueries !== false;
2256
2282
  const model = runtime.getModelInstance(modelName);
2257
2283
  const targets = /* @__PURE__ */ new Map();
2258
2284
  const scopes = [];
@@ -2292,16 +2318,23 @@ function captureVirtualPopulateDependencies(runtime, modelName, options, globalO
2292
2318
  label,
2293
2319
  `target "${ref}" requires a registered same-connection non-discriminator ObjectId model bound to the source ref`
2294
2320
  );
2295
- const targetOptions = ref === modelName ? options : runtime.getModelOptions(ref);
2321
+ const { options: targetOptions } = capturePopulateTargetConfiguration(
2322
+ runtime,
2323
+ ref,
2324
+ `dependsOnPopulate virtual "${label}"`,
2325
+ ref === modelName ? options : void 0
2326
+ );
2296
2327
  const defaults = runtime.getDefaultModelOptions();
2297
- const readOption = (key) => resolveNestedOption((path2, fallback) => (0, import_utils15.get)(targetOptions, path2, fallback), key, (0, import_utils15.get)(defaults, key));
2328
+ const readOption = (key) => resolveNestedOption((path2, fallback) => (0, import_utils16.get)(targetOptions, path2, fallback), key, (0, import_utils16.get)(defaults, key));
2298
2329
  const documentPermissionField = targetOptions.documentPermissionField ?? defaults.documentPermissionField;
2299
2330
  const catalog = captureAuthorizationFieldCatalog(targetModel.schema, void 0, documentPermissionField);
2300
2331
  const configuration = copyAuthorizationValue(
2301
2332
  {
2302
2333
  catalog,
2303
2334
  documentPermissionField,
2304
- requireReadOperationAccessForPopulateDependencies,
2335
+ requireOperationAccessForPopulateDependencies,
2336
+ requireOperationAccessForPopulateQueries,
2337
+ permissionSchema: targetOptions.permissionSchema,
2305
2338
  operationAccess: { read: readOption("operationAccess.read") },
2306
2339
  baseFilter: { read: readOption("baseFilter.read") },
2307
2340
  overrideFilter: { read: readOption("overrideFilter.read") },
@@ -2350,13 +2383,13 @@ function captureVirtualPopulateDependencies(runtime, modelName, options, globalO
2350
2383
  );
2351
2384
  continue;
2352
2385
  }
2353
- if (!(0, import_utils15.isPlainObject)(raw)) continue;
2386
+ if (!(0, import_utils16.isPlainObject)(raw)) continue;
2354
2387
  const entry = raw;
2355
2388
  const label = join(sourcePrefix, name);
2356
2389
  captureList(entry.dependsOnPopulate, label);
2357
2390
  if (!("get" in entry))
2358
2391
  for (const access of accesses) {
2359
- if ((0, import_utils15.isPlainObject)(entry[access]))
2392
+ if ((0, import_utils16.isPlainObject)(entry[access]))
2360
2393
  captureList(entry[access].dependsOnPopulate, `${label}.${access}`);
2361
2394
  }
2362
2395
  const byAccess = {};
@@ -2406,15 +2439,16 @@ function resolveVirtualPopulateDependencies(configuration, scopePath, virtualNam
2406
2439
  )
2407
2440
  );
2408
2441
  }
2409
- var import_utils15, elementType, join, within, accesses, fail, varyingSchema, varyingModel, snapshots;
2442
+ var import_utils16, elementType, join, within, accesses, fail, varyingSchema, varyingModel, snapshots;
2410
2443
  var init_virtual_populate_dependencies = __esm({
2411
2444
  "src/acl/virtual-populate-dependencies.ts"() {
2412
- import_utils15 = require("@web-ts-toolkit/utils");
2445
+ import_utils16 = require("@web-ts-toolkit/utils");
2413
2446
  init_query();
2414
2447
  init_manager();
2415
2448
  init_authorization_snapshot();
2416
2449
  init_field_catalog();
2417
2450
  init_virtual_descriptor();
2451
+ init_populate_target_configuration();
2418
2452
  elementType = (type) => type.$embeddedSchemaType ?? type.embeddedSchemaType ?? type.caster;
2419
2453
  join = (prefix, path) => prefix ? `${prefix}.${path}` : path;
2420
2454
  within = (path, parent) => path === parent || path.startsWith(`${parent}.`);
@@ -2429,14 +2463,14 @@ var init_virtual_populate_dependencies = __esm({
2429
2463
  });
2430
2464
 
2431
2465
  // src/runtime.ts
2432
- var import_mongoose4, import_mongoose_schema_jsonschema, import_utils16, mongooseJsonSchemaInitialized, ensureMongooseJsonSchemaInitialized, pluralize, FIELD_ACCESS_KEYS, defaultModelOptions, defaultDataOptions, normalizeBasePath, hasModelPermission, classifyPermissionSchema, refreshPermissionMetadata, VIRTUAL_SUPPORTED_ACCESSES, VIRTUAL_UNSUPPORTED_ACCESSES, VIRTUAL_RESERVED_NAMES, VIRTUAL_DANGEROUS_SEGMENTS, VIRTUAL_NAME_PATTERN, isVirtualContainerValue2, virtualFullPath, virtualPathsOverlap, collectStoredTopLevel, getChildSchemaForField, getNestedChildFields, cloneVirtualsForValidation, validateVirtualDependsOn, validateVirtualLeafValue, validateVirtualsScope, validateVirtualsTree, AccessRuntime, defaultRuntime;
2466
+ var import_mongoose4, import_mongoose_schema_jsonschema, import_utils17, mongooseJsonSchemaInitialized, ensureMongooseJsonSchemaInitialized, pluralize, FIELD_ACCESS_KEYS, defaultModelOptions, defaultDataOptions, normalizeBasePath, hasModelPermission, classifyPermissionSchema, refreshPermissionMetadata, VIRTUAL_SUPPORTED_ACCESSES, VIRTUAL_UNSUPPORTED_ACCESSES, VIRTUAL_RESERVED_NAMES, VIRTUAL_DANGEROUS_SEGMENTS, VIRTUAL_NAME_PATTERN, isVirtualContainerValue2, virtualFullPath, virtualPathsOverlap, collectStoredTopLevel, getChildSchemaForField, getNestedChildFields, cloneVirtualsForValidation, validateVirtualDependsOn, validateVirtualLeafValue, validateVirtualsScope, validateVirtualsTree, AccessRuntime, defaultRuntime;
2433
2467
  var init_runtime = __esm({
2434
2468
  "src/runtime.ts"() {
2435
2469
  import_mongoose4 = __toESM(require("mongoose"));
2436
2470
  init_public_admission();
2437
2471
  init_hydration();
2438
2472
  import_mongoose_schema_jsonschema = __toESM(require("mongoose-schema-jsonschema"));
2439
- import_utils16 = require("@web-ts-toolkit/utils");
2473
+ import_utils17 = require("@web-ts-toolkit/utils");
2440
2474
  init_helpers();
2441
2475
  init_registry();
2442
2476
  init_logger_default();
@@ -2465,44 +2499,44 @@ var init_runtime = __esm({
2465
2499
  listHardLimit: DEFAULT_LIST_HARD_LIMIT
2466
2500
  };
2467
2501
  normalizeBasePath = (name, value) => {
2468
- if ((0, import_utils16.isNil)(value)) {
2502
+ if ((0, import_utils17.isNil)(value)) {
2469
2503
  return `/${pluralize(name)}`;
2470
2504
  }
2471
- return (0, import_utils16.isString)(value) ? (0, import_utils16.addLeadingSlash)(value) : "";
2505
+ return (0, import_utils17.isString)(value) ? (0, import_utils17.addLeadingSlash)(value) : "";
2472
2506
  };
2473
2507
  hasModelPermission = (value, modelPermissionPrefix) => {
2474
2508
  if (!modelPermissionPrefix) {
2475
2509
  return true;
2476
2510
  }
2477
- if ((0, import_utils16.isString)(value)) {
2511
+ if ((0, import_utils17.isString)(value)) {
2478
2512
  return value.trim().split(" ").some((item) => item.startsWith(modelPermissionPrefix));
2479
2513
  }
2480
- if ((0, import_utils16.isArray)(value)) {
2514
+ if ((0, import_utils17.isArray)(value)) {
2481
2515
  return value.some((item) => {
2482
- if ((0, import_utils16.isString)(item) || (0, import_utils16.isArray)(item)) {
2516
+ if ((0, import_utils17.isString)(item) || (0, import_utils17.isArray)(item)) {
2483
2517
  return hasModelPermission(item, modelPermissionPrefix);
2484
2518
  }
2485
2519
  return true;
2486
2520
  });
2487
2521
  }
2488
- return (0, import_utils16.isFunction)(value);
2522
+ return (0, import_utils17.isFunction)(value);
2489
2523
  };
2490
2524
  classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
2491
2525
  const schemaKeys = Object.keys(permissionSchema);
2492
2526
  const globalPermissionKeys = {};
2493
2527
  const modelPermissionKeys = {};
2494
- (0, import_utils16.forEach)(schemaKeys, (schemaKey) => {
2528
+ (0, import_utils17.forEach)(schemaKeys, (schemaKey) => {
2495
2529
  const schemaRule = permissionSchema[schemaKey];
2496
- const ruleEntries = (0, import_utils16.isPlainObject)(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
2530
+ const ruleEntries = (0, import_utils17.isPlainObject)(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
2497
2531
  for (let x = 0; x < ruleEntries.length; x++) {
2498
2532
  const [accessKey, value] = ruleEntries[x];
2499
- if (!(0, import_utils16.isArray)(globalPermissionKeys[accessKey])) {
2533
+ if (!(0, import_utils17.isArray)(globalPermissionKeys[accessKey])) {
2500
2534
  globalPermissionKeys[accessKey] = [];
2501
2535
  }
2502
- if (!(0, import_utils16.isArray)(modelPermissionKeys[accessKey])) {
2536
+ if (!(0, import_utils17.isArray)(modelPermissionKeys[accessKey])) {
2503
2537
  modelPermissionKeys[accessKey] = [];
2504
2538
  }
2505
- if ((0, import_utils16.isBoolean)(value)) {
2539
+ if ((0, import_utils17.isBoolean)(value)) {
2506
2540
  globalPermissionKeys[accessKey].push(schemaKey);
2507
2541
  continue;
2508
2542
  }
@@ -2534,13 +2568,13 @@ var init_runtime = __esm({
2534
2568
  VIRTUAL_RESERVED_NAMES = /* @__PURE__ */ new Set(["_id", "__v", "id"]);
2535
2569
  VIRTUAL_DANGEROUS_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
2536
2570
  VIRTUAL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
2537
- isVirtualContainerValue2 = (value) => (0, import_utils16.isPlainObject)(value) && "sub" in value;
2571
+ isVirtualContainerValue2 = (value) => (0, import_utils17.isPlainObject)(value) && "sub" in value;
2538
2572
  virtualFullPath = (prefix, name) => prefix ? `${prefix}.${name}` : name;
2539
2573
  virtualPathsOverlap = (virtualPath, permissionField) => {
2540
2574
  if (!virtualPath || !permissionField) return false;
2541
2575
  try {
2542
- if ((0, import_utils16.get)((0, import_utils16.set)({}, virtualPath, true), permissionField) !== void 0) return true;
2543
- if ((0, import_utils16.get)((0, import_utils16.set)({}, permissionField, true), virtualPath) !== void 0) return true;
2576
+ if ((0, import_utils17.get)((0, import_utils17.set)({}, virtualPath, true), permissionField) !== void 0) return true;
2577
+ if ((0, import_utils17.get)((0, import_utils17.set)({}, permissionField, true), virtualPath) !== void 0) return true;
2544
2578
  } catch {
2545
2579
  return false;
2546
2580
  }
@@ -2601,12 +2635,12 @@ var init_runtime = __esm({
2601
2635
  };
2602
2636
  validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirtualNames) => {
2603
2637
  if (dependsOn === void 0) return [];
2604
- if (!(0, import_utils16.isArray)(dependsOn)) {
2638
+ if (!(0, import_utils17.isArray)(dependsOn)) {
2605
2639
  throw new Error(`Virtual ${entryLabel}: dependsOn must be an array of top-level persisted field names`);
2606
2640
  }
2607
2641
  const list = dependsOn;
2608
2642
  for (const dep of list) {
2609
- if (!(0, import_utils16.isString)(dep) || dep.length === 0) {
2643
+ if (!(0, import_utils17.isString)(dep) || dep.length === 0) {
2610
2644
  throw new Error(`Virtual ${entryLabel}: dependsOn entries must be non-empty strings`);
2611
2645
  }
2612
2646
  if (dep.includes(".")) {
@@ -2628,12 +2662,12 @@ var init_runtime = __esm({
2628
2662
  return list;
2629
2663
  };
2630
2664
  validateVirtualLeafValue = (value, entryLabel, storedFields, siblingVirtualNames, sharedDependsOn) => {
2631
- if ((0, import_utils16.isFunction)(value)) return;
2632
- if (!(0, import_utils16.isPlainObject)(value)) {
2665
+ if ((0, import_utils17.isFunction)(value)) return;
2666
+ if (!(0, import_utils17.isPlainObject)(value)) {
2633
2667
  throw new Error(`Virtual ${entryLabel}: descriptor must be a getter function or { get, dependsOn }`);
2634
2668
  }
2635
2669
  const record3 = value;
2636
- if (!(0, import_utils16.isFunction)(record3.get)) {
2670
+ if (!(0, import_utils17.isFunction)(record3.get)) {
2637
2671
  throw new Error(`Virtual ${entryLabel}: descriptor.get must be a callable getter function`);
2638
2672
  }
2639
2673
  validateVirtualDependsOn(record3.dependsOn ?? sharedDependsOn, entryLabel, storedFields, siblingVirtualNames);
@@ -2675,7 +2709,7 @@ var init_runtime = __esm({
2675
2709
  `Virtual ${scopeLabel}${name}: scope-container must only hold "sub" (embedded container "${name}" is not a virtual-leaf collision)`
2676
2710
  );
2677
2711
  }
2678
- if (!(0, import_utils16.isPlainObject)(record4.sub)) {
2712
+ if (!(0, import_utils17.isPlainObject)(record4.sub)) {
2679
2713
  throw new Error(`Virtual ${scopeLabel}${name}: scope-container "sub" must be a plain object`);
2680
2714
  }
2681
2715
  continue;
@@ -2685,8 +2719,8 @@ var init_runtime = __esm({
2685
2719
  `Virtual ${scopeLabel}${name}: collides with a stored path (inspected actual Mongoose schema/child scope; getModelAtt alone is insufficient)`
2686
2720
  );
2687
2721
  }
2688
- if ((0, import_utils16.isFunction)(raw)) continue;
2689
- if (!(0, import_utils16.isPlainObject)(raw)) {
2722
+ if ((0, import_utils17.isFunction)(raw)) continue;
2723
+ if (!(0, import_utils17.isPlainObject)(raw)) {
2690
2724
  throw new Error(
2691
2725
  `Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record)`
2692
2726
  );
@@ -2740,7 +2774,7 @@ var init_runtime = __esm({
2740
2774
  };
2741
2775
  validateVirtualsTree = (model, virtuals, documentPermissionField, modelName) => {
2742
2776
  if (virtuals === void 0 || virtuals === null) return;
2743
- if (!(0, import_utils16.isPlainObject)(virtuals)) {
2777
+ if (!(0, import_utils17.isPlainObject)(virtuals)) {
2744
2778
  throw new Error(`Virtuals for model "${modelName}": virtuals must be a plain object mapping names to descriptors`);
2745
2779
  }
2746
2780
  const rootSchema = model?.schema ?? null;
@@ -2792,8 +2826,8 @@ var init_runtime = __esm({
2792
2826
  {
2793
2827
  requestPermissionField: "_permissions",
2794
2828
  globalPermissions: () => ({}),
2795
- requireRegisteredPopulateModels: true,
2796
- requireReadOperationAccessForPopulateDependencies: true,
2829
+ requireOperationAccessForPopulateQueries: true,
2830
+ requireOperationAccessForPopulateDependencies: true,
2797
2831
  logger: defaultLogger,
2798
2832
  requestComplexity: defaultRequestComplexity
2799
2833
  },
@@ -2848,7 +2882,7 @@ var init_runtime = __esm({
2848
2882
  }
2849
2883
  registerOpenApiRoute(route) {
2850
2884
  this.openApiRegistry.register(
2851
- this.openApiPathPrefix ? { ...route, path: (0, import_utils16.normalizeUrlPath)(`${this.openApiPathPrefix}/${route.path}`) } : route
2885
+ this.openApiPathPrefix ? { ...route, path: (0, import_utils17.normalizeUrlPath)(`${this.openApiPathPrefix}/${route.path}`) } : route
2852
2886
  );
2853
2887
  }
2854
2888
  /**
@@ -2859,7 +2893,7 @@ var init_runtime = __esm({
2859
2893
  */
2860
2894
  withOpenApiPathPrefix(prefix, build) {
2861
2895
  const previous = this.openApiPathPrefix;
2862
- this.openApiPathPrefix = (0, import_utils16.normalizeUrlPath)(`${previous}/${prefix}`);
2896
+ this.openApiPathPrefix = (0, import_utils17.normalizeUrlPath)(`${previous}/${prefix}`);
2863
2897
  try {
2864
2898
  return build();
2865
2899
  } finally {
@@ -2967,13 +3001,15 @@ var init_runtime = __esm({
2967
3001
  /** Shallowly replace supplied global values; nested objects such as requestComplexity are not deep-merged. */
2968
3002
  setGlobalOptions(options) {
2969
3003
  const next = { ...options };
2970
- if ("requireReadOperationAccessForPopulateDependencies" in next)
2971
- next.requireReadOperationAccessForPopulateDependencies = next.requireReadOperationAccessForPopulateDependencies !== false;
3004
+ if ("requireOperationAccessForPopulateQueries" in next)
3005
+ next.requireOperationAccessForPopulateQueries = next.requireOperationAccessForPopulateQueries !== false;
3006
+ if ("requireOperationAccessForPopulateDependencies" in next)
3007
+ next.requireOperationAccessForPopulateDependencies = next.requireOperationAccessForPopulateDependencies !== false;
2972
3008
  this.globalOptions.assign(next);
2973
3009
  }
2974
3010
  setGlobalOption(key, value) {
2975
- if (key === "requireReadOperationAccessForPopulateDependencies") {
2976
- this.globalOptions.set("requireReadOperationAccessForPopulateDependencies", value !== false);
3011
+ if (key === "requireOperationAccessForPopulateQueries" || key === "requireOperationAccessForPopulateDependencies") {
3012
+ this.globalOptions.set(key, value !== false);
2977
3013
  return;
2978
3014
  }
2979
3015
  this.globalOptions.set(key, value);
@@ -3002,7 +3038,7 @@ var init_runtime = __esm({
3002
3038
  if (keyStr === "defaults") assertPublicDefaults(value);
3003
3039
  else if (keyStr.startsWith("defaults.")) {
3004
3040
  const proposed = {};
3005
- (0, import_utils16.set)(proposed, keyStr.slice("defaults.".length), value);
3041
+ (0, import_utils17.set)(proposed, keyStr.slice("defaults.".length), value);
3006
3042
  assertPublicDefaults(proposed);
3007
3043
  }
3008
3044
  this.defaultModelOptions.set(key, value);
@@ -3031,7 +3067,7 @@ var init_runtime = __esm({
3031
3067
  }).onchange("basePath", function(newval, key, target) {
3032
3068
  target[key] = normalizeBasePath(
3033
3069
  modelName,
3034
- (0, import_utils16.isString)(newval) || (0, import_utils16.isNil)(newval) ? newval : void 0
3070
+ (0, import_utils17.isString)(newval) || (0, import_utils17.isNil)(newval) ? newval : void 0
3035
3071
  );
3036
3072
  }).build();
3037
3073
  this.modelJsonSchemas[modelName] = model.jsonSchema();
@@ -3075,7 +3111,7 @@ var init_runtime = __esm({
3075
3111
  if (keyStr === "defaults") assertPublicDefaults(value);
3076
3112
  else if (keyStr.startsWith("defaults.")) {
3077
3113
  const proposed = {};
3078
- (0, import_utils16.set)(proposed, keyStr.slice("defaults.".length), value);
3114
+ (0, import_utils17.set)(proposed, keyStr.slice("defaults.".length), value);
3079
3115
  assertPublicDefaults(proposed);
3080
3116
  }
3081
3117
  const manager = this.modelOptions[modelName];
@@ -3096,15 +3132,15 @@ var init_runtime = __esm({
3096
3132
  const segments = nestedPath.split(".");
3097
3133
  let cursor = cloned;
3098
3134
  for (let i = 0; i < segments.length - 1; i++) {
3099
- if (!(0, import_utils16.isPlainObject)(cursor)) break;
3135
+ if (!(0, import_utils17.isPlainObject)(cursor)) break;
3100
3136
  cursor = cursor[segments[i]];
3101
3137
  }
3102
- if ((0, import_utils16.isPlainObject)(cursor)) {
3138
+ if ((0, import_utils17.isPlainObject)(cursor)) {
3103
3139
  delete cursor[segments[segments.length - 1]];
3104
3140
  }
3105
3141
  proposedVirtuals = cloned;
3106
3142
  } else {
3107
- (0, import_utils16.set)(cloned, nestedPath, value);
3143
+ (0, import_utils17.set)(cloned, nestedPath, value);
3108
3144
  proposedVirtuals = cloned;
3109
3145
  }
3110
3146
  }
@@ -3205,7 +3241,7 @@ var init_runtime = __esm({
3205
3241
  manager.onchange("basePath", function(newval, key, target) {
3206
3242
  target[key] = normalizeBasePath(
3207
3243
  dataName,
3208
- (0, import_utils16.isString)(newval) || (0, import_utils16.isNil)(newval) ? newval : void 0
3244
+ (0, import_utils17.isString)(newval) || (0, import_utils17.isNil)(newval) ? newval : void 0
3209
3245
  );
3210
3246
  }).build();
3211
3247
  return manager;
@@ -3265,20 +3301,20 @@ var init_runtime = __esm({
3265
3301
  const schema = model.schema;
3266
3302
  this.modelRefs[modelName] = buildRefs(schema.tree);
3267
3303
  this.modelSubs[modelName] = buildSubPaths(schema.tree);
3268
- this.modelAtts[modelName] = (0, import_utils16.keys)(schema.obj);
3304
+ this.modelAtts[modelName] = (0, import_utils17.keys)(schema.obj);
3269
3305
  }
3270
3306
  getModelRef(modelName, refPath) {
3271
3307
  this.ensureModelMeta(modelName);
3272
- const value = (0, import_utils16.get)(this.modelRefs, `${modelName}.${refPath}`, null);
3273
- return (0, import_utils16.isString)(value) ? value : null;
3308
+ const value = (0, import_utils17.get)(this.modelRefs, `${modelName}.${refPath}`, null);
3309
+ return (0, import_utils17.isString)(value) ? value : null;
3274
3310
  }
3275
3311
  getModelSub(modelName) {
3276
3312
  this.ensureModelMeta(modelName);
3277
- return (0, import_utils16.get)(this.modelSubs, modelName, []);
3313
+ return (0, import_utils17.get)(this.modelSubs, modelName, []);
3278
3314
  }
3279
3315
  getModelAtt(modelName) {
3280
3316
  this.ensureModelMeta(modelName);
3281
- return (0, import_utils16.get)(this.modelAtts, modelName, []);
3317
+ return (0, import_utils17.get)(this.modelAtts, modelName, []);
3282
3318
  }
3283
3319
  };
3284
3320
  defaultRuntime = new AccessRuntime({ allowGlobalModelLookup: true });
@@ -3454,10 +3490,10 @@ var init_options = __esm({
3454
3490
  });
3455
3491
 
3456
3492
  // src/permission.ts
3457
- var import_utils17, Permission, permission_default;
3493
+ var import_utils18, Permission, permission_default;
3458
3494
  var init_permission = __esm({
3459
3495
  "src/permission.ts"() {
3460
- import_utils17 = require("@web-ts-toolkit/utils");
3496
+ import_utils18 = require("@web-ts-toolkit/utils");
3461
3497
  Permission = class {
3462
3498
  constructor(permissions) {
3463
3499
  this.permissions = { ...permissions };
@@ -3473,7 +3509,7 @@ var init_permission = __esm({
3473
3509
  return [permissionOrPermissions].concat(permissions);
3474
3510
  }
3475
3511
  hasKey(permission) {
3476
- return (0, import_utils17.hasOwn)(this.permissions, permission);
3512
+ return (0, import_utils18.hasOwn)(this.permissions, permission);
3477
3513
  }
3478
3514
  has(permission) {
3479
3515
  return this.permissions[permission] === true;
@@ -3507,6 +3543,47 @@ function serviceAuthorizationInput(policy, targets) {
3507
3543
  }
3508
3544
  return { rows: policy.rows, outputFields: policy.outputFields, related };
3509
3545
  }
3546
+ function hasStableConfigurationCopies(value) {
3547
+ if (value === null || typeof value !== "object") return true;
3548
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return false;
3549
+ return Object.values(value).every(hasStableConfigurationCopies);
3550
+ }
3551
+ function captureAuthorizationAccess(value) {
3552
+ const input = authorizationRecord(value, "access");
3553
+ const access = {};
3554
+ for (const key of ["operation", "rowAccess", "virtualAccess", "outputAccess", "docPermissionsAccess"]) {
3555
+ if (key === "rowAccess" && input[key] === void 0) continue;
3556
+ if (typeof input[key] !== "string") authorizationError(`invalid access.${key}`);
3557
+ access[key] = input[key];
3558
+ }
3559
+ return Object.freeze(access);
3560
+ }
3561
+ function captureAuthorizationCatalog(value) {
3562
+ const input = authorizationRecord(value, "catalog");
3563
+ const catalog = {};
3564
+ for (const key of [
3565
+ "paths",
3566
+ "persistedPaths",
3567
+ "arrayPaths",
3568
+ "schemaVirtualPaths",
3569
+ "metadataPaths",
3570
+ "dynamicPaths",
3571
+ "containerPaths",
3572
+ "virtualPaths",
3573
+ "virtualScopePaths",
3574
+ "opaquePaths",
3575
+ "referencePaths",
3576
+ "protectedPaths"
3577
+ ]) {
3578
+ const paths = input[key];
3579
+ if (key !== "paths" && paths === void 0) continue;
3580
+ if (!Array.isArray(paths) || Array.from(paths).some((path) => typeof path !== "string")) {
3581
+ authorizationError(`invalid catalog.${key}`);
3582
+ }
3583
+ catalog[key] = paths;
3584
+ }
3585
+ return Object.freeze(catalog);
3586
+ }
3510
3587
  function assertAuthorizationPath(path) {
3511
3588
  if (typeof path !== "string" || !isSafeOutputPath(path)) {
3512
3589
  authorizationError(`invalid response path: ${String(path)}`);
@@ -3591,7 +3668,6 @@ function normalizePolicy(value, applicability, configuration, targets) {
3591
3668
  const matches = targets.filter((target2) => target2.path === path);
3592
3669
  if (matches.length !== 1) authorizationError(`unused or ambiguous related path: ${path}`);
3593
3670
  const target = matches[0];
3594
- if (target.kind === "passthrough") authorizationError(`related policy requires a registered target: ${path}`);
3595
3671
  const child = authorizationRecord(entry, `related.${path}`);
3596
3672
  assertAuthorizationKeys(
3597
3673
  child,
@@ -3637,6 +3713,9 @@ function resolveServiceAuthorization(input) {
3637
3713
  const raw = copyAuthorizationValue(input.authorization);
3638
3714
  const configuration = copyAuthorizationValue(input.configuration, true);
3639
3715
  const targets = copyAuthorizationValue(input.relatedTargets ?? [], true);
3716
+ const access = copyAuthorizationValue(input.access);
3717
+ const outputAccess = captureAuthorizationAccess(access);
3718
+ const catalog = captureAuthorizationCatalog(configuration.catalog);
3640
3719
  const policy = copyAuthorizationValue(normalizePolicy(raw, input.applicability ?? "service", configuration, targets));
3641
3720
  const scopePath = Array.from(scope(input.binding));
3642
3721
  for (const part of scopePath) {
@@ -3649,17 +3728,21 @@ function resolveServiceAuthorization(input) {
3649
3728
  authorizationError("derived call request/runtime mismatch");
3650
3729
  }
3651
3730
  }
3652
- const permissions = Object.fromEntries(
3653
- input.requestPermissions.keys.map((key) => [key, input.requestPermissions.has(key)])
3731
+ const permissions = new permission_default(
3732
+ Object.fromEntries(input.requestPermissions.keys.map((key) => [key, input.requestPermissions.has(key) === true]))
3654
3733
  );
3734
+ for (const value of Object.values(permissions)) if (value && typeof value === "object") Object.freeze(value);
3735
+ Object.freeze(permissions);
3655
3736
  const call = Object.freeze(/* @__PURE__ */ Object.create(null));
3656
3737
  calls.set(call, {
3657
3738
  binding: Object.freeze({ ...input.binding, scopePath: Object.freeze(scopePath) }),
3658
- access: copyAuthorizationValue(input.access),
3739
+ access,
3659
3740
  configuration,
3660
3741
  policy,
3661
3742
  relatedTargets: targets,
3662
- permissions: Object.freeze(permissions),
3743
+ // normalizeFields constructs only mode strings and validated string paths.
3744
+ output: Object.freeze({ access: outputAccess, catalog, outputFields: policy.outputFields }),
3745
+ permissions,
3663
3746
  provenance: input.parent ? callRecord(input.parent).provenance : call
3664
3747
  });
3665
3748
  return call;
@@ -3677,12 +3760,75 @@ function readServiceAuthorization(call, binding) {
3677
3760
  true
3678
3761
  );
3679
3762
  }
3763
+ function readServiceAuthorizationAccess(call, binding) {
3764
+ assertAuthorizationBinding(call, binding);
3765
+ return callRecord(call).output.access;
3766
+ }
3767
+ function readServiceAuthorizationHydration(call, binding) {
3768
+ assertAuthorizationBinding(call, binding);
3769
+ return copyAuthorizationValue({ hydration: callRecord(call).configuration.hydration }, true);
3770
+ }
3771
+ function outputMetadata(configuration) {
3772
+ return {
3773
+ documentPermissionField: configuration.documentPermissionField,
3774
+ exposedDocPermissionKeys: configuration.exposedDocPermissionKeys,
3775
+ stripPermissionsField: configuration.stripPermissionsField,
3776
+ disableFieldPermissions: configuration.disableFieldPermissions
3777
+ };
3778
+ }
3779
+ function readServiceOutputMetadata(call, binding) {
3780
+ assertAuthorizationBinding(call, binding);
3781
+ return copyAuthorizationValue(outputMetadata(callRecord(call).configuration), true);
3782
+ }
3783
+ function readServiceDocumentPermissions(call, binding) {
3784
+ assertAuthorizationBinding(call, binding);
3785
+ const record3 = callRecord(call);
3786
+ return Object.freeze({
3787
+ catalog: record3.output.catalog,
3788
+ ...copyAuthorizationValue({ docPermissions: record3.configuration.docPermissions }, true)
3789
+ });
3790
+ }
3791
+ function readServiceFinalizerConfiguration(call, binding) {
3792
+ assertAuthorizationBinding(call, binding);
3793
+ const record3 = callRecord(call);
3794
+ return Object.freeze({
3795
+ catalog: record3.output.catalog,
3796
+ ...copyAuthorizationValue(
3797
+ {
3798
+ ...outputMetadata(record3.configuration),
3799
+ virtuals: record3.configuration.virtuals,
3800
+ docPermissions: record3.configuration.docPermissions
3801
+ },
3802
+ true
3803
+ )
3804
+ });
3805
+ }
3806
+ function readServiceOutputPolicy(call, binding) {
3807
+ assertAuthorizationBinding(call, binding);
3808
+ return callRecord(call).output;
3809
+ }
3810
+ function readServiceOutputFieldAuthorization(call, binding) {
3811
+ assertAuthorizationBinding(call, binding);
3812
+ const record3 = callRecord(call);
3813
+ const reusable = record3.reusableFieldConfiguration ??= hasStableConfigurationCopies(record3.configuration.permissionSchema) && // Coercion methods can observe an object's identity even without native
3814
+ // leaves. Keep those receivers local to the original resolution boundary.
3815
+ (record3.configuration.modelPermissionPrefix === null || typeof record3.configuration.modelPermissionPrefix !== "object");
3816
+ return Object.freeze({
3817
+ access: record3.output.access,
3818
+ outputFields: record3.output.outputFields,
3819
+ reusable,
3820
+ ...copyAuthorizationValue(
3821
+ {
3822
+ permissionSchema: record3.configuration.permissionSchema,
3823
+ modelPermissionPrefix: record3.configuration.modelPermissionPrefix
3824
+ },
3825
+ true
3826
+ )
3827
+ });
3828
+ }
3680
3829
  function getAuthorizationPermissions(call, binding) {
3681
3830
  assertAuthorizationBinding(call, binding);
3682
- const permission = new permission_default({ ...callRecord(call).permissions });
3683
- for (const value of Object.values(permission)) if (value && typeof value === "object") Object.freeze(value);
3684
- Object.freeze(permission);
3685
- return permission;
3831
+ return callRecord(call).permissions;
3686
3832
  }
3687
3833
  function getAuthorizationProvenance(call) {
3688
3834
  return callRecord(call).provenance;
@@ -3721,7 +3867,7 @@ function normalizeOutputAuthorizationOptions(input, options, purpose) {
3721
3867
  const stage = stageRecord(documentRecord(handle).stage);
3722
3868
  const binding = Object.freeze({ ...input.binding, scopePath: stage.binding.scopePath });
3723
3869
  assertAuthorizationBinding(stage.call, binding);
3724
- const access = readServiceAuthorization(stage.call, binding).access;
3870
+ const access = readServiceAuthorizationAccess(stage.call, binding);
3725
3871
  if (input.access.outputAccess !== access.outputAccess) authorizationError("document access mismatch");
3726
3872
  return Object.freeze({ kind: "document", documentAuthorization: handle, binding });
3727
3873
  }
@@ -3799,7 +3945,16 @@ function readDocumentAuthorization(handle, binding) {
3799
3945
  authorization: stage.call,
3800
3946
  stage: record3.stage,
3801
3947
  grants: copyAuthorizationValue(record3.grants),
3802
- ...readServiceAuthorization(stage.call, binding).access
3948
+ ...readServiceAuthorizationAccess(stage.call, binding)
3949
+ });
3950
+ }
3951
+ function readDocumentAuthorizationAccess(handle, binding) {
3952
+ const record3 = documentRecord(handle);
3953
+ const stage = stageRecord(record3.stage);
3954
+ return Object.freeze({
3955
+ authorization: stage.call,
3956
+ stage: record3.stage,
3957
+ ...readServiceAuthorizationAccess(stage.call, binding)
3803
3958
  });
3804
3959
  }
3805
3960
  function memoizeDocumentFieldDecision(handle, binding, field, evaluate) {
@@ -3807,15 +3962,23 @@ function memoizeDocumentFieldDecision(handle, binding, field, evaluate) {
3807
3962
  const stage = stageRecord(record3.stage);
3808
3963
  assertAuthorizationBinding(stage.call, binding);
3809
3964
  let decision = record3.decisions.get(field);
3810
- if (!decision) {
3965
+ if (decision) {
3966
+ return decision.then((result2) => {
3967
+ stageRecord(record3.stage);
3968
+ assertAuthorizationBinding(stage.call, binding);
3969
+ return result2;
3970
+ });
3971
+ } else {
3811
3972
  decision = Promise.resolve().then(async () => {
3812
3973
  stageRecord(record3.stage);
3974
+ assertAuthorizationBinding(stage.call, binding);
3813
3975
  const result2 = await evaluate.call(
3814
3976
  stage.binding.request,
3815
3977
  getAuthorizationPermissions(stage.call, binding),
3816
3978
  copyAuthorizationValue(record3.grants)
3817
3979
  );
3818
3980
  stageRecord(record3.stage);
3981
+ assertAuthorizationBinding(stage.call, binding);
3819
3982
  if (typeof result2 !== "boolean") authorizationError("field decision must be boolean");
3820
3983
  return result2;
3821
3984
  });
@@ -3832,7 +3995,7 @@ function assertDocumentAuthorization(handle, use) {
3832
3995
  const record3 = documentRecord(handle);
3833
3996
  const stage = stageRecord(record3.stage);
3834
3997
  assertAuthorizationBinding(stage.call, use.binding);
3835
- if (use.authorization !== void 0 && stage.call !== use.authorization || use.stage !== void 0 && record3.stage !== use.stage || readServiceAuthorization(stage.call, use.binding).access.outputAccess !== use.outputAccess) {
3998
+ 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) {
3836
3999
  authorizationError("document call/access/stage mismatch");
3837
4000
  }
3838
4001
  const association = outputs.get(use.output);
@@ -3887,7 +4050,7 @@ function transferDocumentAuthorizationGraph(source, copy, copyOrigins) {
3887
4050
  {
3888
4051
  binding: stage.binding,
3889
4052
  output: original,
3890
- outputAccess: readServiceAuthorization(stage.call, stage.binding).access.outputAccess,
4053
+ outputAccess: readServiceAuthorizationAccess(stage.call, stage.binding).outputAccess,
3891
4054
  authorization: stage.call,
3892
4055
  stage: documentRecord(association.handle).stage
3893
4056
  },
@@ -3960,7 +4123,7 @@ function attachIncludeOutput(doc, path, value) {
3960
4123
  const state = stateFor(doc);
3961
4124
  if (!state) throw new Error("Include attachment requires a prepared output row");
3962
4125
  state.attachments.push({ path, value });
3963
- (0, import_utils18.set)(doc, path, deepIsolateValue(value));
4126
+ (0, import_utils19.set)(doc, path, deepIsolateValue(value));
3964
4127
  }
3965
4128
  function getIncludeSource(doc) {
3966
4129
  return stateFor(doc)?.source ?? doc;
@@ -3975,14 +4138,14 @@ function restoreIncludeOutput(doc, output) {
3975
4138
  for (const { path, value } of attachments) {
3976
4139
  const copy = deepIsolateValue(value);
3977
4140
  if (copy !== null && typeof copy === "object") origins.set(copy, value);
3978
- (0, import_utils18.set)(output, path, copy);
4141
+ (0, import_utils19.set)(output, path, copy);
3979
4142
  }
3980
4143
  return origins;
3981
4144
  }
3982
- var import_utils18, outputs2, stateFor;
4145
+ var import_utils19, outputs2, stateFor;
3983
4146
  var init_include_output = __esm({
3984
4147
  "src/output/include-output.ts"() {
3985
- import_utils18 = require("@web-ts-toolkit/utils");
4148
+ import_utils19 = require("@web-ts-toolkit/utils");
3986
4149
  init_document_authorization();
3987
4150
  init_document();
3988
4151
  outputs2 = /* @__PURE__ */ new WeakMap();
@@ -4052,12 +4215,20 @@ function classifyRequestFieldRule(rule, permissions, modelPermissionPrefix = "")
4052
4215
  );
4053
4216
  return (typeof rule === "string" ? possible.stringHandler(rule) : possible.arrayHandler(rule)) ? "deferred" : "denied";
4054
4217
  }
4218
+ function prepareRequestFieldRule(rule, permissions, modelPermissionPrefix = "") {
4219
+ const state = classifyRequestFieldRule(rule, permissions, modelPermissionPrefix);
4220
+ const invariant = state === "denied" && modelPermissionPrefix ? classifyRequestFieldRule(rule, permissions) : state;
4221
+ return Object.freeze({ state, decision: invariant === "deferred" ? void 0 : invariant === "granted" });
4222
+ }
4223
+ function combineFieldAuthorizationStates(states2) {
4224
+ return states2.includes("denied") ? "denied" : states2.includes("deferred") ? "deferred" : "granted";
4225
+ }
4055
4226
  function classifyEffectiveFieldAuthorization(args) {
4056
4227
  if (hasExplicitFieldGrant(args.outputFields, args.path)) return "granted";
4057
4228
  const states2 = args.rules.map(
4058
4229
  (rule) => classifyRequestFieldRule(rule, args.globalPermissions, args.modelPermissionPrefix)
4059
4230
  );
4060
- return states2.includes("denied") ? "denied" : states2.includes("deferred") ? "deferred" : "granted";
4231
+ return combineFieldAuthorizationStates(states2);
4061
4232
  }
4062
4233
  async function evaluateScopedAccessRule(args) {
4063
4234
  const { req, rule, globalPermissions, docPermissions, modelPermissionPrefix } = args;
@@ -4324,10 +4495,10 @@ function planVirtualProjection(args) {
4324
4495
  relatedPlans
4325
4496
  };
4326
4497
  }
4327
- var import_utils20, getScopedPermissionSchema, resolveAlwaysSelectForAccess, stripVirtualsFromNormalizedSelect, snapshotVirtualPaths, stripLeadingDash, dbPrefixForScope, toDbPath;
4498
+ var import_utils21, getScopedPermissionSchema, resolveAlwaysSelectForAccess, stripVirtualsFromNormalizedSelect, snapshotVirtualPaths, stripLeadingDash, dbPrefixForScope, toDbPath;
4328
4499
  var init_virtual_projection = __esm({
4329
4500
  "src/acl/virtual-projection.ts"() {
4330
- import_utils20 = require("@web-ts-toolkit/utils");
4501
+ import_utils21 = require("@web-ts-toolkit/utils");
4331
4502
  init_query();
4332
4503
  init_field_authorization();
4333
4504
  init_output_projection();
@@ -4335,20 +4506,20 @@ var init_virtual_projection = __esm({
4335
4506
  init_virtual_populate_dependencies();
4336
4507
  init_virtual_descriptor();
4337
4508
  getScopedPermissionSchema = (root, scopePath) => {
4338
- if (!root || !(0, import_utils20.isPlainObject)(root)) return null;
4509
+ if (!root || !(0, import_utils21.isPlainObject)(root)) return null;
4339
4510
  if (scopePath.length === 0) return root;
4340
- const scoped = (0, import_utils20.get)(root, scopePath.join("."));
4341
- return (0, import_utils20.isPlainObject)(scoped) ? scoped : null;
4511
+ const scoped = (0, import_utils21.get)(root, scopePath.join("."));
4512
+ return (0, import_utils21.isPlainObject)(scoped) ? scoped : null;
4342
4513
  };
4343
4514
  resolveAlwaysSelectForAccess = (raw, access) => {
4344
4515
  if (!raw) return [];
4345
- if ((0, import_utils20.isArray)(raw)) return [...raw];
4346
- if ((0, import_utils20.isPlainObject)(raw)) {
4516
+ if ((0, import_utils21.isArray)(raw)) return [...raw];
4517
+ if ((0, import_utils21.isPlainObject)(raw)) {
4347
4518
  const record3 = raw;
4348
4519
  const exact = record3[access];
4349
- if ((0, import_utils20.isArray)(exact)) return [...exact];
4520
+ if ((0, import_utils21.isArray)(exact)) return [...exact];
4350
4521
  const def = record3.default;
4351
- if ((0, import_utils20.isArray)(def)) return [...def];
4522
+ if ((0, import_utils21.isArray)(def)) return [...def];
4352
4523
  return [];
4353
4524
  }
4354
4525
  return [];
@@ -4613,8 +4784,8 @@ var init_output_projection = __esm({
4613
4784
 
4614
4785
  // src/acl/model-output-planning.ts
4615
4786
  function getCapturedAuthorizationOption(configuration, key) {
4616
- const defaultValue = key.startsWith("operationAccess.") ? (0, import_utils21.get)(configuration.operationAccessDefaults, key.slice("operationAccess.".length)) : void 0;
4617
- return resolveNestedOption((path, fallback) => (0, import_utils21.get)(configuration, path, fallback), key, defaultValue);
4787
+ const defaultValue = key.startsWith("operationAccess.") ? (0, import_utils22.get)(configuration.operationAccessDefaults, key.slice("operationAccess.".length)) : void 0;
4788
+ return resolveNestedOption((path, fallback) => (0, import_utils22.get)(configuration, path, fallback), key, defaultValue);
4618
4789
  }
4619
4790
  function captureModelAuthorizationConfiguration(runtime, modelName, options = runtime.getModelOptions(modelName), globalOptions = runtime.getGlobalOptions()) {
4620
4791
  const model = runtime.getModelInstance(modelName);
@@ -4629,9 +4800,9 @@ function captureModelAuthorizationConfiguration(runtime, modelName, options = ru
4629
4800
  options,
4630
4801
  globalOptions
4631
4802
  ),
4632
- requireReadOperationAccessForPopulateDependencies: globalOptions.requireReadOperationAccessForPopulateDependencies !== false,
4633
- requireRegisteredPopulateModels: globalOptions.requireRegisteredPopulateModels,
4634
- permissionSchema: options.permissionSchema,
4803
+ requireOperationAccessForPopulateDependencies: globalOptions.requireOperationAccessForPopulateDependencies !== false,
4804
+ requireOperationAccessForPopulateQueries: globalOptions.requireOperationAccessForPopulateQueries !== false,
4805
+ permissionSchema: options.permissionSchema === void 0 ? runtime.getDefaultModelOption("permissionSchema") : options.permissionSchema,
4635
4806
  virtuals,
4636
4807
  alwaysSelectFields: options.alwaysSelectFields,
4637
4808
  modelPermissionPrefix: options.modelPermissionPrefix,
@@ -4677,10 +4848,10 @@ function planModelOutputForHelper(args) {
4677
4848
  requestedSelect: args.select
4678
4849
  });
4679
4850
  }
4680
- var import_utils21;
4851
+ var import_utils22;
4681
4852
  var init_model_output_planning = __esm({
4682
4853
  "src/acl/model-output-planning.ts"() {
4683
- import_utils21 = require("@web-ts-toolkit/utils");
4854
+ import_utils22 = require("@web-ts-toolkit/utils");
4684
4855
  init_manager();
4685
4856
  init_field_catalog();
4686
4857
  init_authorization_snapshot();
@@ -4692,10 +4863,10 @@ var init_model_output_planning = __esm({
4692
4863
 
4693
4864
  // src/core-shared.ts
4694
4865
  function isAndFilter(filter2) {
4695
- return !!filter2 && (0, import_utils22.isPlainObject)(filter2) && Object.keys(filter2).length === 1 && (0, import_utils22.isArray)(filter2.$and);
4866
+ return !!filter2 && (0, import_utils23.isPlainObject)(filter2) && Object.keys(filter2).length === 1 && (0, import_utils23.isArray)(filter2.$and);
4696
4867
  }
4697
4868
  function isMergeableClause(filter2) {
4698
- return !!filter2 && (0, import_utils22.isPlainObject)(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
4869
+ return !!filter2 && (0, import_utils23.isPlainObject)(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
4699
4870
  }
4700
4871
  function optimizeAndFilter(clausesInput) {
4701
4872
  const clauses = [];
@@ -4712,11 +4883,11 @@ function optimizeAndFilter(clausesInput) {
4712
4883
  const dedupeKey = (value) => {
4713
4884
  if (value instanceof RegExp) return `regexp:${JSON.stringify([value.source, value.flags])}`;
4714
4885
  if (value instanceof Date) return `date:${value.getTime()}`;
4715
- if ((0, import_utils22.isArray)(value)) {
4886
+ if ((0, import_utils23.isArray)(value)) {
4716
4887
  const entries = Array.from(value, dedupeKey);
4717
4888
  return entries.every((entry) => entry !== void 0) ? `array:${JSON.stringify(entries)}` : void 0;
4718
4889
  }
4719
- if ((0, import_utils22.isPlainObject)(value)) {
4890
+ if ((0, import_utils23.isPlainObject)(value)) {
4720
4891
  const entries = Object.keys(value).sort().map((key) => [key, dedupeKey(value[key])]);
4721
4892
  return entries.every(([, entry]) => entry !== void 0) ? `object:${JSON.stringify(entries)}` : void 0;
4722
4893
  }
@@ -4746,7 +4917,7 @@ function optimizeAndFilter(clausesInput) {
4746
4917
  }
4747
4918
  let canMerge = true;
4748
4919
  for (const [key, value] of Object.entries(clause)) {
4749
- if ((0, import_utils22.hasOwn)(mergedClause, key) && !(0, import_node_util4.isDeepStrictEqual)(mergedClause[key], value)) {
4920
+ if ((0, import_utils23.hasOwn)(mergedClause, key) && !(0, import_node_util4.isDeepStrictEqual)(mergedClause[key], value)) {
4750
4921
  canMerge = false;
4751
4922
  break;
4752
4923
  }
@@ -4757,24 +4928,24 @@ function optimizeAndFilter(clausesInput) {
4757
4928
  }
4758
4929
  Object.assign(mergedClause, clause);
4759
4930
  }
4760
- const finalClauses = (0, import_utils22.isEmpty)(mergedClause) ? remainingClauses : [mergedClause, ...remainingClauses];
4931
+ const finalClauses = (0, import_utils23.isEmpty)(mergedClause) ? remainingClauses : [mergedClause, ...remainingClauses];
4761
4932
  if (finalClauses.length === 0) return null;
4762
4933
  if (finalClauses.length === 1) return finalClauses[0];
4763
4934
  return { $and: finalClauses };
4764
4935
  }
4765
4936
  function normalizeFilter(filter2) {
4766
4937
  if (filter2 === false) return false;
4767
- if (!filter2 || !(0, import_utils22.isPlainObject)(filter2) || (0, import_utils22.isEmpty)(filter2)) return null;
4938
+ if (!filter2 || !(0, import_utils23.isPlainObject)(filter2) || (0, import_utils23.isEmpty)(filter2)) return null;
4768
4939
  if (!isAndFilter(filter2)) {
4769
4940
  return filter2;
4770
4941
  }
4771
4942
  return optimizeAndFilter(filter2.$and);
4772
4943
  }
4773
4944
  async function resolveIdentifierFilter(req, idField, resolveIdFilter, id) {
4774
- if ((0, import_utils22.isFunction)(resolveIdFilter)) {
4945
+ if ((0, import_utils23.isFunction)(resolveIdFilter)) {
4775
4946
  return await resolveIdFilter.call(req, id);
4776
4947
  }
4777
- if ((0, import_utils22.isString)(idField)) {
4948
+ if ((0, import_utils23.isString)(idField)) {
4778
4949
  return { [idField]: id };
4779
4950
  }
4780
4951
  return { _id: id };
@@ -4798,7 +4969,7 @@ async function resolveAccessFilterParts({
4798
4969
  }
4799
4970
  const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
4800
4971
  const baseFilterFn = rows2.mode === "request" ? getOption(`baseFilter.${access}`, null) : null;
4801
- if (nextFilter !== false && (0, import_utils22.isFunction)(overrideFilterFn)) {
4972
+ if (nextFilter !== false && (0, import_utils23.isFunction)(overrideFilterFn)) {
4802
4973
  nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
4803
4974
  }
4804
4975
  if (nextFilter === false) return { transformed: false, scope: {}, filter: false };
@@ -4806,7 +4977,7 @@ async function resolveAccessFilterParts({
4806
4977
  const scope2 = rows2.mode === "scope" ? rows2.filter : {};
4807
4978
  return { transformed: nextFilter ?? {}, scope: scope2, filter: intersectFilters(nextFilter, scope2) };
4808
4979
  }
4809
- if (!(0, import_utils22.isFunction)(baseFilterFn)) return { transformed: nextFilter ?? {}, scope: {}, filter: nextFilter ?? {} };
4980
+ if (!(0, import_utils23.isFunction)(baseFilterFn)) return { transformed: nextFilter ?? {}, scope: {}, filter: nextFilter ?? {} };
4810
4981
  const cached = cache.get(cacheKey);
4811
4982
  const reusable = cached?.resolver === baseFilterFn;
4812
4983
  const baseFilter = normalizeFilter(
@@ -4841,34 +5012,34 @@ async function setRequestPermissions(req) {
4841
5012
  if (owner === activeRuntime) return;
4842
5013
  if (hasFieldValue && owner === void 0) return;
4843
5014
  const globalPermissions = getGlobalOption("globalPermissions");
4844
- const permissions = (0, import_utils22.isFunction)(globalPermissions) ? await globalPermissions.call(req, req) : void 0;
4845
- if ((0, import_utils22.isPlainObject)(permissions)) req[requestPermissionField] = permissions;
4846
- else if ((0, import_utils22.isArray)(permissions)) req[requestPermissionField] = (0, import_utils22.arrayToRecord)(permissions);
4847
- else if ((0, import_utils22.isString)(permissions)) req[requestPermissionField] = { [permissions]: true };
5015
+ const permissions = (0, import_utils23.isFunction)(globalPermissions) ? await globalPermissions.call(req, req) : void 0;
5016
+ if ((0, import_utils23.isPlainObject)(permissions)) req[requestPermissionField] = permissions;
5017
+ else if ((0, import_utils23.isArray)(permissions)) req[requestPermissionField] = (0, import_utils23.arrayToRecord)(permissions);
5018
+ else if ((0, import_utils23.isString)(permissions)) req[requestPermissionField] = { [permissions]: true };
4848
5019
  else req[requestPermissionField] = {};
4849
5020
  owners.set(requestPermissionField, activeRuntime);
4850
5021
  }
4851
5022
  async function evaluateRouteGuard(req, permissions, routeGuard) {
4852
5023
  const phas = (key) => permissions.has(key);
4853
5024
  const { stringHandler, arrayHandler } = createValidator(phas);
4854
- if ((0, import_utils22.isBoolean)(routeGuard)) {
5025
+ if ((0, import_utils23.isBoolean)(routeGuard)) {
4855
5026
  return routeGuard === true;
4856
5027
  }
4857
- if ((0, import_utils22.isString)(routeGuard)) {
5028
+ if ((0, import_utils23.isString)(routeGuard)) {
4858
5029
  return stringHandler(routeGuard);
4859
5030
  }
4860
- if ((0, import_utils22.isArray)(routeGuard)) {
5031
+ if ((0, import_utils23.isArray)(routeGuard)) {
4861
5032
  return arrayHandler(routeGuard);
4862
5033
  }
4863
- if ((0, import_utils22.isFunction)(routeGuard)) {
5034
+ if ((0, import_utils23.isFunction)(routeGuard)) {
4864
5035
  return routeGuard.call(req, permissions);
4865
5036
  }
4866
5037
  return false;
4867
5038
  }
4868
5039
  async function callHookChain(req, hook, doc, permissions, context) {
4869
- const hooks = (0, import_utils22.castArray)(hook);
5040
+ const hooks = (0, import_utils23.castArray)(hook);
4870
5041
  for (let x = 0; x < hooks.length; x++) {
4871
- if ((0, import_utils22.isFunction)(hooks[x])) {
5042
+ if ((0, import_utils23.isFunction)(hooks[x])) {
4872
5043
  doc = await hooks[x].call(req, doc, permissions, context);
4873
5044
  }
4874
5045
  }
@@ -4888,22 +5059,22 @@ async function collectSchemaFields({
4888
5059
  for (let x = 0; x < keys2.length; x++) {
4889
5060
  const key = keys2[x];
4890
5061
  const value = extractScopedOutputRule(permissionSchema, key, access);
4891
- if ((0, import_utils22.isBoolean)(value)) {
5062
+ if ((0, import_utils23.isBoolean)(value)) {
4892
5063
  if (value) fields.push(key);
4893
- } else if ((0, import_utils22.isString)(value)) {
5064
+ } else if ((0, import_utils23.isString)(value)) {
4894
5065
  if (stringHandler(value)) fields.push(key);
4895
- } else if ((0, import_utils22.isArray)(value)) {
5066
+ } else if ((0, import_utils23.isArray)(value)) {
4896
5067
  if (arrayHandler(value)) fields.push(key);
4897
- } else if ((0, import_utils22.isFunction)(value)) {
5068
+ } else if ((0, import_utils23.isFunction)(value)) {
4898
5069
  if (await value.call(req, ...functionArgs)) fields.push(key);
4899
5070
  }
4900
5071
  }
4901
5072
  return fields;
4902
5073
  }
4903
- var import_utils22, import_node_util4, permissionFieldOwners;
5074
+ var import_utils23, import_node_util4, permissionFieldOwners;
4904
5075
  var init_core_shared = __esm({
4905
5076
  "src/core-shared.ts"() {
4906
- import_utils22 = require("@web-ts-toolkit/utils");
5077
+ import_utils23 = require("@web-ts-toolkit/utils");
4907
5078
  import_node_util4 = require("util");
4908
5079
  init_helpers();
4909
5080
  init_options();
@@ -5272,8 +5443,8 @@ function equivalentVirtualPopulatePredicate(left, right) {
5272
5443
  }
5273
5444
  if (Array.isArray(left) || Array.isArray(right))
5274
5445
  return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((entry, index) => equivalentVirtualPopulatePredicate(entry, right[index]));
5275
- if ((0, import_utils26.isPlainObject)(left) || (0, import_utils26.isPlainObject)(right)) {
5276
- if (!(0, import_utils26.isPlainObject)(left) || !(0, import_utils26.isPlainObject)(right)) return false;
5446
+ if ((0, import_utils27.isPlainObject)(left) || (0, import_utils27.isPlainObject)(right)) {
5447
+ if (!(0, import_utils27.isPlainObject)(left) || !(0, import_utils27.isPlainObject)(right)) return false;
5277
5448
  const a = Object.keys(left), b = Object.keys(right);
5278
5449
  return a.length === b.length && a.every(
5279
5450
  (key, index) => key === b[index] && equivalentVirtualPopulatePredicate(
@@ -5344,7 +5515,6 @@ function captureVirtualPopulateRows(batch, inputs, plan, publicPopulate = [], so
5344
5515
  address,
5345
5516
  plan: currentPlan,
5346
5517
  refs: /* @__PURE__ */ new Map(),
5347
- referenceTargets: /* @__PURE__ */ new Map(),
5348
5518
  dependencies: /* @__PURE__ */ new Map()
5349
5519
  };
5350
5520
  row.occurrences.set(addressKey(address), occurrence);
@@ -5352,7 +5522,6 @@ function captureVirtualPopulateRows(batch, inputs, plan, publicPopulate = [], so
5352
5522
  const dependencies = (candidate.populateDependencies ?? []).map((dependency) => {
5353
5523
  if (!occurrence.refs.has(dependency.path))
5354
5524
  occurrence.refs.set(dependency.path, originalRef(value, dependency.path));
5355
- occurrence.referenceTargets.set(dependency.path, dependency.target);
5356
5525
  const request = {
5357
5526
  binding: dependency.target,
5358
5527
  target: dependency.target,
@@ -5450,7 +5619,7 @@ async function execute(state) {
5450
5619
  if (!pending) {
5451
5620
  pending = (async () => {
5452
5621
  try {
5453
- if (target.configuration.requireReadOperationAccessForPopulateDependencies !== false && !await targetReadAllowed(state, target))
5622
+ if (target.configuration.requireOperationAccessForPopulateDependencies !== false && !await targetReadAllowed(state, target))
5454
5623
  return void 0;
5455
5624
  const parts = await work.hookGate.run(
5456
5625
  () => resolveAccessFilterParts({
@@ -5536,12 +5705,6 @@ function readVirtualPopulateOriginalReference(row, address, path) {
5536
5705
  function hasVirtualPopulatePublicReference(row, address, path) {
5537
5706
  return !!rows.get(row)?.publicRequests.some((request) => addressKey(request.address) === addressKey([...address, path]));
5538
5707
  }
5539
- function isVirtualPopulateReferenceVisible(row, address, path) {
5540
- const record3 = rows.get(row);
5541
- const occurrence = record3?.occurrences.get(addressKey(address));
5542
- const target = occurrence?.refs.get(path)?.present ? occurrence.referenceTargets.get(path) : void 0;
5543
- return record3 && target ? targetReadAllowed(record3.batch, target) : Promise.resolve(true);
5544
- }
5545
5708
  function readVirtualPopulateInputs(row, address, virtualName) {
5546
5709
  const record3 = rows.get(row);
5547
5710
  if (!record3?.batch.completed) return { available: false };
@@ -5665,12 +5828,12 @@ async function loadVirtualPopulateOutputRows(owner, inputs, plan, publicPopulate
5665
5828
  }
5666
5829
  return output;
5667
5830
  }
5668
- var import_mongoose7, import_node_util5, import_utils26, batches, rows, outputRows, fetchedPublicIdentities, publicViewIdentities, familyRawFetches, addressKey, storage, originalRef, copyOriginalValue, readPolicy, compatible, idKey, logUnavailable;
5831
+ var import_mongoose7, import_node_util5, import_utils27, batches, rows, outputRows, fetchedPublicIdentities, publicViewIdentities, familyRawFetches, addressKey, storage, originalRef, copyOriginalValue, readPolicy, compatible, idKey, logUnavailable;
5669
5832
  var init_virtual_populate_execution = __esm({
5670
5833
  "src/acl/virtual-populate-execution.ts"() {
5671
5834
  import_mongoose7 = require("mongoose");
5672
5835
  import_node_util5 = require("util");
5673
- import_utils26 = require("@web-ts-toolkit/utils");
5836
+ import_utils27 = require("@web-ts-toolkit/utils");
5674
5837
  init_cache();
5675
5838
  init_core_shared();
5676
5839
  init_document();
@@ -5687,7 +5850,7 @@ var init_virtual_populate_execution = __esm({
5687
5850
  publicViewIdentities = /* @__PURE__ */ new WeakMap();
5688
5851
  familyRawFetches = /* @__PURE__ */ new WeakMap();
5689
5852
  addressKey = (address) => JSON.stringify(address);
5690
- storage = (value) => value instanceof import_mongoose7.Document ? value._doc : (0, import_utils26.isPlainObject)(value) ? value : void 0;
5853
+ storage = (value) => value instanceof import_mongoose7.Document ? value._doc : (0, import_utils27.isPlainObject)(value) ? value : void 0;
5691
5854
  originalRef = (source, key) => {
5692
5855
  const record3 = storage(source);
5693
5856
  return {
@@ -5700,7 +5863,7 @@ var init_virtual_populate_execution = __esm({
5700
5863
  const option = getCapturedAuthorizationOption(configuration, `${key}.${access}`);
5701
5864
  return key === "operationAccess" || typeof option === "function" ? option : void 0;
5702
5865
  });
5703
- 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.requireReadOperationAccessForPopulateDependencies === b.binding.configuration.requireReadOperationAccessForPopulateDependencies && // Different captures may share only an equivalent terminal persisted binding/policy.
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.
5704
5867
  equivalentVirtualPopulatePredicate(
5705
5868
  a.binding.configuration.catalog.persistedPaths,
5706
5869
  b.binding.configuration.catalog.persistedPaths
@@ -5714,20 +5877,115 @@ var init_virtual_populate_execution = __esm({
5714
5877
  });
5715
5878
 
5716
5879
  // src/acl/output-field-filter.ts
5880
+ function callFieldPolicy(authorization, binding) {
5881
+ assertAuthorizationBinding(authorization, binding);
5882
+ let policy = fieldPolicies.get(authorization);
5883
+ if (!policy) {
5884
+ const view = readServiceOutputFieldAuthorization(authorization, binding);
5885
+ policy = {
5886
+ schema: view.permissionSchema,
5887
+ requestPaths: view.permissionSchema == null || (0, import_utils28.isPlainObject)(view.permissionSchema) && Object.isFrozen(view.permissionSchema) ? Object.freeze(Object.keys(view.permissionSchema ?? {})) : void 0,
5888
+ prefix: view.modelPermissionPrefix ?? "",
5889
+ outputFields: view.outputFields,
5890
+ accesses: /* @__PURE__ */ new Map()
5891
+ };
5892
+ if (view.reusable) fieldPolicies.set(authorization, policy);
5893
+ }
5894
+ return policy;
5895
+ }
5896
+ function requestRuleClassification(entry, authorization, permissions, prefix) {
5897
+ if (typeof prefix !== "string") return void 0;
5898
+ let classification = entry.classifications.get(authorization);
5899
+ if (!classification) {
5900
+ classification = prepareRequestFieldRule(entry.rule, permissions, prefix);
5901
+ entry.classifications.set(authorization, classification);
5902
+ }
5903
+ return classification;
5904
+ }
5905
+ function invariantRuleLookup(schema, path) {
5906
+ if (schema == null) return true;
5907
+ if (!(0, import_utils28.isPlainObject)(schema) || !Object.isFrozen(schema)) return false;
5908
+ const exact = Object.getOwnPropertyDescriptor(schema, path);
5909
+ if (exact) {
5910
+ const raw2 = exact.value;
5911
+ return !raw2 || typeof raw2 !== "object" || Array.isArray(raw2) || (0, import_utils28.isPlainObject)(raw2) && Object.isFrozen(raw2);
5912
+ }
5913
+ const [head, ...rest] = path.split(".");
5914
+ const raw = Object.getOwnPropertyDescriptor(schema, head)?.value;
5915
+ if (raw == null) return true;
5916
+ if (!(0, import_utils28.isPlainObject)(raw) || !Object.isFrozen(raw)) return false;
5917
+ const sub = Object.getOwnPropertyDescriptor(raw, "sub");
5918
+ return !rest.length || !sub || invariantRuleLookup(sub.value, rest.join("."));
5919
+ }
5920
+ function prepareOutputPath(policy, authorization, binding, path, access) {
5921
+ let cache = policy.accesses.get(access);
5922
+ if (!cache) {
5923
+ cache = /* @__PURE__ */ new Map();
5924
+ policy.accesses.set(access, cache);
5925
+ }
5926
+ let prepared = cache.get(path);
5927
+ if (!prepared) {
5928
+ const invariant = invariantRuleLookup(policy.schema, path);
5929
+ const permissions = getAuthorizationPermissions(authorization, binding);
5930
+ const entries = extractOutputFieldRuleEntries(policy.schema, path, access).map((entry) => ({
5931
+ ...entry,
5932
+ classifications: /* @__PURE__ */ new WeakMap()
5933
+ }));
5934
+ const classifications = entries.map(
5935
+ (entry) => requestRuleClassification(entry, authorization, permissions, policy.prefix)
5936
+ );
5937
+ const backendGrant = hasExplicitFieldGrant(policy.outputFields, path);
5938
+ prepared = {
5939
+ entries,
5940
+ backendGrant,
5941
+ state: backendGrant ? "granted" : classifications.every((value) => value !== void 0) ? combineFieldAuthorizationStates(classifications.map((value) => value.state)) : void 0
5942
+ };
5943
+ if (invariant) cache.set(path, prepared);
5944
+ }
5945
+ return prepared;
5946
+ }
5947
+ function assertOwningOutputDocuments(handle) {
5948
+ const owner = owningDocuments.get(handle);
5949
+ if (owner) {
5950
+ readDocumentAuthorizationAccess(owner.handle, owner.binding);
5951
+ assertOwningOutputDocuments(owner.handle);
5952
+ }
5953
+ }
5954
+ function prepareOutputDocumentAuthorization(authorization, binding, configuration) {
5955
+ const access = readServiceAuthorizationAccess(authorization, binding);
5956
+ const key = `docPermissions.${access.docPermissionsAccess}`;
5957
+ const docPermissions = configuration.docPermissions;
5958
+ const invariant = /^[A-Za-z0-9_-]+$/.test(access.docPermissionsAccess) && (docPermissions == null || (0, import_utils28.isPlainObject)(docPermissions));
5959
+ const option = invariant ? getCapturedAuthorizationOption(configuration, key) : void 0;
5960
+ const hook = typeof option === "function" ? option : void 0;
5961
+ const resolveHook = invariant ? () => hook : () => {
5962
+ const slice = { catalog: configuration.catalog, ...copyAuthorizationValue({ docPermissions }, true) };
5963
+ const option2 = getCapturedAuthorizationOption(slice, key);
5964
+ return typeof option2 === "function" ? option2 : void 0;
5965
+ };
5966
+ return async (args) => {
5967
+ if (args.authorization !== authorization) authorizationError("document resolver call mismatch");
5968
+ assertAuthorizationBinding(authorization, args.binding);
5969
+ return resolveOutputDocumentAuthorizationWithHook(args, access, resolveHook);
5970
+ };
5971
+ }
5717
5972
  async function resolveOutputDocumentAuthorization(args) {
5973
+ const configuration = readServiceDocumentPermissions(args.authorization, args.binding);
5974
+ return prepareOutputDocumentAuthorization(args.authorization, args.binding, configuration)(args);
5975
+ }
5976
+ async function resolveOutputDocumentAuthorizationWithHook(args, access, resolveHook) {
5718
5977
  const { authorization, binding } = args;
5719
- const { configuration, access } = readServiceAuthorization(authorization, binding);
5720
5978
  const stage = args.stage ?? createDocumentAuthorizationStage(authorization, binding, args.snapshot);
5721
5979
  if (args.stage) assertDocumentAuthorizationStage(stage, authorization, binding, args.snapshot);
5722
5980
  const handle = await resolveDocumentAuthorization(stage, async function(doc, permissions) {
5723
5981
  if (args.owningDocument) return readDocumentAuthorization(args.owningDocument, args.owningBinding).grants;
5724
5982
  if (args.knownOutput) return {};
5725
- const hook = getCapturedAuthorizationOption(configuration, `docPermissions.${access.docPermissionsAccess}`);
5726
- if (typeof hook !== "function") return {};
5983
+ const hook = resolveHook();
5984
+ if (!hook) return {};
5727
5985
  try {
5728
5986
  const run = () => hook.call(this, doc, permissions, args.context);
5729
5987
  const grants = await getRequestWorkState(binding.request, binding.runtime).hookGate.run(run);
5730
- return (0, import_utils27.isPlainObject)(grants) ? grants : {};
5988
+ return (0, import_utils28.isPlainObject)(grants) ? grants : {};
5731
5989
  } catch (error2) {
5732
5990
  warn("docPermissions hook failed; applying empty document permissions", {
5733
5991
  modelName: binding.modelName,
@@ -5744,6 +6002,7 @@ async function resolveOutputDocumentAuthorization(args) {
5744
6002
  return handle;
5745
6003
  }
5746
6004
  function evaluateRequestRule(handle, binding, entry, prefix, access) {
6005
+ assertOwningOutputDocuments(handle);
5747
6006
  const owner = owningDocuments.get(handle);
5748
6007
  if (owner && !entry.path.startsWith(`${(binding.scopePath ?? []).join(".")}.`)) {
5749
6008
  return evaluateRequestRule(owner.handle, owner.binding, entry, prefix, access);
@@ -5752,65 +6011,78 @@ function evaluateRequestRule(handle, binding, entry, prefix, access) {
5752
6011
  handle,
5753
6012
  binding,
5754
6013
  `request:${access}:${entry.path}`,
5755
- function(permissions, grants) {
5756
- return evaluateScopedAccessRule({
5757
- req: this,
5758
- rule: entry.rule,
5759
- globalPermissions: permissions,
5760
- docPermissions: grants,
5761
- modelPermissionPrefix: prefix
5762
- });
6014
+ async function(permissions, grants) {
6015
+ const { authorization } = readDocumentAuthorizationAccess(handle, binding);
6016
+ const classification = requestRuleClassification(entry, authorization, permissions, prefix);
6017
+ let allowed = classification?.decision;
6018
+ if (allowed === void 0) {
6019
+ allowed = await evaluateScopedAccessRule({
6020
+ req: this,
6021
+ rule: entry.rule,
6022
+ globalPermissions: permissions,
6023
+ docPermissions: grants,
6024
+ modelPermissionPrefix: prefix
6025
+ });
6026
+ }
6027
+ assertOwningOutputDocuments(handle);
6028
+ return allowed;
5763
6029
  }
5764
6030
  );
5765
6031
  }
5766
6032
  function evaluateAuthorizedOutputField(handle, binding, field) {
5767
- const { authorization } = readDocumentAuthorization(handle, binding);
5768
- const { configuration, authorization: policy, access } = readServiceAuthorization(authorization, binding);
6033
+ const { authorization } = readDocumentAuthorizationAccess(handle, binding);
6034
+ assertOwningOutputDocuments(handle);
5769
6035
  const path = [...binding.scopePath ?? [], field].join(".");
5770
- const entries = extractOutputFieldRuleEntries(
5771
- configuration.permissionSchema,
5772
- path,
5773
- access.outputAccess
5774
- );
5775
- return memoizeDocumentFieldDecision(handle, binding, `output:${path}`, function(permissions, grants) {
5776
- const args = {
6036
+ const decision = memoizeDocumentFieldDecision(handle, binding, `output:${path}`, async function(permissions) {
6037
+ const policy = callFieldPolicy(authorization, binding);
6038
+ const { outputAccess } = readDocumentAuthorizationAccess(handle, binding);
6039
+ const prepared = prepareOutputPath(policy, authorization, binding, path, outputAccess);
6040
+ const unresolved = unresolvedDocuments.has(handle);
6041
+ const state = unresolved ? prepared.state ?? classifyEffectiveFieldAuthorization({
5777
6042
  path,
5778
- rules: entries.map((entry) => entry.rule),
6043
+ rules: prepared.entries.map((entry) => entry.rule),
6044
+ modelPermissionPrefix: policy.prefix,
5779
6045
  outputFields: policy.outputFields,
5780
- globalPermissions: permissions,
5781
- modelPermissionPrefix: configuration.modelPermissionPrefix ?? ""
5782
- };
5783
- if (unresolvedDocuments.has(handle) && classifyEffectiveFieldAuthorization(args) !== "granted") return false;
5784
- return evaluateEffectiveFieldAuthorization({
5785
- ...args,
5786
- req: this,
5787
- docPermissions: grants,
5788
- evaluateRule: (_rule, index) => evaluateRequestRule(handle, binding, entries[index], args.modelPermissionPrefix, access.outputAccess)
5789
- });
6046
+ globalPermissions: permissions
6047
+ }) : void 0;
6048
+ let allowed;
6049
+ if (unresolved && state !== "granted") allowed = false;
6050
+ else if (prepared.backendGrant) allowed = true;
6051
+ else {
6052
+ allowed = true;
6053
+ for (const entry of prepared.entries) {
6054
+ if (!await evaluateRequestRule(handle, binding, entry, policy.prefix, outputAccess)) {
6055
+ allowed = false;
6056
+ break;
6057
+ }
6058
+ }
6059
+ }
6060
+ assertOwningOutputDocuments(handle);
6061
+ return allowed;
5790
6062
  });
6063
+ return owningDocuments.has(handle) ? decision.then((allowed) => {
6064
+ assertOwningOutputDocuments(handle);
6065
+ return allowed;
6066
+ }) : decision;
5791
6067
  }
5792
6068
  async function requestOutputFieldPaths(handle, binding, access) {
5793
- const { authorization } = readDocumentAuthorization(handle, binding);
5794
- const { configuration } = readServiceAuthorization(authorization, binding);
5795
- const schema = configuration.permissionSchema;
6069
+ const { authorization } = readDocumentAuthorizationAccess(handle, binding);
6070
+ assertOwningOutputDocuments(handle);
6071
+ const policy = callFieldPolicy(authorization, binding);
5796
6072
  const fields = [];
5797
- for (const path of Object.keys(schema ?? {})) {
5798
- const entries = extractOutputFieldRuleEntries(schema, path, access);
6073
+ for (const path of policy.requestPaths ?? Object.keys(policy.schema ?? {})) {
6074
+ const { entries } = prepareOutputPath(policy, authorization, binding, path, access);
5799
6075
  let allowed = true;
5800
6076
  for (const entry of entries) {
5801
- if (!await evaluateRequestRule(
5802
- handle,
5803
- binding,
5804
- entry,
5805
- configuration.modelPermissionPrefix ?? "",
5806
- access
5807
- )) {
6077
+ if (!await evaluateRequestRule(handle, binding, entry, policy.prefix, access)) {
5808
6078
  allowed = false;
5809
6079
  break;
5810
6080
  }
5811
6081
  }
5812
6082
  if (allowed && entries.length) fields.push(path);
5813
6083
  }
6084
+ assertOwningOutputDocuments(handle);
6085
+ readDocumentAuthorizationAccess(handle, binding);
5814
6086
  return fields;
5815
6087
  }
5816
6088
  function authorizeEmbeddedOutputPlan(parent, child) {
@@ -5831,24 +6103,24 @@ function authorizeEmbeddedOutputPlan(parent, child) {
5831
6103
  function hasWholeOutputValueGrant(plan, field) {
5832
6104
  if (plan.fieldStates?.[field] !== "granted") return false;
5833
6105
  const binding = plan.authorizationBinding;
5834
- const { authorization } = readServiceAuthorization(plan.authorization, binding);
5835
- return hasExplicitFieldGrant(authorization.outputFields, [...binding.scopePath ?? [], field].join("."));
6106
+ const { outputFields } = readServiceOutputPolicy(plan.authorization, binding);
6107
+ return hasExplicitFieldGrant(outputFields, [...binding.scopePath ?? [], field].join("."));
5836
6108
  }
5837
6109
  function copyOutputValue(value, plan, path) {
5838
- const { configuration } = readServiceAuthorization(plan.authorization, plan.authorizationBinding);
5839
- const virtualValue = configuration.catalog.virtualPaths?.some((p) => matchesCatalogPattern(p, path));
5840
- const opaque = configuration.catalog.opaquePaths?.some((p) => matchesCatalogPattern(p, path));
6110
+ const { catalog } = readServiceOutputPolicy(plan.authorization, plan.authorizationBinding);
6111
+ const virtualValue = catalog.virtualPaths?.some((p) => matchesCatalogPattern(p, path));
6112
+ const opaque = catalog.opaquePaths?.some((p) => matchesCatalogPattern(p, path));
5841
6113
  const excluded = plan.effectiveSelectNorm.filter((token) => token.startsWith("-")).map((token) => [...plan.authorizationBinding.scopePath ?? [], token.slice(1)].join("."));
5842
6114
  const visit = (input, current) => {
5843
6115
  if (Array.isArray(input)) return input.map((entry) => visit(entry, current));
5844
6116
  if (input instanceof Map) return visit(Object.fromEntries(input), current);
5845
- if (!(0, import_utils27.isPlainObject)(input)) return deepIsolateValue(input);
6117
+ if (!(0, import_utils28.isPlainObject)(input)) return deepIsolateValue(input);
5846
6118
  const output = {};
5847
6119
  for (const [key, entry] of Object.entries(input)) {
5848
6120
  const child = `${current}.${key}`;
5849
6121
  if (excluded.some((entry2) => child === entry2 || child.startsWith(`${entry2}.`))) continue;
5850
6122
  const safeValueKey = !["__proto__", "constructor", "prototype", "_doc", "$__", "__v"].includes(key);
5851
- if (!(opaque ? safeValueKey : isSafeOutputPath(key)) || configuration.catalog.protectedPaths?.some((p) => matchesCatalogPattern(p, child, true)) || !opaque && !virtualValue && !isCatalogOutputPath(configuration.catalog, child))
6123
+ if (!(opaque ? safeValueKey : isSafeOutputPath(key)) || catalog.protectedPaths?.some((p) => matchesCatalogPattern(p, child, true)) || !opaque && !virtualValue && !isCatalogOutputPath(catalog, child))
5852
6124
  continue;
5853
6125
  output[key] = visit(entry, child);
5854
6126
  }
@@ -5858,25 +6130,21 @@ function copyOutputValue(value, plan, path) {
5858
6130
  }
5859
6131
  function copyAuthorizedReference(value, plan, path) {
5860
6132
  if (Array.isArray(value)) return value.map((entry) => copyAuthorizedReference(entry, plan, path));
5861
- if (!(0, import_utils27.isPlainObject)(value)) return deepIsolateValue(value);
6133
+ if (!(0, import_utils28.isPlainObject)(value)) return deepIsolateValue(value);
5862
6134
  const handle = getAssociatedDocumentAuthorization(value, plan.authorization);
5863
6135
  if (handle) {
5864
6136
  const copy = deepIsolateValue(value);
5865
6137
  transferDocumentAuthorizationGraph(value, copy);
5866
6138
  return copy;
5867
6139
  }
5868
- const { relatedTargets } = readServiceAuthorization(plan.authorization, plan.authorizationBinding);
5869
- if (relatedTargets.some((target) => target.path === path && target.kind === "passthrough")) {
5870
- return deepIsolateValue(value);
5871
- }
5872
6140
  return Object.prototype.hasOwnProperty.call(value, "_id") ? deepIsolateValue(value._id) : void 0;
5873
6141
  }
5874
6142
  function retainOutputMetadata(output, source, configuration, scope2) {
5875
6143
  if (scope2.length || configuration.stripPermissionsField) return;
5876
6144
  const parts = (configuration.documentPermissionField ?? "_permissions").split(".");
5877
6145
  let value = source;
5878
- for (const part of parts) value = (0, import_utils27.isPlainObject)(value) ? value[part] : void 0;
5879
- if (!(0, import_utils27.isPlainObject)(value)) return;
6146
+ for (const part of parts) value = (0, import_utils28.isPlainObject)(value) ? value[part] : void 0;
6147
+ if (!(0, import_utils28.isPlainObject)(value)) return;
5880
6148
  const keys2 = configuration.exposedDocPermissionKeys;
5881
6149
  const metadata = Object.fromEntries(
5882
6150
  Object.entries(value).filter(
@@ -5886,7 +6154,7 @@ function retainOutputMetadata(output, source, configuration, scope2) {
5886
6154
  const copy = { ...output };
5887
6155
  let target = copy;
5888
6156
  for (const part of parts.slice(0, -1)) {
5889
- target[part] = (0, import_utils27.isPlainObject)(target[part]) ? { ...target[part] } : {};
6157
+ target[part] = (0, import_utils28.isPlainObject)(target[part]) ? { ...target[part] } : {};
5890
6158
  target = target[part];
5891
6159
  }
5892
6160
  target[parts[parts.length - 1]] = deepIsolateValue(metadata);
@@ -5928,11 +6196,11 @@ async function filterExistingOutput(source, plan, handle, associated) {
5928
6196
  for (const entry of value2) values.push(await one(entry) ?? null);
5929
6197
  return values;
5930
6198
  }
5931
- if (!(0, import_utils27.isPlainObject)(value2)) return value2 === null ? null : void 0;
6199
+ if (!(0, import_utils28.isPlainObject)(value2)) return value2 === null ? null : void 0;
5932
6200
  const childHandle = associated ? getAssociatedDocumentAuthorization(value2) : void 0;
5933
6201
  let child = scoped;
5934
6202
  if (childHandle) {
5935
- const state = readDocumentAuthorization(childHandle, scoped.authorizationBinding);
6203
+ const state = readDocumentAuthorizationAccess(childHandle, scoped.authorizationBinding);
5936
6204
  if (getAuthorizationProvenance(state.authorization) !== getAuthorizationProvenance(plan.authorization))
5937
6205
  authorizationError("child call mismatch");
5938
6206
  child = helperPlan(state.authorization, scoped.authorizationBinding);
@@ -5996,7 +6264,7 @@ async function filterModelOutputForHelper(args) {
5996
6264
  if (normalized.kind === "document") {
5997
6265
  const { documentAuthorization: handle2, binding: binding2 } = normalized;
5998
6266
  assertDocumentAuthorization(handle2, { binding: binding2, output: args.doc, outputAccess: access });
5999
- const state = readDocumentAuthorization(handle2, binding2);
6267
+ const state = readDocumentAuthorizationAccess(handle2, binding2);
6000
6268
  return filterExistingOutput(
6001
6269
  args.doc,
6002
6270
  helperPlan(state.authorization, binding2),
@@ -6008,7 +6276,7 @@ async function filterModelOutputForHelper(args) {
6008
6276
  const plan = helperPlan(normalized.authorization, binding);
6009
6277
  if (args.doc == null) return { output: args.doc, fields: [] };
6010
6278
  const source = isolateDocForOutput(args.doc);
6011
- if (!(0, import_utils27.isPlainObject)(source)) return authorizationError("output helper requires one plain or hydrated document");
6279
+ if (!(0, import_utils28.isPlainObject)(source)) return authorizationError("output helper requires one plain or hydrated document");
6012
6280
  const knownOutput = !!args.doc && typeof args.doc === "object" && isKnownAuthorizationOutput(args.doc);
6013
6281
  const handle = await resolveOutputDocumentAuthorization({
6014
6282
  authorization: normalized.authorization,
@@ -6019,10 +6287,10 @@ async function filterModelOutputForHelper(args) {
6019
6287
  });
6020
6288
  return filterExistingOutput(source, plan, handle, false);
6021
6289
  }
6022
- var import_utils27, unresolvedDocuments, owningDocuments;
6290
+ var import_utils28, unresolvedDocuments, owningDocuments, fieldPolicies;
6023
6291
  var init_output_field_filter = __esm({
6024
6292
  "src/acl/output-field-filter.ts"() {
6025
- import_utils27 = require("@web-ts-toolkit/utils");
6293
+ import_utils28 = require("@web-ts-toolkit/utils");
6026
6294
  init_permissions_alias();
6027
6295
  init_document();
6028
6296
  init_request_work();
@@ -6036,6 +6304,7 @@ var init_output_field_filter = __esm({
6036
6304
  init_output_projection();
6037
6305
  unresolvedDocuments = /* @__PURE__ */ new WeakSet();
6038
6306
  owningDocuments = /* @__PURE__ */ new WeakMap();
6307
+ fieldPolicies = /* @__PURE__ */ new WeakMap();
6039
6308
  }
6040
6309
  });
6041
6310
 
@@ -6104,27 +6373,53 @@ __export(finalize_model_output_exports, {
6104
6373
  finalizeModelOutput: () => finalizeModelOutput,
6105
6374
  finalizeModelOutputs: () => finalizeModelOutputs
6106
6375
  });
6107
- async function finalizeSingleDoc(args, input) {
6108
- const scopePath = args.scopePath;
6376
+ function assertFinalizerCall(args, prepared) {
6109
6377
  const plan = args.plan;
6110
6378
  const binding = plan.authorizationBinding;
6111
- const captured = plan.authorization && binding ? readServiceAuthorization(plan.authorization, binding) : void 0;
6112
- if (plan.authorization && (!binding || binding.request !== args.request || binding.modelName !== args.receivingModelName || captured?.access.virtualAccess !== args.virtualAccess || captured.access.outputAccess !== args.outputAccess || captured.access.docPermissionsAccess !== args.docPermissionsAccess || (binding.scopePath ?? []).join(".") !== scopePath.filter((p) => p !== "sub").join("."))) {
6379
+ if (plan.authorization !== prepared.authorization) authorizationError("finalizer call changed during pass");
6380
+ if (plan.authorization && binding) assertAuthorizationBinding(plan.authorization, binding);
6381
+ const access = prepared.policy?.access;
6382
+ 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("."))) {
6113
6383
  authorizationError("finalizer call/model/scope/access mismatch");
6114
6384
  }
6115
- const snapshot = captured ? captured.configuration : args.snapshot;
6385
+ }
6386
+ function prepareFinalizer(args, sharedContext = false) {
6387
+ const { authorization, authorizationBinding: binding } = args.plan;
6388
+ const policy = authorization && binding ? readServiceOutputPolicy(authorization, binding) : void 0;
6389
+ assertFinalizerCall(args, { authorization, policy });
6390
+ const configuration = authorization ? readServiceFinalizerConfiguration(authorization, binding) : void 0;
6391
+ const snapshot = configuration ? configuration : deepIsolateValue(args.snapshot);
6392
+ return {
6393
+ authorization,
6394
+ policy,
6395
+ snapshot,
6396
+ baseContext: deepIsolateValue(args.context ?? {}),
6397
+ sharedContext,
6398
+ scopeVirtualNames: getScopeVirtualNames(snapshot, args.scopePath),
6399
+ scopedSchema: configuration ? null : getScopedPermissionSchema2(snapshot.permissionSchema, args.scopePath),
6400
+ permissions: authorization ? getAuthorizationPermissions(authorization, binding) : void 0,
6401
+ resolveDocument: authorization ? prepareOutputDocumentAuthorization(authorization, binding, configuration) : void 0
6402
+ };
6403
+ }
6404
+ async function finalizeSingleDoc(args, input, prepared) {
6405
+ assertFinalizerCall(args, prepared);
6406
+ const scopePath = args.scopePath;
6407
+ const plan = args.plan;
6408
+ const binding = plan.authorizationBinding;
6409
+ const captured = prepared.policy;
6410
+ const snapshot = prepared.snapshot;
6116
6411
  const limit = args.resolvedLimit;
6117
6412
  const gate = args.sharedGate;
6118
6413
  const original = getIncludeOriginal(input);
6119
6414
  const privateRow = args.virtualPopulateRow ?? (original && typeof original === "object" ? getVirtualPopulateRow(original) : void 0);
6120
6415
  const privateAddress = args.virtualPopulateAddress ?? [];
6121
6416
  const isolated = isolateDocForOutput(input);
6122
- const output = (0, import_utils28.isPlainObject)(isolated) ? isolated : {};
6123
- if (captured && (0, import_utils28.isPlainObject)(input))
6417
+ const output = (0, import_utils29.isPlainObject)(isolated) ? isolated : {};
6418
+ if (captured && (0, import_utils29.isPlainObject)(input))
6124
6419
  for (const key of Object.keys(output)) {
6125
6420
  transferDocumentAuthorizationGraph(input[key], output[key]);
6126
6421
  }
6127
- const baseContext = args.context ?? {};
6422
+ const baseContext = prepared.sharedContext ? deepIsolateValue(prepared.baseContext) : prepared.baseContext;
6128
6423
  const operation = captured?.access.operation ?? baseContext.operation ?? args.operation ?? args.virtualAccess;
6129
6424
  const scopeContext = {
6130
6425
  ...baseContext,
@@ -6142,7 +6437,7 @@ async function finalizeSingleDoc(args, input) {
6142
6437
  if (captured) {
6143
6438
  const projected = args.documentAuthorizationStage ? input : deepIsolateValue(output);
6144
6439
  metadataSource = deepIsolateValue(output);
6145
- documentAuthorization = await resolveOutputDocumentAuthorization({
6440
+ documentAuthorization = await prepared.resolveDocument({
6146
6441
  authorization: plan.authorization,
6147
6442
  binding,
6148
6443
  snapshot: projected,
@@ -6155,13 +6450,10 @@ async function finalizeSingleDoc(args, input) {
6155
6450
  }
6156
6451
  const docGrants = documentAuthorization ? readDocumentAuthorization(documentAuthorization, binding).grants : await resolveDocPermissions(args, output, scopeContext);
6157
6452
  scopeContext.docPermissions = deepIsolateValue(docGrants);
6158
- const globalPerms = captured ? getAuthorizationPermissions(plan.authorization, binding) : resolveGlobalPermissions(args);
6159
- const modelPermissionPrefix = snapshot.modelPermissionPrefix ?? "";
6160
- const documentPermissionField = snapshot.documentPermissionField ?? "_permissions";
6161
- const scopedSchema = getScopedPermissionSchema2(
6162
- snapshot.permissionSchema ?? null,
6163
- scopePath
6164
- );
6453
+ const globalPerms = prepared.permissions ?? resolveGlobalPermissions(args);
6454
+ const modelPermissionPrefix = captured ? "" : deepIsolateValue(snapshot.modelPermissionPrefix ?? "");
6455
+ const documentPermissionField = captured ? "_permissions" : deepIsolateValue(snapshot.documentPermissionField ?? "_permissions");
6456
+ const scopedSchema = deepIsolateValue(prepared.scopedSchema);
6165
6457
  const childPlans = plan.childPlans ?? {};
6166
6458
  for (const [containerName, childPlan] of Object.entries(childPlans)) {
6167
6459
  if (!(containerName in output)) continue;
@@ -6175,17 +6467,18 @@ async function finalizeSingleDoc(args, input) {
6175
6467
  );
6176
6468
  continue;
6177
6469
  }
6178
- if (captured && !(0, import_utils28.isPlainObject)(rawContainer) && !Array.isArray(rawContainer)) {
6470
+ if (captured && !(0, import_utils29.isPlainObject)(rawContainer) && !Array.isArray(rawContainer)) {
6179
6471
  delete output[containerName];
6180
6472
  continue;
6181
6473
  }
6182
6474
  retainRawAssociation(output, containerName, rawContainer);
6183
- const childSnapshot = snapshot;
6184
6475
  const childBase = {
6185
6476
  ...args,
6186
6477
  scopePath: [...childPlan.scopePath ?? [...scopePath, containerName, "sub"]],
6187
6478
  plan: captured ? authorizeEmbeddedOutputPlan(plan, childPlan) : childPlan,
6188
- snapshot: childSnapshot,
6479
+ // Authenticated preparation reads its own authority. Do not clone an
6480
+ // otherwise unused public configuration graph on default traversal.
6481
+ snapshot: args.snapshot,
6189
6482
  // Embedded reuses the owning parent's grants + accesses (VIRT-00A D2).
6190
6483
  docPermissions: docGrants,
6191
6484
  context: scopeContext,
@@ -6196,10 +6489,11 @@ async function finalizeSingleDoc(args, input) {
6196
6489
  related: [],
6197
6490
  virtualPopulateRow: privateRow
6198
6491
  };
6492
+ let childPreparation;
6199
6493
  const finalizeOneChild = async (rawChild, address) => {
6200
6494
  if (captured && Array.isArray(rawChild))
6201
6495
  return mapWithConcurrencyLimit(rawChild, limit, (entry, index) => finalizeOneChild(entry, [...address, index]));
6202
- if (!(0, import_utils28.isPlainObject)(rawChild) && !Array.isArray(rawChild)) return captured ? null : deepIsolateValue(rawChild);
6496
+ if (!(0, import_utils29.isPlainObject)(rawChild) && !Array.isArray(rawChild)) return captured ? null : deepIsolateValue(rawChild);
6203
6497
  if (args.traversal?.finalizeEmbeddedChildren) {
6204
6498
  return args.traversal.finalizeEmbeddedChildren({
6205
6499
  containerName,
@@ -6216,7 +6510,14 @@ async function finalizeSingleDoc(args, input) {
6216
6510
  } : { ...args, virtualPopulateRow: privateRow, virtualPopulateAddress: address }
6217
6511
  });
6218
6512
  }
6219
- return finalizeSingleDoc({ ...childBase, virtualPopulateAddress: address }, rawChild);
6513
+ return finalizeSingleDoc(
6514
+ { ...childBase, virtualPopulateAddress: address },
6515
+ rawChild,
6516
+ childPreparation ??= prepareFinalizer(
6517
+ childBase,
6518
+ Array.isArray(rawContainer) && (rawContainer.length > 1 || Array.isArray(rawContainer[0]))
6519
+ )
6520
+ );
6220
6521
  };
6221
6522
  if (Array.isArray(rawContainer)) {
6222
6523
  const finalized = await mapWithConcurrencyLimit(
@@ -6225,7 +6526,7 @@ async function finalizeSingleDoc(args, input) {
6225
6526
  (entry, index) => finalizeOneChild(entry, [...privateAddress, containerName, index])
6226
6527
  );
6227
6528
  output[containerName] = finalized;
6228
- } else if ((0, import_utils28.isPlainObject)(rawContainer)) {
6529
+ } else if ((0, import_utils29.isPlainObject)(rawContainer)) {
6229
6530
  output[containerName] = await finalizeOneChild(rawContainer, [
6230
6531
  ...privateAddress,
6231
6532
  containerName
@@ -6237,8 +6538,8 @@ async function finalizeSingleDoc(args, input) {
6237
6538
  let rawRelated;
6238
6539
  let originalRelated;
6239
6540
  try {
6240
- rawRelated = target.path.includes(".") ? (0, import_utils28.get)(output, target.path) : output[target.path];
6241
- originalRelated = (0, import_utils28.get)(original, target.path);
6541
+ rawRelated = target.path.includes(".") ? (0, import_utils29.get)(output, target.path) : output[target.path];
6542
+ originalRelated = (0, import_utils29.get)(original, target.path);
6242
6543
  } catch {
6243
6544
  continue;
6244
6545
  }
@@ -6268,8 +6569,9 @@ async function finalizeSingleDoc(args, input) {
6268
6569
  context: scopeContext,
6269
6570
  related: target.plan.relatedPlans?.length ? (args.related ?? []).filter((entry) => entry.path.startsWith(`${target.path}.`)) : []
6270
6571
  };
6572
+ let relatedPreparation;
6271
6573
  const finalizeOneRelated = async (rawChild, originalChild = rawChild) => {
6272
- if (!(0, import_utils28.isPlainObject)(rawChild) && !Array.isArray(rawChild)) return rawChild;
6574
+ if (!(0, import_utils29.isPlainObject)(rawChild) && !Array.isArray(rawChild)) return rawChild;
6273
6575
  if (typeof rawChild?._bsontype === "string") return rawChild;
6274
6576
  const childOriginal = getIncludeOriginal(originalChild);
6275
6577
  const childRow = childOriginal && typeof childOriginal === "object" ? getVirtualPopulateRow(childOriginal) : void 0;
@@ -6281,7 +6583,11 @@ async function finalizeSingleDoc(args, input) {
6281
6583
  parentArgs: { ...args, virtualPopulateRow: childRow, virtualPopulateAddress: [] }
6282
6584
  });
6283
6585
  }
6284
- return finalizeSingleDoc({ ...relatedBase, virtualPopulateRow: childRow }, rawChild);
6586
+ return finalizeSingleDoc(
6587
+ { ...relatedBase, virtualPopulateRow: childRow },
6588
+ rawChild,
6589
+ relatedPreparation ??= prepareFinalizer(relatedBase, Array.isArray(rawRelated) && rawRelated.length > 1)
6590
+ );
6285
6591
  };
6286
6592
  let finalized;
6287
6593
  if (Array.isArray(rawRelated)) {
@@ -6290,29 +6596,35 @@ async function finalizeSingleDoc(args, input) {
6290
6596
  limit,
6291
6597
  (entry, index) => finalizeOneRelated(entry, Array.isArray(originalRelated) ? originalRelated[index] : void 0)
6292
6598
  );
6293
- } else if ((0, import_utils28.isPlainObject)(rawRelated)) {
6599
+ } else if ((0, import_utils29.isPlainObject)(rawRelated)) {
6294
6600
  finalized = await finalizeOneRelated(rawRelated, originalRelated);
6295
6601
  } else {
6296
6602
  continue;
6297
6603
  }
6298
6604
  try {
6299
- if (target.path.includes(".")) (0, import_utils28.set)(output, target.path, finalized);
6605
+ if (target.path.includes(".")) (0, import_utils29.set)(output, target.path, finalized);
6300
6606
  else output[target.path] = finalized;
6301
6607
  } catch {
6302
6608
  }
6303
6609
  }
6304
- const scopeVirtualNames = getScopeVirtualNames(snapshot, scopePath);
6305
- for (const virtualName of scopeVirtualNames) {
6610
+ for (const virtualName of prepared.scopeVirtualNames) {
6306
6611
  if (virtualName in output) delete output[virtualName];
6307
6612
  }
6308
- const stableBase = deepIsolateValue(output);
6309
- const scopeLabel = scopePath.length > 0 ? scopePath.join(".") : "";
6310
6613
  const candidates = plan.candidates ?? [];
6614
+ const needsGetterView = candidates.some((candidate) => {
6615
+ const descriptor = candidate.descriptor;
6616
+ if (typeof descriptor?.get !== "function") return false;
6617
+ if (!privateRow && descriptor.dependsOnPopulate?.length) return false;
6618
+ const dependsOn = Array.isArray(descriptor.dependsOn) ? descriptor.dependsOn : [];
6619
+ return dependsOn.every((dep) => typeof dep === "string" && !isAbsentDependency(output, dep));
6620
+ });
6621
+ let stableBase = needsGetterView ? deepIsolateValue(output) : void 0;
6622
+ const scopeLabel = scopePath.length > 0 ? scopePath.join(".") : "";
6311
6623
  const staged = /* @__PURE__ */ new Map();
6312
6624
  const runCandidate = async (candidate) => {
6313
6625
  const dependsOn = Array.isArray(candidate.descriptor?.dependsOn) ? candidate.descriptor.dependsOn : [];
6314
6626
  for (const dep of dependsOn) {
6315
- if (typeof dep !== "string" || isAbsentDependency(stableBase, dep)) return;
6627
+ if (typeof dep !== "string" || isAbsentDependency(stableBase ?? output, dep)) return;
6316
6628
  }
6317
6629
  const allowed = documentAuthorization ? await evaluateAuthorizedOutputField(documentAuthorization, binding, candidate.name) : await evaluateEffectiveFieldAuthorization({
6318
6630
  req: args.request,
@@ -6327,9 +6639,9 @@ async function finalizeSingleDoc(args, input) {
6327
6639
  if (typeof getter !== "function") return;
6328
6640
  const privateInputs = privateRow ? readVirtualPopulateInputs(privateRow, privateAddress, candidate.name) : candidate.descriptor.dependsOnPopulate?.length ? { available: false } : { available: true, populated: {} };
6329
6641
  if (!privateInputs.available) return;
6330
- const isolatedView = deepIsolateValue(stableBase);
6642
+ const isolatedView = deepIsolateValue(stableBase ??= deepIsolateValue(output));
6331
6643
  const virtualContext = {
6332
- ...scopeContext,
6644
+ ...deepIsolateValue(scopeContext),
6333
6645
  docPermissions: deepIsolateValue(docGrants)
6334
6646
  };
6335
6647
  const outcome = await runVirtualGetter({
@@ -6410,7 +6722,6 @@ async function finalizeSingleDoc(args, input) {
6410
6722
  if (!captured && documentPermissionField in output) keep.add(documentPermissionField);
6411
6723
  const trimmed = {};
6412
6724
  for (const key of keep) {
6413
- if (privateRow && !await isVirtualPopulateReferenceVisible(privateRow, privateAddress, key)) continue;
6414
6725
  if (privateRow && !hasVirtualPopulatePublicReference(privateRow, privateAddress, key)) {
6415
6726
  const ref = readVirtualPopulateOriginalReference(privateRow, privateAddress, key);
6416
6727
  if (ref) {
@@ -6421,12 +6732,25 @@ async function finalizeSingleDoc(args, input) {
6421
6732
  if (!(key in output)) continue;
6422
6733
  if (captured && !plan.childPlans[key]) {
6423
6734
  const path = [...binding.scopePath ?? [], key].join(".");
6424
- trimmed[key] = captured.configuration.catalog.referencePaths?.some((p) => matchesCatalogPattern(p, path)) ? copyAuthorizedReference(output[key], plan, path) : copyOutputValue(output[key], plan, path);
6735
+ trimmed[key] = captured.catalog.referencePaths?.some((p) => matchesCatalogPattern(p, path)) ? copyAuthorizedReference(output[key], plan, path) : copyOutputValue(output[key], plan, path);
6425
6736
  if (trimmed[key] === void 0) delete trimmed[key];
6426
6737
  } else trimmed[key] = output[key];
6427
6738
  }
6428
6739
  if (documentAuthorization) {
6429
- retainOutputMetadata(trimmed, metadataSource, captured.configuration, binding.scopePath ?? []);
6740
+ retainOutputMetadata(
6741
+ trimmed,
6742
+ metadataSource,
6743
+ copyAuthorizationValue(
6744
+ {
6745
+ documentPermissionField: snapshot.documentPermissionField,
6746
+ stripPermissionsField: snapshot.stripPermissionsField,
6747
+ exposedDocPermissionKeys: snapshot.exposedDocPermissionKeys,
6748
+ disableFieldPermissions: snapshot.disableFieldPermissions
6749
+ },
6750
+ true
6751
+ ),
6752
+ binding.scopePath ?? []
6753
+ );
6430
6754
  associateDocumentAuthorization(documentAuthorization, binding, trimmed);
6431
6755
  args.onFinalized?.(
6432
6756
  trimmed,
@@ -6435,7 +6759,7 @@ async function finalizeSingleDoc(args, input) {
6435
6759
  output: trimmed,
6436
6760
  outputAccess: args.outputAccess,
6437
6761
  authorization: plan.authorization,
6438
- stage: readDocumentAuthorization(documentAuthorization, binding).stage
6762
+ stage: readDocumentAuthorizationAccess(documentAuthorization, binding).stage
6439
6763
  })
6440
6764
  );
6441
6765
  }
@@ -6456,7 +6780,7 @@ async function finalizeModelOutput(args) {
6456
6780
  sharedGate: gate,
6457
6781
  resolvedLimit: limit
6458
6782
  };
6459
- return finalizeSingleDoc(internal, args.input);
6783
+ return finalizeSingleDoc(internal, args.input, prepareFinalizer(internal));
6460
6784
  }
6461
6785
  async function finalizeModelOutputs(args) {
6462
6786
  const gate = finalizerGate(args);
@@ -6465,22 +6789,30 @@ async function finalizeModelOutputs(args) {
6465
6789
  if ("documentAuthorizationStage" in args || documentAuthorizationStages && (documentAuthorizationStages.length !== inputs.length || new Set(documentAuthorizationStages).size !== inputs.length)) {
6466
6790
  authorizationError("list finalization requires a distinct document stage per row");
6467
6791
  }
6792
+ if (!inputs.length) return [];
6793
+ const internal = {
6794
+ ...rest,
6795
+ scopePath: [...rest.scopePath ?? rest.plan.scopePath ?? []],
6796
+ sharedGate: gate,
6797
+ resolvedLimit: limit
6798
+ };
6799
+ const prepared = prepareFinalizer(internal, inputs.length > 1);
6468
6800
  return mapWithConcurrencyLimit(inputs, limit, (input, index) => {
6469
- const internal = {
6470
- ...rest,
6471
- scopePath: [...rest.scopePath ?? rest.plan.scopePath ?? []],
6472
- onFinalized: rest.onFinalized ? (output, context) => rest.onFinalized(output, context, index) : void 0,
6473
- documentAuthorizationStage: documentAuthorizationStages?.[index],
6474
- sharedGate: gate,
6475
- resolvedLimit: limit
6476
- };
6477
- return finalizeSingleDoc(internal, input);
6801
+ return finalizeSingleDoc(
6802
+ {
6803
+ ...internal,
6804
+ onFinalized: rest.onFinalized ? (output, context) => rest.onFinalized(output, context, index) : void 0,
6805
+ documentAuthorizationStage: documentAuthorizationStages?.[index]
6806
+ },
6807
+ input,
6808
+ prepared
6809
+ );
6478
6810
  });
6479
6811
  }
6480
- var import_utils28, isVirtualContainerValue3, getScopeVirtualsObject2, getScopeVirtualNames, getScopedPermissionSchema2, resolveGlobalPermissions, resolveDocPermissions, isAbsentDependency, __finalizeInternals;
6812
+ var import_utils29, isVirtualContainerValue3, getScopeVirtualsObject2, getScopeVirtualNames, getScopedPermissionSchema2, resolveGlobalPermissions, resolveDocPermissions, isAbsentDependency, __finalizeInternals;
6481
6813
  var init_finalize_model_output = __esm({
6482
6814
  "src/output/finalize-model-output.ts"() {
6483
- import_utils28 = require("@web-ts-toolkit/utils");
6815
+ import_utils29 = require("@web-ts-toolkit/utils");
6484
6816
  init_document();
6485
6817
  init_concurrency();
6486
6818
  init_request_work();
@@ -6493,20 +6825,20 @@ var init_finalize_model_output = __esm({
6493
6825
  init_field_catalog();
6494
6826
  init_virtual_populate_execution();
6495
6827
  init_include_output();
6496
- isVirtualContainerValue3 = (value) => (0, import_utils28.isPlainObject)(value) && "sub" in value;
6828
+ isVirtualContainerValue3 = (value) => (0, import_utils29.isPlainObject)(value) && "sub" in value;
6497
6829
  getScopeVirtualsObject2 = (rootVirtuals, scopePath) => {
6498
- if (!rootVirtuals || !(0, import_utils28.isPlainObject)(rootVirtuals)) return null;
6830
+ if (!rootVirtuals || !(0, import_utils29.isPlainObject)(rootVirtuals)) return null;
6499
6831
  if (scopePath.length === 0) return rootVirtuals;
6500
6832
  let current = rootVirtuals;
6501
6833
  for (let i = 0; i < scopePath.length; i += 2) {
6502
6834
  const field = scopePath[i];
6503
6835
  const subMarker = scopePath[i + 1];
6504
6836
  if (!field || subMarker !== "sub") return null;
6505
- if (!(0, import_utils28.isPlainObject)(current)) return null;
6837
+ if (!(0, import_utils29.isPlainObject)(current)) return null;
6506
6838
  const container = current[field];
6507
6839
  if (!isVirtualContainerValue3(container)) return null;
6508
6840
  current = container.sub;
6509
- if (!(0, import_utils28.isPlainObject)(current)) return null;
6841
+ if (!(0, import_utils29.isPlainObject)(current)) return null;
6510
6842
  }
6511
6843
  return current;
6512
6844
  };
@@ -6519,11 +6851,11 @@ var init_finalize_model_output = __esm({
6519
6851
  return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !isVirtualContainerValue3(v)).map(([k]) => k);
6520
6852
  };
6521
6853
  getScopedPermissionSchema2 = (root, scopePath) => {
6522
- if (!root || !(0, import_utils28.isPlainObject)(root)) return null;
6854
+ if (!root || !(0, import_utils29.isPlainObject)(root)) return null;
6523
6855
  if (scopePath.length === 0) return root;
6524
6856
  try {
6525
- const scoped = (0, import_utils28.get)(root, scopePath.join("."));
6526
- return (0, import_utils28.isPlainObject)(scoped) ? scoped : null;
6857
+ const scoped = (0, import_utils29.get)(root, scopePath.join("."));
6858
+ return (0, import_utils29.isPlainObject)(scoped) ? scoped : null;
6527
6859
  } catch {
6528
6860
  return null;
6529
6861
  }
@@ -6544,7 +6876,7 @@ var init_finalize_model_output = __esm({
6544
6876
  };
6545
6877
  resolveDocPermissions = async (args, isolatedForHook, scopeContext) => {
6546
6878
  if (args.docPermissions != null && typeof args.docPermissions === "object" && !Array.isArray(args.docPermissions)) {
6547
- const grants = args.docPermissions;
6879
+ const grants = deepIsolateValue(args.docPermissions);
6548
6880
  scopeContext.docPermissions = grants;
6549
6881
  return grants;
6550
6882
  }
@@ -6582,7 +6914,7 @@ var init_finalize_model_output = __esm({
6582
6914
  const grants = await args.sharedGate.run(
6583
6915
  () => hook.call(args.request, isolatedForHook, globalPerms, scopeContext)
6584
6916
  );
6585
- const normalized = (0, import_utils28.isPlainObject)(grants) ? grants : {};
6917
+ const normalized = (0, import_utils29.isPlainObject)(grants) ? grants : {};
6586
6918
  scopeContext.docPermissions = normalized;
6587
6919
  return normalized;
6588
6920
  }
@@ -6654,40 +6986,20 @@ __export(index_exports, {
6654
6986
  setModelOptions: () => setModelOptions
6655
6987
  });
6656
6988
  module.exports = __toCommonJS(index_exports);
6657
- var import_utils46 = require("@web-ts-toolkit/utils");
6989
+ var import_utils47 = require("@web-ts-toolkit/utils");
6658
6990
 
6659
6991
  // src/middleware.ts
6660
6992
  var import_express_json_router5 = __toESM(require("@web-ts-toolkit/express-json-router"));
6661
- var import_utils39 = require("@web-ts-toolkit/utils");
6993
+ var import_utils40 = require("@web-ts-toolkit/utils");
6662
6994
 
6663
6995
  // src/core.ts
6664
6996
  init_public_admission();
6665
- var import_utils38 = require("@web-ts-toolkit/utils");
6997
+ var import_utils39 = require("@web-ts-toolkit/utils");
6666
6998
  init_options();
6667
-
6668
- // src/meta.ts
6669
- init_runtime();
6670
- init_runtime_context();
6671
- var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
6672
- var getModelRef = (modelName, refPath) => {
6673
- const direct = getRuntime5().getModelRef(modelName, refPath);
6674
- if (direct) return direct;
6675
- if (!refPath.includes(".")) return null;
6676
- try {
6677
- const runtime = getRuntime5();
6678
- const instance = runtime.getModelInstance?.(
6679
- modelName
6680
- );
6681
- const schemaPath = instance?.schema?.path?.(refPath);
6682
- const ref = schemaPath?.options?.ref ?? schemaPath?.caster?.options?.ref ?? null;
6683
- return typeof ref === "string" && ref.length > 0 ? ref : null;
6684
- } catch {
6685
- return null;
6686
- }
6687
- };
6999
+ init_permission();
6688
7000
 
6689
7001
  // src/services/base.ts
6690
- var import_utils23 = require("@web-ts-toolkit/utils");
7002
+ var import_utils24 = require("@web-ts-toolkit/utils");
6691
7003
  init_options();
6692
7004
  init_helpers();
6693
7005
  init_document();
@@ -6813,7 +7125,7 @@ function requestFieldScope(options) {
6813
7125
  init_public_admission();
6814
7126
 
6815
7127
  // src/correlated-includes.ts
6816
- var import_utils19 = require("@web-ts-toolkit/utils");
7128
+ var import_utils20 = require("@web-ts-toolkit/utils");
6817
7129
  init_request_complexity();
6818
7130
  init_sort_policy();
6819
7131
  var PARENT_MARKER_KEY = "$parent";
@@ -6826,10 +7138,10 @@ var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
6826
7138
  var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
6827
7139
  var ownKeys = (value) => Object.keys(value);
6828
7140
  function isParentRefMarker(value) {
6829
- return (0, import_utils19.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
7141
+ return (0, import_utils20.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
6830
7142
  }
6831
7143
  function isEscapedParentRef(value) {
6832
- return (0, import_utils19.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
7144
+ return (0, import_utils20.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
6833
7145
  }
6834
7146
  function hasMarkerKey(value) {
6835
7147
  return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
@@ -6848,12 +7160,12 @@ function containsParentMarkerShape(value) {
6848
7160
  const seen = /* @__PURE__ */ new Set();
6849
7161
  const visit = (current) => {
6850
7162
  if (isParentRefMarker(current) || isEscapedParentRef(current)) return true;
6851
- if ((0, import_utils19.isArray)(current)) {
7163
+ if ((0, import_utils20.isArray)(current)) {
6852
7164
  if (seen.has(current)) return false;
6853
7165
  seen.add(current);
6854
7166
  return current.some(visit);
6855
7167
  }
6856
- if ((0, import_utils19.isPlainObject)(current)) {
7168
+ if ((0, import_utils20.isPlainObject)(current)) {
6857
7169
  if (seen.has(current)) return false;
6858
7170
  seen.add(current);
6859
7171
  if (hasMarkerKey(current)) return true;
@@ -6883,7 +7195,7 @@ function scanTemplateValueNode(value, path, issues) {
6883
7195
  }
6884
7196
  return;
6885
7197
  }
6886
- if ((0, import_utils19.isPlainObject)(value)) {
7198
+ if ((0, import_utils20.isPlainObject)(value)) {
6887
7199
  if (hasMarkerKey(value)) {
6888
7200
  issues.push({
6889
7201
  message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
@@ -6894,7 +7206,7 @@ function scanTemplateValueNode(value, path, issues) {
6894
7206
  scanTemplateObjectEntries(value, path, issues);
6895
7207
  return;
6896
7208
  }
6897
- if ((0, import_utils19.isArray)(value)) {
7209
+ if ((0, import_utils20.isArray)(value)) {
6898
7210
  value.forEach((item, index) => scanTemplateValueNode(item, [...path, index], issues));
6899
7211
  }
6900
7212
  }
@@ -6935,7 +7247,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
6935
7247
  continue;
6936
7248
  }
6937
7249
  if (LOGICAL_CLAUSE_KEYS.has(key)) {
6938
- if (!(0, import_utils19.isArray)(child)) {
7250
+ if (!(0, import_utils20.isArray)(child)) {
6939
7251
  scanTemplateValueNode(child, childPath, issues);
6940
7252
  continue;
6941
7253
  }
@@ -6948,7 +7260,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
6948
7260
  });
6949
7261
  return;
6950
7262
  }
6951
- if ((0, import_utils19.isPlainObject)(clause)) {
7263
+ if ((0, import_utils20.isPlainObject)(clause)) {
6952
7264
  scanTemplateObjectEntries(clause, clausePath, issues);
6953
7265
  }
6954
7266
  });
@@ -6959,7 +7271,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
6959
7271
  }
6960
7272
  function validateCorrelatedFilterTemplate(filter2) {
6961
7273
  const issues = [];
6962
- if (!(0, import_utils19.isPlainObject)(filter2)) {
7274
+ if (!(0, import_utils20.isPlainObject)(filter2)) {
6963
7275
  issues.push({ message: "Correlated include filter must be an object", path: [] });
6964
7276
  return issues;
6965
7277
  }
@@ -7002,13 +7314,13 @@ function collectFilterReferencePaths(filter2) {
7002
7314
  return;
7003
7315
  }
7004
7316
  if (isEscapedParentRef(value)) return;
7005
- if ((0, import_utils19.isArray)(value)) {
7317
+ if ((0, import_utils20.isArray)(value)) {
7006
7318
  if (seen.has(value)) return;
7007
7319
  seen.add(value);
7008
7320
  value.forEach(visitValue);
7009
7321
  return;
7010
7322
  }
7011
- if ((0, import_utils19.isPlainObject)(value)) {
7323
+ if ((0, import_utils20.isPlainObject)(value)) {
7012
7324
  if (seen.has(value)) return;
7013
7325
  seen.add(value);
7014
7326
  for (const [key, child] of Object.entries(value)) {
@@ -7054,7 +7366,7 @@ function getOwnPathValue(parent, path) {
7054
7366
  return current;
7055
7367
  }
7056
7368
  function cloneSubstitutedValue(value) {
7057
- if ((0, import_utils19.isPlainObject)(value) || (0, import_utils19.isArray)(value)) return (0, import_utils19.cloneDeep)(value);
7369
+ if ((0, import_utils20.isPlainObject)(value) || (0, import_utils20.isArray)(value)) return (0, import_utils20.cloneDeep)(value);
7058
7370
  return value;
7059
7371
  }
7060
7372
  function literalParentRefObject(path) {
@@ -7095,7 +7407,7 @@ function resolveObjectEntries(obj, parent) {
7095
7407
  continue;
7096
7408
  }
7097
7409
  if (LOGICAL_CLAUSE_KEYS.has(key)) {
7098
- if (!(0, import_utils19.isArray)(child)) {
7410
+ if (!(0, import_utils20.isArray)(child)) {
7099
7411
  out[key] = resolveValueNode(child, parent, false);
7100
7412
  continue;
7101
7413
  }
@@ -7103,7 +7415,7 @@ function resolveObjectEntries(obj, parent) {
7103
7415
  if (isParentRefMarker(clause) || isEscapedParentRef(clause)) {
7104
7416
  throw new CorrelatedReferenceError(`Unsupported $parent reference as a direct ${key} clause`);
7105
7417
  }
7106
- if (!(0, import_utils19.isPlainObject)(clause)) return cloneSubstitutedValue(clause);
7418
+ if (!(0, import_utils20.isPlainObject)(clause)) return cloneSubstitutedValue(clause);
7107
7419
  return resolveObjectEntries(clause, parent);
7108
7420
  });
7109
7421
  continue;
@@ -7132,10 +7444,10 @@ function resolveValueNode(template, parent, bare) {
7132
7444
  }
7133
7445
  const value = getOwnPathValue(parent, path);
7134
7446
  if (value === void 0 || value === null) throw UNRESOLVABLE;
7135
- if (bare && (0, import_utils19.isPlainObject)(value)) return { $eq: (0, import_utils19.cloneDeep)(value) };
7447
+ if (bare && (0, import_utils20.isPlainObject)(value)) return { $eq: (0, import_utils20.cloneDeep)(value) };
7136
7448
  return cloneSubstitutedValue(value);
7137
7449
  }
7138
- if ((0, import_utils19.isPlainObject)(template)) {
7450
+ if ((0, import_utils20.isPlainObject)(template)) {
7139
7451
  if (hasMarkerKey(template)) {
7140
7452
  throw new CorrelatedReferenceError(
7141
7453
  'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }'
@@ -7143,7 +7455,7 @@ function resolveValueNode(template, parent, bare) {
7143
7455
  }
7144
7456
  return resolveObjectEntries(template, parent);
7145
7457
  }
7146
- if ((0, import_utils19.isArray)(template)) {
7458
+ if ((0, import_utils20.isArray)(template)) {
7147
7459
  return template.map((item) => resolveValueNode(item, parent, false));
7148
7460
  }
7149
7461
  return template;
@@ -7177,7 +7489,7 @@ function resolveCorrelatedIdTemplate(id, parent) {
7177
7489
  }
7178
7490
  function resolveCorrelatedFilterTemplate(filter2, parent) {
7179
7491
  try {
7180
- if (!(0, import_utils19.isPlainObject)(filter2) || isParentRefMarker(filter2) || isEscapedParentRef(filter2)) {
7492
+ if (!(0, import_utils20.isPlainObject)(filter2) || isParentRefMarker(filter2) || isEscapedParentRef(filter2)) {
7181
7493
  throw new CorrelatedReferenceError("Correlated include filter must be an object");
7182
7494
  }
7183
7495
  return { status: "resolved", filter: resolveObjectEntries(filter2, parent) };
@@ -7192,7 +7504,7 @@ function validateExpandedCorrelatedOperands(expanded, limits) {
7192
7504
  var MAX_NESTED_INCLUDE_DEPTH = 10;
7193
7505
  function validateCorrelatedArgsShape(args, op, depth) {
7194
7506
  if (args === void 0) return [];
7195
- if (!(0, import_utils19.isPlainObject)(args)) return ["Correlated include args must be an object"];
7507
+ if (!(0, import_utils20.isPlainObject)(args)) return ["Correlated include args must be an object"];
7196
7508
  const record3 = args;
7197
7509
  const allowed = op === "list" ? /* @__PURE__ */ new Set(["select", "sort", "skip", "limit", "page", "pageSize", "include"]) : /* @__PURE__ */ new Set(["select", "sort", "include"]);
7198
7510
  for (const key of Object.keys(record3)) {
@@ -7214,7 +7526,7 @@ function validateCorrelatedArgsShape(args, op, depth) {
7214
7526
  return [];
7215
7527
  }
7216
7528
  function validateCorrelatedIncludeShape(entry, depth = 0) {
7217
- if (!(0, import_utils19.isPlainObject)(entry)) return ["Correlated include must be an object"];
7529
+ if (!(0, import_utils20.isPlainObject)(entry)) return ["Correlated include must be an object"];
7218
7530
  const record3 = entry;
7219
7531
  if (record3.mode !== "correlated") return ['Correlated include mode must be "correlated"'];
7220
7532
  if (typeof record3.model !== "string" || record3.model.length === 0)
@@ -7227,7 +7539,7 @@ function validateCorrelatedIncludeShape(entry, depth = 0) {
7227
7539
  if ("localField" in record3 || "foreignField" in record3) {
7228
7540
  return ["Correlated includes must not carry localField or foreignField"];
7229
7541
  }
7230
- if (record3.options !== void 0 && !((0, import_utils19.isPlainObject)(record3.options) && Object.keys(record3.options).length === 0)) {
7542
+ if (record3.options !== void 0 && !((0, import_utils20.isPlainObject)(record3.options) && Object.keys(record3.options).length === 0)) {
7231
7543
  return ["Correlated include options must be absent or empty"];
7232
7544
  }
7233
7545
  const hasId = record3.id !== void 0;
@@ -7249,7 +7561,7 @@ function validateCorrelatedIncludeShape(entry, depth = 0) {
7249
7561
  return validateCorrelatedArgsShape(record3.args, record3.op, depth);
7250
7562
  }
7251
7563
  function isCorrelatedInclude(value) {
7252
- return (0, import_utils19.isPlainObject)(value) && value.mode === "correlated";
7564
+ return (0, import_utils20.isPlainObject)(value) && value.mode === "correlated";
7253
7565
  }
7254
7566
 
7255
7567
  // src/services/base.ts
@@ -7473,9 +7785,31 @@ function castCountGroups(input, values) {
7473
7785
  // src/services/base.ts
7474
7786
  init_service_authorization();
7475
7787
 
7788
+ // src/meta.ts
7789
+ init_runtime();
7790
+ init_runtime_context();
7791
+ var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
7792
+ var getModelRef = (modelName, refPath, runtime = getRuntime5()) => {
7793
+ const direct = runtime.getModelRef(modelName, refPath);
7794
+ if (direct) return direct;
7795
+ if (!refPath.includes(".")) return null;
7796
+ try {
7797
+ const instance = runtime.getModelInstance?.(
7798
+ modelName
7799
+ );
7800
+ const schemaPath = instance?.schema?.path?.(refPath);
7801
+ const element = schemaPath?.$embeddedSchemaType ?? schemaPath?.embeddedSchemaType ?? schemaPath?.caster;
7802
+ const ref = schemaPath?.options?.ref ?? element?.options?.ref ?? null;
7803
+ return typeof ref === "string" && ref.length > 0 ? ref : null;
7804
+ } catch {
7805
+ return null;
7806
+ }
7807
+ };
7808
+
7476
7809
  // src/acl/related-execution.ts
7477
7810
  init_public_admission();
7478
7811
  init_model_output_planning();
7812
+ init_populate_target_configuration();
7479
7813
  init_authorization_snapshot();
7480
7814
 
7481
7815
  // src/acl/service-execution.ts
@@ -7513,9 +7847,9 @@ function createServiceExecution(receiver, access, authorization, applicability =
7513
7847
  receiver,
7514
7848
  binding,
7515
7849
  authorization: call,
7516
- access: readServiceAuthorization(call, binding).access,
7850
+ access: readServiceAuthorizationAccess(call, binding),
7517
7851
  family: execution,
7518
- hydration: resolveHydration(readServiceAuthorization(call, binding).configuration, hydration)
7852
+ hydration: resolveHydration(readServiceAuthorizationHydration(call, binding), hydration)
7519
7853
  })
7520
7854
  );
7521
7855
  return execution;
@@ -7526,7 +7860,7 @@ function resolveHydration(configuration, source) {
7526
7860
  }
7527
7861
  function withExecutionHydration(receiver, parent, source) {
7528
7862
  const record3 = readServiceExecution(receiver, parent);
7529
- const { configuration } = readServiceAuthorization(record3.authorization, record3.binding);
7863
+ const configuration = readServiceAuthorizationHydration(record3.authorization, record3.binding);
7530
7864
  const execution = Object.freeze(/* @__PURE__ */ Object.create(null));
7531
7865
  executions.set(execution, Object.freeze({ ...record3, hydration: resolveHydration(configuration, source) }));
7532
7866
  return execution;
@@ -7563,7 +7897,7 @@ function deriveServiceExecution(receiver, parent, access, applicability = "servi
7563
7897
  const execution = Object.freeze(/* @__PURE__ */ Object.create(null));
7564
7898
  executions.set(
7565
7899
  execution,
7566
- Object.freeze({ ...record3, authorization: call, access: readServiceAuthorization(call, record3.binding).access })
7900
+ Object.freeze({ ...record3, authorization: call, access: readServiceAuthorizationAccess(call, record3.binding) })
7567
7901
  );
7568
7902
  return execution;
7569
7903
  }
@@ -7770,16 +8104,15 @@ function captureRelatedTargets(runtime, modelName, args = {}, depth = 0, globalO
7770
8104
  for (const entry of entries(args.populate)) {
7771
8105
  const path = typeof entry === "string" ? entry : entry.path;
7772
8106
  if (typeof entry !== "string") assertPopulateAccess(entry.access);
7773
- const model = getModelRef(modelName, path);
7774
- if (model && runtime.hasModel(model))
8107
+ const model = getModelRef(modelName, path, runtime);
8108
+ if (model) {
8109
+ const target = capturePopulateTargetConfiguration(runtime, model, `${modelName}.${path}`);
7775
8110
  targets.push({
7776
8111
  path,
7777
8112
  modelName: model,
7778
- configuration: captureModelAuthorizationConfiguration(runtime, model, void 0, globalOptions),
8113
+ configuration: captureModelAuthorizationConfiguration(runtime, model, target.options, globalOptions),
7779
8114
  staticObjectIdReference: staticObjectIdReference(runtime, modelName, path, model)
7780
8115
  });
7781
- else if (model && !(globalOptions.requireRegisteredPopulateModels === void 0 ? true : globalOptions.requireRegisteredPopulateModels)) {
7782
- targets.push({ path, modelName: model, kind: "passthrough", configuration: { catalog: { paths: [] } } });
7783
8116
  }
7784
8117
  }
7785
8118
  for (const entry of entries(args.include)) {
@@ -7965,20 +8298,20 @@ var copyAuthorizedOutput = (value) => {
7965
8298
  transferDocumentAuthorizationGraph(value, copy);
7966
8299
  return copy;
7967
8300
  };
7968
- var isVirtualContainerValueForGuard = (value) => (0, import_utils23.isPlainObject)(value) && "sub" in value;
8301
+ var isVirtualContainerValueForGuard = (value) => (0, import_utils24.isPlainObject)(value) && "sub" in value;
7969
8302
  var getScopeVirtualsForGuard = (rootVirtuals, scopePath) => {
7970
- if (!rootVirtuals || !(0, import_utils23.isPlainObject)(rootVirtuals)) return null;
8303
+ if (!rootVirtuals || !(0, import_utils24.isPlainObject)(rootVirtuals)) return null;
7971
8304
  if (scopePath.length === 0) return rootVirtuals;
7972
8305
  let current = rootVirtuals;
7973
8306
  for (let i = 0; i < scopePath.length; i += 2) {
7974
8307
  const field = scopePath[i];
7975
8308
  const subMarker = scopePath[i + 1];
7976
8309
  if (!field || subMarker !== "sub") return null;
7977
- if (!(0, import_utils23.isPlainObject)(current)) return null;
8310
+ if (!(0, import_utils24.isPlainObject)(current)) return null;
7978
8311
  const container = current[field];
7979
8312
  if (!isVirtualContainerValueForGuard(container)) return null;
7980
8313
  current = container.sub;
7981
- if (!(0, import_utils23.isPlainObject)(current)) return null;
8314
+ if (!(0, import_utils24.isPlainObject)(current)) return null;
7982
8315
  }
7983
8316
  return current;
7984
8317
  };
@@ -8044,7 +8377,7 @@ var sanitizeSubScopeVirtualKeys = (data, snapshot, sub) => {
8044
8377
  for (const entry of value) stripScope(entry, currentScope);
8045
8378
  return;
8046
8379
  }
8047
- if (!(0, import_utils23.isPlainObject)(value)) return;
8380
+ if (!(0, import_utils24.isPlainObject)(value)) return;
8048
8381
  const record3 = value;
8049
8382
  const leaves = getScopeVirtualLeafNames(snapshot, currentScope);
8050
8383
  for (const leaf of leaves) {
@@ -8057,7 +8390,7 @@ var sanitizeSubScopeVirtualKeys = (data, snapshot, sub) => {
8057
8390
  for (const key of Object.keys(record3)) {
8058
8391
  if (leaves.has(key) || containers.has(key)) continue;
8059
8392
  const child = record3[key];
8060
- if (Array.isArray(child) || (0, import_utils23.isPlainObject)(child)) {
8393
+ if (Array.isArray(child) || (0, import_utils24.isPlainObject)(child)) {
8061
8394
  stripScope(child, currentScope);
8062
8395
  }
8063
8396
  }
@@ -8069,7 +8402,7 @@ var LOGICAL_FILTER_KEYS = /* @__PURE__ */ new Set(["$and", "$or", "$nor"]);
8069
8402
  var isOperatorKey = (key) => key.startsWith("$");
8070
8403
  var REMOVED_VIRTUAL_FILTER = /* @__PURE__ */ Symbol("removed virtual filter clause");
8071
8404
  var rewriteFilterObject = (value, visit) => {
8072
- if (!(0, import_utils23.isPlainObject)(value)) return value;
8405
+ if (!(0, import_utils24.isPlainObject)(value)) return value;
8073
8406
  let changed = false;
8074
8407
  const entries = [];
8075
8408
  for (const [key, child] of Object.entries(value)) {
@@ -8100,7 +8433,7 @@ var resolveVirtualFilterField = (path, scope2) => {
8100
8433
  if (!Object.hasOwn(scope2, segment) || scope2[segment] === void 0) return null;
8101
8434
  const definition = scope2[segment];
8102
8435
  if (!isVirtualContainerValueForGuard(definition)) return REMOVED_VIRTUAL_FILTER;
8103
- scope2 = (0, import_utils23.isPlainObject)(definition.sub) ? definition.sub : null;
8436
+ scope2 = (0, import_utils24.isPlainObject)(definition.sub) ? definition.sub : null;
8104
8437
  inContainer = true;
8105
8438
  }
8106
8439
  return scope2;
@@ -8112,7 +8445,7 @@ var stripVirtualFilterOperator = (key, value, scope2) => {
8112
8445
  }
8113
8446
  if (key === "$all") {
8114
8447
  return rewriteFilterClauses(value, (entry) => {
8115
- if (!(0, import_utils23.isPlainObject)(entry) || Object.keys(entry).length !== 1 || !Object.hasOwn(entry, "$elemMatch")) return entry;
8448
+ if (!(0, import_utils24.isPlainObject)(entry) || Object.keys(entry).length !== 1 || !Object.hasOwn(entry, "$elemMatch")) return entry;
8116
8449
  return rewriteFilterObject(entry, (operator, operand) => stripVirtualFilterOperator(operator, operand, scope2));
8117
8450
  });
8118
8451
  }
@@ -8127,7 +8460,7 @@ var stripVirtualFilterQuery = (value, scope2, element = false) => {
8127
8460
  if (isOperatorKey(key)) return element ? stripVirtualFilterOperator(key, child, scope2) : child;
8128
8461
  const fieldScope = resolveVirtualFilterField(key, scope2);
8129
8462
  if (fieldScope === REMOVED_VIRTUAL_FILTER) return REMOVED_VIRTUAL_FILTER;
8130
- if (!fieldScope || !(0, import_utils23.isPlainObject)(child)) return child;
8463
+ if (!fieldScope || !(0, import_utils24.isPlainObject)(child)) return child;
8131
8464
  if (Object.keys(child).some(isOperatorKey)) {
8132
8465
  return rewriteFilterObject(
8133
8466
  child,
@@ -8149,9 +8482,9 @@ var stripSubVirtualKeysFromFilter = (filter2, snapshot, sub) => {
8149
8482
  function getCorrelatedTemplateDepth(includes) {
8150
8483
  let max = 0;
8151
8484
  const visit = (entries, depth) => {
8152
- const list = (0, import_utils23.castArray)(entries).filter(Boolean);
8485
+ const list = (0, import_utils24.castArray)(entries).filter(Boolean);
8153
8486
  for (const entry of list) {
8154
- if (!(0, import_utils23.isPlainObject)(entry)) continue;
8487
+ if (!(0, import_utils24.isPlainObject)(entry)) continue;
8155
8488
  max = Math.max(max, depth);
8156
8489
  const nested = entry.args?.include;
8157
8490
  if (nested !== void 0) {
@@ -8176,11 +8509,11 @@ function validateClientFilter(filter2) {
8176
8509
  }
8177
8510
  const blockedOperators = /* @__PURE__ */ new Set(["$where", "$expr", "$function", "$accumulator"]);
8178
8511
  const visit = (value, path) => {
8179
- if ((0, import_utils23.isArray)(value)) {
8512
+ if ((0, import_utils24.isArray)(value)) {
8180
8513
  value.forEach((item, index) => visit(item, `${path}[${index}]`));
8181
8514
  return;
8182
8515
  }
8183
- if (!(0, import_utils23.isPlainObject)(value)) return;
8516
+ if (!(0, import_utils24.isPlainObject)(value)) return;
8184
8517
  Object.entries(value).forEach(([key, child]) => {
8185
8518
  const nextPath = path ? `${path}.${key}` : key;
8186
8519
  if (blockedOperators.has(key)) {
@@ -8385,7 +8718,7 @@ var Base = class {
8385
8718
  }
8386
8719
  processInclude(include, authorization) {
8387
8720
  assertPublicAdmission(include, "include");
8388
- const items = (0, import_utils23.compact)((0, import_utils23.castArray)(include));
8721
+ const items = (0, import_utils24.compact)((0, import_utils24.castArray)(include));
8389
8722
  const legacy = [];
8390
8723
  const correlated = [];
8391
8724
  const correlatedReferenceFields = [];
@@ -8421,23 +8754,23 @@ var Base = class {
8421
8754
  else this.assertIncludeOutputPaths([...legacy, ...correlated]);
8422
8755
  let includeLocalFields = [];
8423
8756
  let includePaths = [];
8424
- (0, import_utils23.forEach)(legacy, (inc) => {
8757
+ (0, import_utils24.forEach)(legacy, (inc) => {
8425
8758
  includeLocalFields.push(inc.localField);
8426
8759
  includePaths.push(inc.path);
8427
8760
  });
8428
- includeLocalFields = (0, import_utils23.uniq)((0, import_utils23.compact)(includeLocalFields));
8429
- includePaths = (0, import_utils23.uniq)((0, import_utils23.compact)(includePaths));
8761
+ includeLocalFields = (0, import_utils24.uniq)((0, import_utils24.compact)(includeLocalFields));
8762
+ includePaths = (0, import_utils24.uniq)((0, import_utils24.compact)(includePaths));
8430
8763
  return {
8431
8764
  includes: legacy,
8432
8765
  correlatedIncludes: correlated,
8433
8766
  includeLocalFields,
8434
8767
  includePaths,
8435
- correlatedReferenceFields: (0, import_utils23.uniq)((0, import_utils23.compact)(correlatedReferenceFields))
8768
+ correlatedReferenceFields: (0, import_utils24.uniq)((0, import_utils24.compact)(correlatedReferenceFields))
8436
8769
  };
8437
8770
  }
8438
8771
  validateIncludeOutputPath(path, modelName, configuration) {
8439
8772
  const permissionField = configuration ? configuration.documentPermissionField ?? "_permissions" : getModelOption(modelName, "documentPermissionField");
8440
- if ((0, import_utils23.get)((0, import_utils23.set)({}, path, true), permissionField) !== void 0 || (0, import_utils23.get)((0, import_utils23.set)({}, permissionField, true), path) !== void 0) {
8773
+ if ((0, import_utils24.get)((0, import_utils24.set)({}, path, true), permissionField) !== void 0 || (0, import_utils24.get)((0, import_utils24.set)({}, permissionField, true), path) !== void 0) {
8441
8774
  this.throwClientRequestError(
8442
8775
  "bad_request" /* BadRequest */,
8443
8776
  `Include output path ${path} overlaps document permission field ${permissionField} on model ${modelName}`
@@ -8462,9 +8795,9 @@ var Base = class {
8462
8795
  const walk = (node, prefix) => {
8463
8796
  for (const [key, value] of Object.entries(node)) {
8464
8797
  if (value === void 0) continue;
8465
- if ((0, import_utils23.isPlainObject)(value) && "sub" in value) {
8798
+ if ((0, import_utils24.isPlainObject)(value) && "sub" in value) {
8466
8799
  const sub = value.sub;
8467
- if ((0, import_utils23.isPlainObject)(sub)) walk(sub, prefix ? `${prefix}.${key}` : key);
8800
+ if ((0, import_utils24.isPlainObject)(sub)) walk(sub, prefix ? `${prefix}.${key}` : key);
8468
8801
  continue;
8469
8802
  }
8470
8803
  out.push(prefix ? `${prefix}.${key}` : key);
@@ -8517,7 +8850,7 @@ var Base = class {
8517
8850
  this.throwClientRequestError("bad_request" /* BadRequest */, `Model ${modelName} not found`);
8518
8851
  }
8519
8852
  for (const entry of entries) {
8520
- if (!(0, import_utils23.isPlainObject)(entry)) continue;
8853
+ if (!(0, import_utils24.isPlainObject)(entry)) continue;
8521
8854
  const models = visited.get(entry) ?? /* @__PURE__ */ new Set();
8522
8855
  if (models.has(modelName)) continue;
8523
8856
  models.add(modelName);
@@ -8530,7 +8863,7 @@ var Base = class {
8530
8863
  const target = targets?.find((target2) => target2.path === entry.path);
8531
8864
  if (nested)
8532
8865
  pending.push({
8533
- entries: (0, import_utils23.compact)((0, import_utils23.castArray)(nested)),
8866
+ entries: (0, import_utils24.compact)((0, import_utils24.castArray)(nested)),
8534
8867
  modelName: entry.model,
8535
8868
  configuration: target?.configuration,
8536
8869
  targets: target?.relatedTargets
@@ -8540,7 +8873,7 @@ var Base = class {
8540
8873
  }
8541
8874
  }
8542
8875
  getForeignKeyValues(value) {
8543
- return (0, import_utils23.flatten)((0, import_utils23.castArray)(value));
8876
+ return (0, import_utils24.flatten)((0, import_utils24.castArray)(value));
8544
8877
  }
8545
8878
  getForeignKey(value) {
8546
8879
  return String(value);
@@ -8548,7 +8881,7 @@ var Base = class {
8548
8881
  buildForeignKeyIndex(rows2, foreignField) {
8549
8882
  const index = /* @__PURE__ */ new Map();
8550
8883
  for (const row of rows2) {
8551
- for (const value of this.getForeignKeyValues((0, import_utils23.get)(row, foreignField))) {
8884
+ for (const value of this.getForeignKeyValues((0, import_utils24.get)(row, foreignField))) {
8552
8885
  const key = this.getForeignKey(value);
8553
8886
  const values = index.get(key);
8554
8887
  if (values) values.push(row);
@@ -8584,9 +8917,9 @@ var Base = class {
8584
8917
  async includeDocs(docs, include, parentAuthorization) {
8585
8918
  assertPublicAdmission(include, "include");
8586
8919
  if (!include) return docs;
8587
- const includes = (0, import_utils23.compact)((0, import_utils23.castArray)(include));
8920
+ const includes = (0, import_utils24.compact)((0, import_utils24.castArray)(include));
8588
8921
  if (includes.length === 0) return docs;
8589
- const isSingle = !(0, import_utils23.isArray)(docs);
8922
+ const isSingle = !(0, import_utils24.isArray)(docs);
8590
8923
  let docList = (isSingle ? [docs] : docs).map(prepareIncludeOutput);
8591
8924
  for (let x = 0; x < includes.length; x++) {
8592
8925
  const include2 = includes[x];
@@ -8616,7 +8949,7 @@ var Base = class {
8616
8949
  const svc = await this.getAuthorizedTargetService(model, "read", path, parentAuthorization);
8617
8950
  for (let y = 0; y < docs.length; y++) {
8618
8951
  const doc = docs[y];
8619
- const localValue = (0, import_utils23.get)(doc, localField);
8952
+ const localValue = (0, import_utils24.get)(doc, localField);
8620
8953
  const execution = this.relatedExecution(svc, include, parentAuthorization);
8621
8954
  const prepared = await prepareModelQuery(
8622
8955
  svc,
@@ -8651,15 +8984,15 @@ var Base = class {
8651
8984
  const { model, op, path, localField, foreignField, filter: _filters, args = {}, options = {} } = include;
8652
8985
  const svc = await this.getAuthorizedTargetService(model, op, path, parentAuthorization);
8653
8986
  const includeLocalValues = [];
8654
- (0, import_utils23.forEach)(docs, (doc) => {
8655
- includeLocalValues.push(...this.getForeignKeyValues((0, import_utils23.get)(doc, localField)));
8987
+ (0, import_utils24.forEach)(docs, (doc) => {
8988
+ includeLocalValues.push(...this.getForeignKeyValues((0, import_utils24.get)(doc, localField)));
8656
8989
  });
8657
8990
  const execution = this.relatedExecution(svc, include, parentAuthorization);
8658
8991
  const prepared = await prepareModelQuery(
8659
8992
  svc,
8660
8993
  execution,
8661
8994
  { kind: "client-read", filter: _filters ?? {} },
8662
- { [foreignField]: { $in: (0, import_utils23.flatten)(includeLocalValues) } },
8995
+ { [foreignField]: { $in: (0, import_utils24.flatten)(includeLocalValues) } },
8663
8996
  true
8664
8997
  );
8665
8998
  const trustedArgs = this.sanitizeIncludeArgs(args, "list");
@@ -8702,7 +9035,7 @@ var Base = class {
8702
9035
  }
8703
9036
  for (let y = 0; y < docs.length; y++) {
8704
9037
  const doc = docs[y];
8705
- const matches = this.getIndexedMatches(index, (0, import_utils23.get)(doc, localField));
9038
+ const matches = this.getIndexedMatches(index, (0, import_utils24.get)(doc, localField));
8706
9039
  const isolated = matches.map((m) => copyAuthorizedOutput(m));
8707
9040
  attachIncludeOutput(doc, path, op === "list" ? isolated : isolated[0]);
8708
9041
  }
@@ -8711,7 +9044,7 @@ var Base = class {
8711
9044
  async includeDocsCount(docs, include, parentAuthorization) {
8712
9045
  const { model, path, localField, foreignField, filter: _filters } = include;
8713
9046
  const svc = await this.getAuthorizedTargetService(model, "count", path, parentAuthorization);
8714
- const groups = prepareCountGroups(docs, (doc) => (0, import_utils23.get)(doc, localField));
9047
+ const groups = prepareCountGroups(docs, (doc) => (0, import_utils24.get)(doc, localField));
8715
9048
  const execution = this.relatedExecution(svc, include, parentAuthorization);
8716
9049
  const prepared = await prepareModelQuery(
8717
9050
  svc,
@@ -8765,7 +9098,7 @@ var Base = class {
8765
9098
  }
8766
9099
  const state = this.getCorrelatedExecState();
8767
9100
  docs = docs.map(prepareIncludeOutput);
8768
- const resolvedSnapshots = snapshots2 ?? docs.map((doc) => (0, import_utils23.cloneDeep)(toObject(getIncludeSource(doc))));
9101
+ const resolvedSnapshots = snapshots2 ?? docs.map((doc) => (0, import_utils24.cloneDeep)(toObject(getIncludeSource(doc))));
8769
9102
  for (const include of correlatedIncludes) {
8770
9103
  switch (include.op) {
8771
9104
  case "read":
@@ -9114,10 +9447,10 @@ var Base = class {
9114
9447
  }
9115
9448
  let ret = result2.data;
9116
9449
  if (sqOptions.path) {
9117
- ret = (0, import_utils23.isArray)(ret) ? (0, import_utils23.flatten)(ret.map((v) => (0, import_utils23.get)(v, sqOptions.path))) : (0, import_utils23.get)(ret, sqOptions.path);
9450
+ ret = (0, import_utils24.isArray)(ret) ? (0, import_utils24.flatten)(ret.map((v) => (0, import_utils24.get)(v, sqOptions.path))) : (0, import_utils24.get)(ret, sqOptions.path);
9118
9451
  }
9119
9452
  if (sqOptions.compact) {
9120
- ret = (0, import_utils23.compact)((0, import_utils23.castArray)(ret));
9453
+ ret = (0, import_utils24.compact)((0, import_utils24.castArray)(ret));
9121
9454
  }
9122
9455
  return ret;
9123
9456
  }
@@ -9129,11 +9462,11 @@ var Base = class {
9129
9462
  };
9130
9463
 
9131
9464
  // src/services/service.ts
9132
- var import_utils34 = require("@web-ts-toolkit/utils");
9465
+ var import_utils35 = require("@web-ts-toolkit/utils");
9133
9466
  var import_just_diff = require("just-diff");
9134
9467
 
9135
9468
  // src/model.ts
9136
- var import_utils24 = require("@web-ts-toolkit/utils");
9469
+ var import_utils25 = require("@web-ts-toolkit/utils");
9137
9470
  init_runtime_context();
9138
9471
  init_runtime();
9139
9472
  function admitModelPersistence(adapter, scheduler) {
@@ -9209,7 +9542,7 @@ var Model = class {
9209
9542
  }
9210
9543
  /** Cast a copy using this model's query schema/setters, without executing query middleware or persistence. */
9211
9544
  castFilter(filter2) {
9212
- const query = this.mongooseModel.countDocuments((0, import_utils24.cloneDeep)(filter2));
9545
+ const query = this.mongooseModel.countDocuments((0, import_utils25.cloneDeep)(filter2));
9213
9546
  return query.cast(this.mongooseModel);
9214
9547
  }
9215
9548
  // see https://mongoosejs.com/docs/api.html#model_Model.distinct
@@ -9223,7 +9556,7 @@ var Model = class {
9223
9556
  var model_default = Model;
9224
9557
 
9225
9558
  // src/acl/distinct-field-authorization.ts
9226
- var import_utils25 = require("@web-ts-toolkit/utils");
9559
+ var import_utils26 = require("@web-ts-toolkit/utils");
9227
9560
  init_field_authorization();
9228
9561
  init_field_catalog();
9229
9562
  var schemaVariants = (schema) => Object.values(schema.discriminators ?? {});
@@ -9305,8 +9638,8 @@ function isDistinctFieldAuthorized(args) {
9305
9638
  if (path !== field && !path.startsWith(`${field}.`) && !field.startsWith(`${path}.`)) continue;
9306
9639
  if (path.startsWith(`${field}.`) && (!eligible(path) || !schemaShape(schema, path) || !granted(path)))
9307
9640
  return false;
9308
- if ((0, import_utils25.isPlainObject)(raw) && raw.sub !== void 0) {
9309
- if (!(0, import_utils25.isPlainObject)(raw.sub) || !declaredRulesSafe(raw.sub, path, next)) return false;
9641
+ if ((0, import_utils26.isPlainObject)(raw) && raw.sub !== void 0) {
9642
+ if (!(0, import_utils26.isPlainObject)(raw.sub) || !declaredRulesSafe(raw.sub, path, next)) return false;
9310
9643
  }
9311
9644
  }
9312
9645
  return true;
@@ -9483,7 +9816,7 @@ init_document();
9483
9816
  init_populate_target();
9484
9817
 
9485
9818
  // src/helpers/apply-update.ts
9486
- var import_utils29 = require("@web-ts-toolkit/utils");
9819
+ var import_utils30 = require("@web-ts-toolkit/utils");
9487
9820
  init_lib();
9488
9821
  function applyUpdate(target, data, allowedFields) {
9489
9822
  const wholeFields = /* @__PURE__ */ new Set();
@@ -9499,17 +9832,17 @@ function applyUpdate(target, data, allowedFields) {
9499
9832
  }
9500
9833
  };
9501
9834
  for (const field of allowedFields) {
9502
- collectBoundary((0, import_utils29.set)({}, field, true));
9835
+ collectBoundary((0, import_utils30.set)({}, field, true));
9503
9836
  }
9504
9837
  const apply = (values, prefix = "") => {
9505
9838
  for (const [key, value] of Object.entries(values)) {
9506
9839
  const path = prefix ? `${prefix}.${key}` : key;
9507
- if (partialContainers.has(path) && !wholeFields.has(path) && ((0, import_utils29.isPlainObject)(value) || Array.isArray(value))) {
9840
+ if (partialContainers.has(path) && !wholeFields.has(path) && ((0, import_utils30.isPlainObject)(value) || Array.isArray(value))) {
9508
9841
  apply(value, path);
9509
9842
  } else if (isDocument2(target)) {
9510
9843
  target.set(path, value);
9511
9844
  } else {
9512
- (0, import_utils29.set)(target, path, value);
9845
+ (0, import_utils30.set)(target, path, value);
9513
9846
  }
9514
9847
  }
9515
9848
  };
@@ -9517,7 +9850,7 @@ function applyUpdate(target, data, allowedFields) {
9517
9850
  }
9518
9851
 
9519
9852
  // src/acl/model-input-admission.ts
9520
- var import_utils31 = require("@web-ts-toolkit/utils");
9853
+ var import_utils32 = require("@web-ts-toolkit/utils");
9521
9854
  var import_mongoose8 = require("mongoose");
9522
9855
  init_document();
9523
9856
  init_request_work();
@@ -9525,7 +9858,7 @@ init_logger_helpers();
9525
9858
  init_request_complexity();
9526
9859
 
9527
9860
  // src/acl/select-resolution.ts
9528
- var import_utils30 = require("@web-ts-toolkit/utils");
9861
+ var import_utils31 = require("@web-ts-toolkit/utils");
9529
9862
  init_core_shared();
9530
9863
  init_helpers();
9531
9864
  async function collectAllowedFieldsForRequest({
@@ -9599,15 +9932,15 @@ async function resolveSelectForRequest({
9599
9932
  const excludeall = normalizedSelect.every((v) => v.startsWith("-"));
9600
9933
  if (excludeall) {
9601
9934
  normalizedSelect = normalizedSelect.map((v) => v.substring(1));
9602
- fields = (0, import_utils30.difference)(fields, normalizedSelect);
9935
+ fields = (0, import_utils31.difference)(fields, normalizedSelect);
9603
9936
  if (excludeid) fields.push("-_id");
9604
9937
  } else {
9605
- fields = (0, import_utils30.intersection)(normalizedSelect, fields.concat(excludeid ? "-_id" : "_id"));
9938
+ fields = (0, import_utils31.intersection)(normalizedSelect, fields.concat(excludeid ? "-_id" : "_id"));
9606
9939
  }
9607
9940
  }
9608
9941
  return fields.concat(strippedAlways);
9609
9942
  }
9610
- return (0, import_utils30.intersection)(normalizedSelect, fields);
9943
+ return (0, import_utils31.intersection)(normalizedSelect, fields);
9611
9944
  }
9612
9945
  function stripVirtuals(fields, virtualNames) {
9613
9946
  const set13 = virtualNames instanceof Set ? virtualNames : new Set(virtualNames ?? []);
@@ -9638,7 +9971,7 @@ init_field_catalog();
9638
9971
  init_service_authorization();
9639
9972
  var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
9640
9973
  var inside = (path, root) => path === root || path.startsWith(`${root}.`);
9641
- var record2 = (value) => (0, import_utils31.isPlainObject)(value);
9974
+ var record2 = (value) => (0, import_utils32.isPlainObject)(value);
9642
9975
  function inputValue(value) {
9643
9976
  if (typeof value === "function" || typeof value === "symbol") {
9644
9977
  authorizationError("input values must be persisted data, not functions or symbols");
@@ -9707,7 +10040,7 @@ function requestBoundaries(fields) {
9707
10040
  else visit(value, path);
9708
10041
  }
9709
10042
  };
9710
- for (const field of fields) visit((0, import_utils31.set)({}, field, true));
10043
+ for (const field of fields) visit((0, import_utils32.set)({}, field, true));
9711
10044
  return [...new Set(result2)];
9712
10045
  }
9713
10046
  function ownValue(data, parts) {
@@ -9837,7 +10170,7 @@ function admitModelInput(args) {
9837
10170
  if (forbidden(catalog, path, requested)) continue;
9838
10171
  const supplied = ownValue(data, parts);
9839
10172
  if (!supplied) continue;
9840
- (0, import_utils31.set)(allowedData, field, copyInputValue(supplied.value, path, catalog, scopedRequests, logicalPath(field)));
10173
+ (0, import_utils32.set)(allowedData, field, copyInputValue(supplied.value, path, catalog, scopedRequests, logicalPath(field)));
9841
10174
  selected.push(field);
9842
10175
  }
9843
10176
  return { allowedData, allowedFields };
@@ -9849,7 +10182,7 @@ async function requestInputFields(args) {
9849
10182
  const prefix = configuration.modelPermissionPrefix ?? "";
9850
10183
  return collectModelRequestFields({
9851
10184
  req: args.binding.request,
9852
- permissionSchema: args.scopePath ? (0, import_utils31.get)(configuration.permissionSchema, [args.scopePath, "sub"]) : configuration.permissionSchema,
10185
+ permissionSchema: args.scopePath ? (0, import_utils32.get)(configuration.permissionSchema, [args.scopePath, "sub"]) : configuration.permissionSchema,
9853
10186
  access: args.access,
9854
10187
  permissions,
9855
10188
  grants,
@@ -9893,13 +10226,13 @@ init_logger_helpers();
9893
10226
  init_lib();
9894
10227
 
9895
10228
  // src/services/model-subdocument-service.ts
9896
- var import_utils33 = require("@web-ts-toolkit/utils");
10229
+ var import_utils34 = require("@web-ts-toolkit/utils");
9897
10230
  init_helpers();
9898
10231
  init_document();
9899
10232
  init_public_admission();
9900
10233
 
9901
10234
  // src/acl/subdocument-authorization.ts
9902
- var import_utils32 = require("@web-ts-toolkit/utils");
10235
+ var import_utils33 = require("@web-ts-toolkit/utils");
9903
10236
  init_helpers();
9904
10237
  init_document();
9905
10238
  init_core_shared();
@@ -10088,8 +10421,8 @@ async function subdocumentGateAllowed(service, gate) {
10088
10421
  let guard2;
10089
10422
  if (access.startsWith("subs.")) {
10090
10423
  const [, sub, op] = access.split(".");
10091
- guard2 = (0, import_utils32.get)(configuration, `operationAccess.${access}`);
10092
- if (guard2 === void 0) guard2 = (0, import_utils32.get)(configuration, `operationAccess.subs.${sub}`);
10424
+ guard2 = (0, import_utils33.get)(configuration, `operationAccess.${access}`);
10425
+ if (guard2 === void 0) guard2 = (0, import_utils33.get)(configuration, `operationAccess.subs.${sub}`);
10093
10426
  if (guard2 === void 0) guard2 = getCapturedAuthorizationOption(configuration, `operationAccess.${op}`);
10094
10427
  } else guard2 = getCapturedAuthorizationOption(configuration, `operationAccess.${access}`);
10095
10428
  return evaluateRouteGuard(
@@ -10220,7 +10553,7 @@ async function visibleMutationRows(service, call, parentId, rows2) {
10220
10553
  const read = await subdocumentRows(service, visibility.child, selected, selected, call.sub);
10221
10554
  const list = visibility.list ? await subdocumentRows(service, visibility.list, {}, {}, call.sub) : {};
10222
10555
  if (read === false || list === false) return [];
10223
- const visible = filterCollection((0, import_utils33.get)(parent, call.sub), intersectFilters(read, list));
10556
+ const visible = filterCollection((0, import_utils34.get)(parent, call.sub), intersectFilters(read, list));
10224
10557
  return await finalizeSubdocumentRows(service, call.output, parent, visible);
10225
10558
  } catch {
10226
10559
  return [];
@@ -10245,7 +10578,7 @@ async function listSub(service, id, sub, options) {
10245
10578
  populate: []
10246
10579
  });
10247
10580
  if (!parent) return denied2("not_found" /* NotFound */);
10248
- const rows2 = filterCollection((0, import_utils33.get)(parent, sub), childFilter);
10581
+ const rows2 = filterCollection((0, import_utils34.get)(parent, sub), childFilter);
10249
10582
  const data = await finalizeSubdocumentRows(service, call.output, parent, rows2);
10250
10583
  return { success: true, kind: "list", code: "success" /* Success */, data, count: data.length };
10251
10584
  });
@@ -10269,7 +10602,7 @@ async function readSub(service, id, sub, subId, options) {
10269
10602
  });
10270
10603
  if (!parent) return denied2("not_found" /* NotFound */);
10271
10604
  const row = findElement(
10272
- (0, import_utils33.get)(capture ? parent : copySubdocumentParent(parent), sub),
10605
+ (0, import_utils34.get)(capture ? parent : copySubdocumentParent(parent), sub),
10273
10606
  childFilter
10274
10607
  );
10275
10608
  if (!row) return denied2("not_found" /* NotFound */);
@@ -10290,7 +10623,7 @@ async function updateSub(service, persist, id, sub, subId, data, options) {
10290
10623
  const identity = { _id: subId };
10291
10624
  const filter2 = await subdocumentRows(service, call.child, identity, identity, sub);
10292
10625
  if (filter2 === false) return denied2();
10293
- const row = findElement((0, import_utils33.get)(parent, sub), filter2);
10626
+ const row = findElement((0, import_utils34.get)(parent, sub), filter2);
10294
10627
  if (!row) return denied2("not_found" /* NotFound */);
10295
10628
  const original = isolateDocForOutput(row);
10296
10629
  const admit = await inputAdmission(service, call, parent);
@@ -10317,7 +10650,7 @@ async function bulkUpdateSub(service, persist, id, sub, data, options) {
10317
10650
  const selected = { _id: { $in: data.map((row) => row._id) } };
10318
10651
  const filter2 = await subdocumentRows(service, call.child, selected, selected, sub);
10319
10652
  if (filter2 === false) return denied2();
10320
- const rows2 = filterCollection((0, import_utils33.get)(parent, sub), filter2);
10653
+ const rows2 = filterCollection((0, import_utils34.get)(parent, sub), filter2);
10321
10654
  const originals = rows2.map((row) => isolateDocForOutput(row));
10322
10655
  const admit = await inputAdmission(service, call, parent);
10323
10656
  for (const [index, row] of rows2.entries()) {
@@ -10346,7 +10679,7 @@ async function createSub(service, persist, id, sub, data, options) {
10346
10679
  const lookup = await mutationParent(service, id, call);
10347
10680
  if ("error" in lookup) return lookup.error;
10348
10681
  const { parent } = lookup;
10349
- const rows2 = (0, import_utils33.get)(parent, sub);
10682
+ const rows2 = (0, import_utils34.get)(parent, sub);
10350
10683
  const admit = await inputAdmission(service, call, parent);
10351
10684
  const allowed = (Array.isArray(data) ? data : [data]).map((row) => admit(row).allowedData);
10352
10685
  if (addFirst) rows2.unshift(...allowed);
@@ -10367,7 +10700,7 @@ async function deleteSub(service, persist, id, sub, subId, options) {
10367
10700
  const identity = { _id: subId };
10368
10701
  const filter2 = await subdocumentRows(service, call.child, identity, identity, sub);
10369
10702
  if (filter2 === false) return denied2();
10370
- const row = findElement((0, import_utils33.get)(parent, sub), filter2);
10703
+ const row = findElement((0, import_utils34.get)(parent, sub), filter2);
10371
10704
  if (!row) return denied2("not_found" /* NotFound */);
10372
10705
  await ("deleteOne" in row ? row.deleteOne?.() : row.remove?.());
10373
10706
  await persist(() => parent.save());
@@ -10640,7 +10973,8 @@ var Service = class extends Base {
10640
10973
  async finalizeDirectOutput(args) {
10641
10974
  const { plan, context } = args;
10642
10975
  const { authorization, authorizationBinding: binding } = plan;
10643
- const { access, configuration } = readServiceAuthorization(authorization, binding);
10976
+ const access = readServiceAuthorizationAccess(authorization, binding);
10977
+ const configuration = readServiceOutputMetadata(authorization, binding);
10644
10978
  const source = getIncludeSource(args.doc);
10645
10979
  const snapshot = isolateDocForOutput(source);
10646
10980
  for (const key of Object.keys(snapshot))
@@ -10681,20 +11015,20 @@ var Service = class extends Base {
10681
11015
  const field = configuration.documentPermissionField ?? "_permissions";
10682
11016
  unsetDocPath(output, field);
10683
11017
  if (!configuration.stripPermissionsField) {
10684
- (0, import_utils34.set)(output, field, args.includePermissions || args.includeFieldPermissions ? deepIsolateValue(grants) : {});
11018
+ (0, import_utils35.set)(output, field, args.includePermissions || args.includeFieldPermissions ? deepIsolateValue(grants) : {});
10685
11019
  if (args.includeFieldPermissions && !configuration.disableFieldPermissions) {
10686
11020
  const rowAccess = this.asServiceHookContext(context).fieldPermissionAccess ?? await this.getFieldPermissionAccess([snapshot._id]);
10687
11021
  const id = String(snapshot._id);
10688
11022
  const views = rowAccess.readIds?.has(id) ? await requestOutputFieldPaths(handle, binding, "read") : [];
10689
11023
  const edits = rowAccess.updateIds?.has(id) ? await requestOutputFieldPaths(handle, binding, "update") : [];
10690
11024
  const map2 = (paths) => paths.length ? Object.fromEntries(paths.map((path) => [path, true])) : { $: "_" };
10691
- (0, import_utils34.set)(output, `${field}._view`, map2(views));
10692
- (0, import_utils34.set)(output, `${field}._edit`, map2(edits));
10693
- } else if (!args.includePermissions) (0, import_utils34.set)(output, field, { _view: { $: "_" }, _edit: { $: "_" } });
11025
+ (0, import_utils35.set)(output, `${field}._view`, map2(views));
11026
+ (0, import_utils35.set)(output, `${field}._edit`, map2(edits));
11027
+ } else if (!args.includePermissions) (0, import_utils35.set)(output, field, { _view: { $: "_" }, _edit: { $: "_" } });
10694
11028
  const exposed = configuration.exposedDocPermissionKeys;
10695
11029
  if (exposed) {
10696
- const metadata = (0, import_utils34.get)(output, field);
10697
- (0, import_utils34.set)(
11030
+ const metadata = (0, import_utils35.get)(output, field);
11031
+ (0, import_utils35.set)(
10698
11032
  output,
10699
11033
  field,
10700
11034
  Object.fromEntries(
@@ -10955,7 +11289,7 @@ var Service = class extends Base {
10955
11289
  return docs.map((doc) => {
10956
11290
  try {
10957
11291
  const plain = toObject(doc);
10958
- const val = (0, import_utils34.get)(plain, associationField);
11292
+ const val = (0, import_utils35.get)(plain, associationField);
10959
11293
  return deepIsolateValue(val);
10960
11294
  } catch {
10961
11295
  return void 0;
@@ -10994,10 +11328,6 @@ var Service = class extends Base {
10994
11328
  const target = captured.relatedTargets.find((target2) => target2.path === descriptor.path);
10995
11329
  const modelName = target?.modelName;
10996
11330
  if (!modelName) continue;
10997
- if (target.kind === "passthrough") {
10998
- result2.push({ path: descriptor.path, select: descriptor.select });
10999
- continue;
11000
- }
11001
11331
  const policy = relatedPolicy(state.authorization, state.binding, descriptor.path);
11002
11332
  const svc = this.requestCore.getPublicService(modelName);
11003
11333
  const child = createRelatedExecution(
@@ -11016,7 +11346,7 @@ var Service = class extends Base {
11016
11346
  if (isExecutionRowDenied(svc, child)) continue;
11017
11347
  const childState = readServiceExecution(svc, child);
11018
11348
  const targetConfiguration = readServiceAuthorization(childState.authorization, childState.binding).configuration;
11019
- if (policy?.operation.mode !== "grant" && !await this.hookGate.run(
11349
+ if (targetConfiguration.requireOperationAccessForPopulateQueries !== false && policy?.operation.mode !== "grant" && !await this.hookGate.run(
11020
11350
  () => evaluateRouteGuard(
11021
11351
  this.req,
11022
11352
  getAuthorizationPermissions(childState.authorization, childState.binding),
@@ -11068,7 +11398,7 @@ var Service = class extends Base {
11068
11398
  if (this.isScalarPopulateValue(rawTarget)) return rawTarget;
11069
11399
  const isDoc = (() => {
11070
11400
  try {
11071
- if ((0, import_utils34.isPlainObject)(rawTarget)) return true;
11401
+ if ((0, import_utils35.isPlainObject)(rawTarget)) return true;
11072
11402
  const maybe = rawTarget;
11073
11403
  if (maybe && typeof maybe === "object" && "_doc" in maybe) return true;
11074
11404
  if (maybe && typeof maybe.toObject === "function") return true;
@@ -11156,7 +11486,7 @@ var Service = class extends Base {
11156
11486
  child = getDocValue(current, key);
11157
11487
  if (child === void 0) {
11158
11488
  try {
11159
- child = (0, import_utils34.get)(current, key);
11489
+ child = (0, import_utils35.get)(current, key);
11160
11490
  } catch {
11161
11491
  return;
11162
11492
  }
@@ -11391,7 +11721,7 @@ var Service = class extends Base {
11391
11721
  originalDocumentSnapshot: toObject(doc),
11392
11722
  resolvedQuery: query
11393
11723
  };
11394
- const correlatedSnapshots = [(0, import_utils34.cloneDeep)(toObject(doc))];
11724
+ const correlatedSnapshots = [(0, import_utils35.cloneDeep)(toObject(doc))];
11395
11725
  try {
11396
11726
  doc = await this.includeDocs(doc, includes, state.authorization);
11397
11727
  if (correlatedIncludes.length > 0) {
@@ -11573,7 +11903,7 @@ var Service = class extends Base {
11573
11903
  const findAssociationField = this.getAssociationFieldFromOptions(options);
11574
11904
  const withAssocSelect = findAssociationField && !finalSelect.includes(findAssociationField) ? [...finalSelect, findAssociationField] : finalSelect;
11575
11905
  const dbSelect = this.buildDbSelectForAssociation(withAssocSelect, findAssociationField);
11576
- const filteredPopulate = (0, import_utils34.isArray)(withAssocSelect) && (0, import_utils34.isArray)(_populate) ? _populate.filter(
11906
+ const filteredPopulate = (0, import_utils35.isArray)(withAssocSelect) && (0, import_utils35.isArray)(_populate) ? _populate.filter(
11577
11907
  (p) => [
11578
11908
  ...virtualPlan.publicPersistedFetchSelection,
11579
11909
  ...findAssociationField ? [findAssociationField] : []
@@ -11633,7 +11963,7 @@ var Service = class extends Base {
11633
11963
  findAssociationField ? [findAssociationField] : []
11634
11964
  );
11635
11965
  const capturedAssociation = findAssociationField ? this.captureAssociationValues(docs, findAssociationField) : void 0;
11636
- const correlatedSnapshots = docs.map((doc) => (0, import_utils34.cloneDeep)(toObject(doc)));
11966
+ const correlatedSnapshots = docs.map((doc) => (0, import_utils35.cloneDeep)(toObject(doc)));
11637
11967
  try {
11638
11968
  docs = await this.includeDocs(docs, includes, state.authorization);
11639
11969
  if (correlatedIncludes.length > 0) {
@@ -11832,12 +12162,12 @@ var Service = class extends Base {
11832
12162
  context.allowedFields = allowedFields;
11833
12163
  context.allowedData = allowedData;
11834
12164
  const validated = await this.validate(allowedData, "create", context);
11835
- if ((0, import_utils34.isBoolean)(validated)) {
12165
+ if ((0, import_utils35.isBoolean)(validated)) {
11836
12166
  if (!validated) {
11837
12167
  validationErrors.push({ index, errors: [] });
11838
12168
  return void 0;
11839
12169
  }
11840
- } else if ((0, import_utils34.isArray)(validated)) {
12170
+ } else if ((0, import_utils35.isArray)(validated)) {
11841
12171
  if (validated.length > 0) {
11842
12172
  validationErrors.push({
11843
12173
  index,
@@ -12048,7 +12378,7 @@ var Service = class extends Base {
12048
12378
  }
12049
12379
  let doc = await this.model.findOne({
12050
12380
  filter: _filter,
12051
- select: (0, import_utils34.uniq)([...updatePlan.persistedFetchSelection, ...inputFetchPaths(captured.configuration.catalog)]).map(
12381
+ select: (0, import_utils35.uniq)([...updatePlan.persistedFetchSelection, ...inputFetchPaths(captured.configuration.catalog)]).map(
12052
12382
  (path) => `+${path}`
12053
12383
  )
12054
12384
  });
@@ -12087,12 +12417,12 @@ var Service = class extends Base {
12087
12417
  context.allowedFields = allowedFields;
12088
12418
  context.allowedData = allowedData;
12089
12419
  const validated = await this.validate(allowedData, "update", context);
12090
- if ((0, import_utils34.isBoolean)(validated)) {
12420
+ if ((0, import_utils35.isBoolean)(validated)) {
12091
12421
  if (!validated) {
12092
12422
  this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
12093
12423
  return { success: false, kind: "error", code: "bad_request" /* BadRequest */ };
12094
12424
  }
12095
- } else if ((0, import_utils34.isArray)(validated)) {
12425
+ } else if ((0, import_utils35.isArray)(validated)) {
12096
12426
  if (validated.length > 0) {
12097
12427
  this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
12098
12428
  return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: validated };
@@ -12112,10 +12442,10 @@ var Service = class extends Base {
12112
12442
  const diffExcludeFields = [this.options.documentPermissionField, "__v"];
12113
12443
  this.asServiceHookContext(context).diff = (d) => {
12114
12444
  context.changes = (0, import_just_diff.diff)(
12115
- (0, import_utils34.omit)(context.originalDocumentSnapshot, diffExcludeFields),
12116
- (0, import_utils34.omit)(d.toObject({ virtuals: false }), diffExcludeFields)
12445
+ (0, import_utils35.omit)(context.originalDocumentSnapshot, diffExcludeFields),
12446
+ (0, import_utils35.omit)(d.toObject({ virtuals: false }), diffExcludeFields)
12117
12447
  ) || [];
12118
- context.modifiedPaths = (0, import_utils34.uniq)(context.changes.map((di) => di.path.length > 0 ? String(di.path[0]) : ""));
12448
+ context.modifiedPaths = (0, import_utils35.uniq)(context.changes.map((di) => di.path.length > 0 ? String(di.path[0]) : ""));
12119
12449
  };
12120
12450
  doc = assertModelDocument(await this.afterPersist(doc, "update", context), this.modelName, "afterPersist");
12121
12451
  context.currentDocument = doc;
@@ -12203,7 +12533,7 @@ var Service = class extends Base {
12203
12533
  const upsertOptions = this.resolveUpsertOptions(options);
12204
12534
  const updateIntent = captureModelServiceIntent(this, "updateOne", upsertArgs, upsertOptions);
12205
12535
  const createIntent = captureModelServiceIntent(this, "create", upsertArgs, upsertOptions);
12206
- const related = (0, import_utils34.uniqBy)(
12536
+ const related = (0, import_utils35.uniqBy)(
12207
12537
  [...this.captureRelated(updateIntent.args), ...this.captureRelated(createIntent.args)],
12208
12538
  (target) => target.path
12209
12539
  );
@@ -12644,7 +12974,7 @@ var Service = class extends Base {
12644
12974
  return resolveExistsOptions(this, options);
12645
12975
  }
12646
12976
  async getFieldPermissionAccess(ids) {
12647
- const uniqueIds = (0, import_utils34.compact)((0, import_utils34.uniqBy)(ids, (id) => String(id)).map((id) => String(id)));
12977
+ const uniqueIds = (0, import_utils35.compact)((0, import_utils35.uniqBy)(ids, (id) => String(id)).map((id) => String(id)));
12648
12978
  if (uniqueIds.length === 0) {
12649
12979
  return {
12650
12980
  readIds: /* @__PURE__ */ new Set(),
@@ -12690,7 +13020,7 @@ var Service = class extends Base {
12690
13020
  * (direct/root-entry 200); count measures visible rows, not all updated rows.
12691
13021
  */
12692
13022
  async bulkUpdateSub(id, sub, data, options) {
12693
- return bulkUpdateSub(this, this.persist, id, sub, (0, import_utils34.castArray)(data), options);
13023
+ return bulkUpdateSub(this, this.persist, id, sub, (0, import_utils35.castArray)(data), options);
12694
13024
  }
12695
13025
  /**
12696
13026
  * Append/prepend admitted rows, then return the visible full array (including when
@@ -12714,7 +13044,7 @@ var Service = class extends Base {
12714
13044
  };
12715
13045
 
12716
13046
  // src/services/public-service.ts
12717
- var import_utils35 = require("@web-ts-toolkit/utils");
13047
+ var import_utils36 = require("@web-ts-toolkit/utils");
12718
13048
  init_helpers();
12719
13049
  init_permissions_alias();
12720
13050
  init_document();
@@ -12840,7 +13170,7 @@ var PublicService = class extends Service {
12840
13170
  let d = toObject(doc);
12841
13171
  d = await this.decorate(d, "create", context);
12842
13172
  d = this.runTasks(d, tasks);
12843
- if (select) d = copyPermissionsAlias(d, (0, import_utils35.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]));
13173
+ if (select) d = copyPermissionsAlias(d, (0, import_utils36.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]));
12844
13174
  return d;
12845
13175
  }
12846
13176
  );
@@ -13059,8 +13389,8 @@ var PublicService = class extends Service {
13059
13389
  let d = toObject(doc);
13060
13390
  d = await this.decorate(d, "update", context);
13061
13391
  d = this.runTasks(d, tasks);
13062
- if (select) d = copyPermissionsAlias(d, (0, import_utils35.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]));
13063
- else if (!returningAll) d = copyPermissionsAlias(d, (0, import_utils35.pick)(d, returningKeys));
13392
+ if (select) d = copyPermissionsAlias(d, (0, import_utils36.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]));
13393
+ else if (!returningAll) d = copyPermissionsAlias(d, (0, import_utils36.pick)(d, returningKeys));
13064
13394
  return d;
13065
13395
  }
13066
13396
  );
@@ -13143,8 +13473,8 @@ var PublicService = class extends Service {
13143
13473
  let d = toObject(doc);
13144
13474
  d = await this.decorate(d, "update", context);
13145
13475
  d = this.runTasks(d, tasks2);
13146
- if (select) d = copyPermissionsAlias(d, (0, import_utils35.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]));
13147
- else if (!returningAll) d = copyPermissionsAlias(d, (0, import_utils35.pick)(d, returningKeys));
13476
+ if (select) d = copyPermissionsAlias(d, (0, import_utils36.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]));
13477
+ else if (!returningAll) d = copyPermissionsAlias(d, (0, import_utils36.pick)(d, returningKeys));
13148
13478
  return d;
13149
13479
  }
13150
13480
  );
@@ -13169,7 +13499,7 @@ var PublicService = class extends Service {
13169
13499
  d = await this.decorate(d, "create", context);
13170
13500
  d = this.runTasks(d, tasks);
13171
13501
  if (createSelect)
13172
- d = copyPermissionsAlias(d, (0, import_utils35.pick)(d, [...normalizeSelect(createSelect), ...this.baseFieldsExt]));
13502
+ d = copyPermissionsAlias(d, (0, import_utils36.pick)(d, [...normalizeSelect(createSelect), ...this.baseFieldsExt]));
13173
13503
  return d;
13174
13504
  }
13175
13505
  );
@@ -13314,7 +13644,7 @@ var PublicService = class extends Service {
13314
13644
  init_options();
13315
13645
  init_data_values();
13316
13646
  init_helpers();
13317
- var import_utils36 = require("@web-ts-toolkit/utils");
13647
+ var import_utils37 = require("@web-ts-toolkit/utils");
13318
13648
  init_request_complexity();
13319
13649
  init_runtime_context();
13320
13650
  init_runtime();
@@ -13343,7 +13673,7 @@ var DataService = class {
13343
13673
  let doc = await findElement(this.data, _filter);
13344
13674
  if (!doc) return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
13345
13675
  doc = await this.trimOutputFields(doc, access);
13346
- if (_select.length > 0) doc = (0, import_utils36.pick)(doc, _select);
13676
+ if (_select.length > 0) doc = (0, import_utils37.pick)(doc, _select);
13347
13677
  return {
13348
13678
  success: true,
13349
13679
  kind: "single",
@@ -13389,13 +13719,13 @@ var DataService = class {
13389
13719
  const totalCount = docs.length;
13390
13720
  if (sort) {
13391
13721
  const normalizedSort = normalizeSortForOrderBy(sort);
13392
- docs = (0, import_utils36.orderBy)(docs, normalizedSort.fields, normalizedSort.orders);
13722
+ docs = (0, import_utils37.orderBy)(docs, normalizedSort.fields, normalizedSort.orders);
13393
13723
  }
13394
13724
  const pagedDocs = docs.slice(query.skip, query.limit && query.skip + query.limit);
13395
13725
  const { maxHookConcurrency } = this.getRequestComplexity();
13396
13726
  const trimmed = await mapWithConcurrencyLimit(pagedDocs, maxHookConcurrency, async (doc) => {
13397
13727
  doc = await this.trimOutputFields(doc, "list");
13398
- if (_select.length > 0) doc = (0, import_utils36.pick)(doc, _select);
13728
+ if (_select.length > 0) doc = (0, import_utils37.pick)(doc, _select);
13399
13729
  return copyDataValue(doc);
13400
13730
  });
13401
13731
  if (!hasExplicitLimit && trimmed.length === totalCount) {
@@ -13470,10 +13800,10 @@ var DataService = class {
13470
13800
  init_helpers();
13471
13801
 
13472
13802
  // src/processors.ts
13473
- var import_utils37 = require("@web-ts-toolkit/utils");
13803
+ var import_utils38 = require("@web-ts-toolkit/utils");
13474
13804
  init_permissions_alias();
13475
13805
  function cloneProcessorValue(value) {
13476
- const copy = (0, import_utils37.cloneDeep)(value);
13806
+ const copy = (0, import_utils38.cloneDeep)(value);
13477
13807
  copyPermissionsAliasGraph(value, copy);
13478
13808
  return copy;
13479
13809
  }
@@ -13511,10 +13841,10 @@ function resolveLeafParents(obj, src) {
13511
13841
  for (let ind = 0; ind < segs.length - 1; ind += 1) {
13512
13842
  const seg = segs[ind];
13513
13843
  targets = targets.reduce((ret, target) => {
13514
- if (!(0, import_utils37.isPlainObject)(target)) return ret;
13844
+ if (!(0, import_utils38.isPlainObject)(target)) return ret;
13515
13845
  const targetObject = target;
13516
13846
  const next = targetObject[seg];
13517
- if ((0, import_utils37.isArray)(next)) {
13847
+ if ((0, import_utils38.isArray)(next)) {
13518
13848
  for (const item of next) {
13519
13849
  ret.push(item);
13520
13850
  }
@@ -13524,8 +13854,8 @@ function resolveLeafParents(obj, src) {
13524
13854
  }
13525
13855
  const leaf = segs[segs.length - 1];
13526
13856
  const pairs = [];
13527
- (0, import_utils37.forEach)(targets, (target) => {
13528
- if (!(0, import_utils37.isPlainObject)(target)) return;
13857
+ (0, import_utils38.forEach)(targets, (target) => {
13858
+ if (!(0, import_utils38.isPlainObject)(target)) return;
13529
13859
  pairs.push({ parent: target, leaf });
13530
13860
  });
13531
13861
  return pairs;
@@ -13534,7 +13864,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
13534
13864
  const mutable = options.mutable !== false;
13535
13865
  const idField = options.idField ?? "_id";
13536
13866
  const obj = mutable ? docObject : cloneProcessorValue(docObject);
13537
- (0, import_utils37.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13867
+ (0, import_utils38.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13538
13868
  if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
13539
13869
  const src = op.src;
13540
13870
  const dest = op.dest;
@@ -13543,19 +13873,19 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
13543
13873
  assertSafePath(dest);
13544
13874
  let targets = [obj];
13545
13875
  const segs = src.split(".");
13546
- (0, import_utils37.forEach)(segs, (seg, ind) => {
13876
+ (0, import_utils38.forEach)(segs, (seg, ind) => {
13547
13877
  if (segs.length === ind + 1) {
13548
- (0, import_utils37.forEach)(targets, (target) => {
13549
- if (!(0, import_utils37.isPlainObject)(target)) return;
13878
+ (0, import_utils38.forEach)(targets, (target) => {
13879
+ if (!(0, import_utils38.isPlainObject)(target)) return;
13550
13880
  const targetObject = target;
13551
13881
  const leaf = targetObject[seg];
13552
13882
  const sourceForDest = leaf;
13553
13883
  let depopulated;
13554
- if ((0, import_utils37.isArray)(leaf)) {
13884
+ if ((0, import_utils38.isArray)(leaf)) {
13555
13885
  if (leaf.length === 0) {
13556
13886
  depopulated = [];
13557
- } else if (leaf.every((item) => (0, import_utils37.isPlainObject)(item))) {
13558
- depopulated = (0, import_utils37.map)(leaf, idField).map((id, i) => {
13887
+ } else if (leaf.every((item) => (0, import_utils38.isPlainObject)(item))) {
13888
+ depopulated = (0, import_utils38.map)(leaf, idField).map((id, i) => {
13559
13889
  if (id === void 0) {
13560
13890
  failMissingId(`${src}[${i}]`);
13561
13891
  }
@@ -13564,8 +13894,8 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
13564
13894
  } else {
13565
13895
  return;
13566
13896
  }
13567
- } else if ((0, import_utils37.isPlainObject)(leaf)) {
13568
- const id = (0, import_utils37.get)(leaf, idField);
13897
+ } else if ((0, import_utils38.isPlainObject)(leaf)) {
13898
+ const id = (0, import_utils38.get)(leaf, idField);
13569
13899
  if (id === void 0) {
13570
13900
  failMissingId(src);
13571
13901
  }
@@ -13573,15 +13903,15 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
13573
13903
  } else {
13574
13904
  return;
13575
13905
  }
13576
- (0, import_utils37.set)(targetObject, dest, sourceForDest);
13577
- (0, import_utils37.set)(targetObject, seg, depopulated);
13906
+ (0, import_utils38.set)(targetObject, dest, sourceForDest);
13907
+ (0, import_utils38.set)(targetObject, seg, depopulated);
13578
13908
  });
13579
13909
  } else {
13580
13910
  targets = targets.reduce((ret, target) => {
13581
- if (!(0, import_utils37.isPlainObject)(target)) return ret;
13911
+ if (!(0, import_utils38.isPlainObject)(target)) return ret;
13582
13912
  const targetObject = target;
13583
13913
  const next = targetObject[seg];
13584
- if ((0, import_utils37.isArray)(next)) {
13914
+ if ((0, import_utils38.isArray)(next)) {
13585
13915
  for (const item of next) {
13586
13916
  ret.push(item);
13587
13917
  }
@@ -13596,7 +13926,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
13596
13926
  var copyPaths = (docObject, operations, options = { mutable: true }) => {
13597
13927
  const mutable = options.mutable !== false;
13598
13928
  const obj = mutable ? docObject : cloneProcessorValue(docObject);
13599
- (0, import_utils37.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13929
+ (0, import_utils38.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13600
13930
  if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
13601
13931
  const src = op.src;
13602
13932
  const dest = op.dest;
@@ -13604,9 +13934,9 @@ var copyPaths = (docObject, operations, options = { mutable: true }) => {
13604
13934
  assertSafePath(src, "copyPaths");
13605
13935
  assertSafePath(dest, "copyPaths");
13606
13936
  if (src === dest) return;
13607
- (0, import_utils37.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13608
- if (!(0, import_utils37.hasOwn)(parent, leaf)) return;
13609
- (0, import_utils37.set)(parent, dest, cloneProcessorValue(parent[leaf]));
13937
+ (0, import_utils38.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13938
+ if (!(0, import_utils38.hasOwn)(parent, leaf)) return;
13939
+ (0, import_utils38.set)(parent, dest, cloneProcessorValue(parent[leaf]));
13610
13940
  });
13611
13941
  });
13612
13942
  return obj;
@@ -13614,7 +13944,7 @@ var copyPaths = (docObject, operations, options = { mutable: true }) => {
13614
13944
  var movePaths = (docObject, operations, options = { mutable: true }) => {
13615
13945
  const mutable = options.mutable !== false;
13616
13946
  const obj = mutable ? docObject : cloneProcessorValue(docObject);
13617
- (0, import_utils37.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13947
+ (0, import_utils38.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13618
13948
  if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
13619
13949
  const src = op.src;
13620
13950
  const dest = op.dest;
@@ -13622,10 +13952,10 @@ var movePaths = (docObject, operations, options = { mutable: true }) => {
13622
13952
  assertSafePath(src, "movePaths");
13623
13953
  assertSafePath(dest, "movePaths");
13624
13954
  if (src === dest) return;
13625
- (0, import_utils37.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13626
- if (!(0, import_utils37.hasOwn)(parent, leaf)) return;
13955
+ (0, import_utils38.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13956
+ if (!(0, import_utils38.hasOwn)(parent, leaf)) return;
13627
13957
  const value = cloneProcessorValue(parent[leaf]);
13628
- (0, import_utils37.set)(parent, dest, value);
13958
+ (0, import_utils38.set)(parent, dest, value);
13629
13959
  if (dest === leaf || dest.startsWith(`${leaf}.`)) return;
13630
13960
  delete parent[leaf];
13631
13961
  });
@@ -13637,7 +13967,7 @@ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: D
13637
13967
  const maxSlice = options.maxSlice ?? DEFAULT_MAX_SLICE;
13638
13968
  assertNonNegativeSafeInteger(maxSlice, "maxSlice", "sliceArrays");
13639
13969
  const obj = mutable ? docObject : cloneProcessorValue(docObject);
13640
- (0, import_utils37.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13970
+ (0, import_utils38.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13641
13971
  if (!op || typeof op.src !== "string") return;
13642
13972
  const src = op.src;
13643
13973
  if (src.length === 0) return;
@@ -13654,15 +13984,15 @@ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: D
13654
13984
  assertNonNegativeSafeInteger(op.limit, "limit", "sliceArrays");
13655
13985
  limit = Math.min(op.limit, maxSlice);
13656
13986
  }
13657
- (0, import_utils37.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13987
+ (0, import_utils38.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13658
13988
  const leafValue = parent[leaf];
13659
- if (!(0, import_utils37.isArray)(leafValue)) return;
13989
+ if (!(0, import_utils38.isArray)(leafValue)) return;
13660
13990
  const end = typeof limit === "undefined" ? void 0 : skip + limit;
13661
13991
  const window = leafValue.slice(skip, end);
13662
13992
  if (typeof rawDest === "undefined") {
13663
- (0, import_utils37.set)(parent, leaf, window);
13993
+ (0, import_utils38.set)(parent, leaf, window);
13664
13994
  } else {
13665
- (0, import_utils37.set)(parent, rawDest, window);
13995
+ (0, import_utils38.set)(parent, rawDest, window);
13666
13996
  }
13667
13997
  });
13668
13998
  });
@@ -13671,27 +14001,27 @@ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: D
13671
14001
  var countPaths = (docObject, operations, options = { mutable: true }) => {
13672
14002
  const mutable = options.mutable !== false;
13673
14003
  const obj = mutable ? docObject : cloneProcessorValue(docObject);
13674
- (0, import_utils37.forEach)(Array.isArray(operations) ? operations : [], (op) => {
14004
+ (0, import_utils38.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13675
14005
  if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
13676
14006
  const src = op.src;
13677
14007
  const dest = op.dest;
13678
14008
  if (src.length === 0 || dest.length === 0) return;
13679
14009
  assertSafePath(src, "countPaths");
13680
14010
  assertSafePath(dest, "countPaths");
13681
- (0, import_utils37.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13682
- if (!(0, import_utils37.hasOwn)(parent, leaf)) return;
14011
+ (0, import_utils38.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
14012
+ if (!(0, import_utils38.hasOwn)(parent, leaf)) return;
13683
14013
  const leafValue = parent[leaf];
13684
14014
  let count;
13685
- if ((0, import_utils37.isArray)(leafValue)) {
14015
+ if ((0, import_utils38.isArray)(leafValue)) {
13686
14016
  count = leafValue.length;
13687
- } else if ((0, import_utils37.isPlainObject)(leafValue)) {
14017
+ } else if ((0, import_utils38.isPlainObject)(leafValue)) {
13688
14018
  count = Object.keys(leafValue).length;
13689
14019
  } else if (leafValue === null || typeof leafValue === "undefined") {
13690
14020
  count = 0;
13691
14021
  } else {
13692
14022
  count = 1;
13693
14023
  }
13694
- (0, import_utils37.set)(parent, dest, count);
14024
+ (0, import_utils38.set)(parent, dest, count);
13695
14025
  });
13696
14026
  });
13697
14027
  return obj;
@@ -13699,15 +14029,15 @@ var countPaths = (docObject, operations, options = { mutable: true }) => {
13699
14029
  var maskPaths = (docObject, operations, options = { mutable: true }) => {
13700
14030
  const mutable = options.mutable !== false;
13701
14031
  const obj = mutable ? docObject : cloneProcessorValue(docObject);
13702
- (0, import_utils37.forEach)(Array.isArray(operations) ? operations : [], (op) => {
14032
+ (0, import_utils38.forEach)(Array.isArray(operations) ? operations : [], (op) => {
13703
14033
  if (!op || typeof op.src !== "string") return;
13704
14034
  const src = op.src;
13705
14035
  if (src.length === 0) return;
13706
14036
  assertSafePath(src, "maskPaths");
13707
14037
  const replacement = Object.prototype.hasOwnProperty.call(op, "replacement") ? op.replacement : DEFAULT_MASK_REPLACEMENT;
13708
- (0, import_utils37.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
13709
- if (!(0, import_utils37.hasOwn)(parent, leaf)) return;
13710
- (0, import_utils37.set)(parent, leaf, replacement);
14038
+ (0, import_utils38.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
14039
+ if (!(0, import_utils38.hasOwn)(parent, leaf)) return;
14040
+ (0, import_utils38.set)(parent, leaf, replacement);
13711
14041
  });
13712
14042
  });
13713
14043
  return obj;
@@ -13762,6 +14092,8 @@ init_core_shared();
13762
14092
  init_virtual_projection();
13763
14093
  init_populate_target();
13764
14094
  init_model_output_planning();
14095
+ init_authorization_snapshot();
14096
+ init_document();
13765
14097
  init_output_field_filter();
13766
14098
  init_request_work();
13767
14099
  var emptyPermissionLeaf = () => ({ $: "_" });
@@ -13779,10 +14111,10 @@ var Core = class {
13779
14111
  getIdentifier(modelName) {
13780
14112
  const resolveIdFilter = getModelOption(modelName, "resolveIdFilter");
13781
14113
  const idField = getModelOption(modelName, "idField");
13782
- if ((0, import_utils38.isFunction)(resolveIdFilter)) {
14114
+ if ((0, import_utils39.isFunction)(resolveIdFilter)) {
13783
14115
  return null;
13784
14116
  }
13785
- if ((0, import_utils38.isString)(idField)) {
14117
+ if ((0, import_utils39.isString)(idField)) {
13786
14118
  return idField;
13787
14119
  }
13788
14120
  return "_id";
@@ -13872,17 +14204,11 @@ var Core = class {
13872
14204
  return this.genSelect(modelName, access, targetFields, options);
13873
14205
  }
13874
14206
  /** Persisted request-only populate admission; fetch planning is not field authority. */
13875
- async requestPopulateSelect(modelName, access, targetFields = null, scopePath = []) {
14207
+ async requestPopulateSelect(configuration, permissions, access, targetFields = null, scopePath = []) {
13876
14208
  const subPaths = scopePath.flatMap((part) => [part, "sub"]);
13877
- const permissionSchema = getModelOption(modelName, ["permissionSchema"].concat(subPaths).join("."));
13878
- const permissions = this.getGlobalPermissions();
13879
- let virtualNames;
13880
- try {
13881
- const runtime = getActiveRuntime() ?? defaultRuntime;
13882
- virtualNames = runtime.getVirtualNames(modelName, subPaths);
13883
- } catch {
13884
- virtualNames = [];
13885
- }
14209
+ const permissionSchema = (0, import_utils39.get)(configuration, ["permissionSchema", ...subPaths]);
14210
+ const prefix = scopePath.length ? `${scopePath.join(".")}.` : "";
14211
+ const virtualNames = (configuration.catalog.virtualPaths ?? []).filter((path) => path.startsWith(prefix)).map((path) => path.slice(prefix.length)).filter((path) => !path.includes("."));
13886
14212
  return resolveSelectForRequest({
13887
14213
  req: this.req,
13888
14214
  permissionSchema,
@@ -13901,17 +14227,25 @@ var Core = class {
13901
14227
  assertPublicAdmission(_populate, "populate");
13902
14228
  assertPopulateAccess(access);
13903
14229
  if (!_populate) return [];
13904
- let populate = Array.isArray(_populate) ? _populate : [_populate];
13905
- for (const p of populate) {
13906
- if (!(0, import_utils38.isString)(p)) assertPopulateAccess(p.access);
13907
- }
13908
- populate = (0, import_utils38.compact)(
14230
+ const descriptors = copyAuthorizationValue(Array.isArray(_populate) ? _populate : [_populate], true);
14231
+ for (const p of descriptors) {
14232
+ if (!(0, import_utils39.isString)(p)) assertPopulateAccess(p.access);
14233
+ }
14234
+ const runtime = this.requestRuntime;
14235
+ const globalOptions = modelServiceGlobalOptions(this, runtime);
14236
+ const relatedTargets = captureRelatedTargets(runtime, modelName, { populate: descriptors }, 0, globalOptions);
14237
+ const sourceConfiguration = captureModelAuthorizationConfiguration(runtime, modelName, void 0, globalOptions);
14238
+ const originalPermissions = this.getGlobalPermissions();
14239
+ const permissions = new permission_default(
14240
+ Object.fromEntries(originalPermissions.keys.map((key) => [key, originalPermissions.has(key)]))
14241
+ );
14242
+ const populate = (0, import_utils39.compact)(
13909
14243
  await Promise.all(
13910
- populate.map(async (p) => {
13911
- const populateAccess = !(0, import_utils38.isString)(p) && p.access ? p.access : access;
14244
+ descriptors.map(async (p) => {
14245
+ const populateAccess = !(0, import_utils39.isString)(p) && p.access ? p.access : access;
13912
14246
  assertPopulateAccess(populateAccess);
13913
- const originalSelect = (0, import_utils38.isString)(p) ? void 0 : p.select;
13914
- const ret = (0, import_utils38.isString)(p) ? { path: p } : {
14247
+ const originalSelect = (0, import_utils39.isString)(p) ? void 0 : p.select;
14248
+ const ret = (0, import_utils39.isString)(p) ? { path: p } : {
13915
14249
  path: p.path,
13916
14250
  select: normalizeSelect(p.select)
13917
14251
  };
@@ -13919,78 +14253,86 @@ var Core = class {
13919
14253
  const pathForSourcePermission = scopePrefix && ret.path.startsWith(`${scopePrefix}.`) ? ret.path.slice(scopePrefix.length + 1) : ret.path;
13920
14254
  const parentPath = pathForSourcePermission.includes(".") ? pathForSourcePermission.split(".")[0] : pathForSourcePermission;
13921
14255
  const allowedParentPaths = await this.requestPopulateSelect(
13922
- modelName,
14256
+ sourceConfiguration,
14257
+ permissions,
13923
14258
  populateAccess,
13924
14259
  [parentPath],
13925
14260
  scopePath
13926
14261
  );
13927
14262
  if (!allowedParentPaths.includes(parentPath)) return null;
13928
- const refModelName = getModelRef(modelName, ret.path);
13929
- if (!refModelName) return null;
13930
- const runtime = getActiveRuntime();
13931
- if (!runtime) return null;
13932
- const requireRegisteredPopulateModels = getGlobalOption("requireRegisteredPopulateModels", true);
13933
- if (!runtime.hasModel(refModelName)) {
13934
- return requireRegisteredPopulateModels ? null : ret;
13935
- }
13936
- const allowedTargetOperation = await this.isAllowed(refModelName, populateAccess);
13937
- if (!allowedTargetOperation) {
14263
+ const target = relatedTargets.find((entry) => entry.path === ret.path);
14264
+ if (!target) return null;
14265
+ const refModelName = target.modelName;
14266
+ const targetConfiguration = target.configuration;
14267
+ if (targetConfiguration.requireOperationAccessForPopulateQueries !== false && !await withCapturedModelOperationGuard(
14268
+ this,
14269
+ refModelName,
14270
+ populateAccess,
14271
+ getCapturedAuthorizationOption(targetConfiguration, `operationAccess.${populateAccess}`),
14272
+ permissions,
14273
+ () => this.isAllowed(refModelName, populateAccess)
14274
+ )) {
13938
14275
  return null;
13939
14276
  }
13940
- ret.select = await this.requestPopulateSelect(refModelName, populateAccess, ret.select);
13941
- const hookInputs = getModelOption(refModelName, `alwaysSelectFields.${populateAccess}`, []);
13942
- ret.select = (0, import_utils38.uniq)([...ret.select, ...stripVirtuals(hookInputs, runtime.getVirtualNames(refModelName))]);
13943
- const filter2 = await this.genFilter(refModelName, populateAccess, null);
14277
+ ret.select = await this.requestPopulateSelect(
14278
+ targetConfiguration,
14279
+ permissions,
14280
+ populateAccess,
14281
+ ret.select
14282
+ );
14283
+ const hookInputs = resolveAlwaysSelectForAccess(
14284
+ targetConfiguration.alwaysSelectFields,
14285
+ populateAccess
14286
+ );
14287
+ ret.select = (0, import_utils39.uniq)([
14288
+ ...ret.select,
14289
+ ...stripVirtuals(hookInputs, [...targetConfiguration.catalog.virtualPaths ?? []])
14290
+ ]);
14291
+ const match = (0, import_utils39.isString)(p) ? {} : p.match ?? {};
14292
+ const filter2 = await resolveAccessFilterForRequest({
14293
+ req: this.req,
14294
+ permissions,
14295
+ cache: this.caches.baseFilter,
14296
+ cacheKey: `${refModelName}_baseFilter_${populateAccess}`,
14297
+ access: populateAccess,
14298
+ filter: deepIsolateValue(match),
14299
+ getOption: (key) => getCapturedAuthorizationOption(targetConfiguration, key)
14300
+ });
13944
14301
  if (filter2 === false) return null;
13945
- ret.match = filter2;
13946
- try {
13947
- const planAccess = populateAccess;
13948
- const targetSnapshot = {
13949
- permissionSchema: getModelOption(refModelName, "permissionSchema", null) ?? null,
13950
- virtuals: getModelOption(refModelName, "virtuals", null) ?? null,
13951
- alwaysSelectFields: getModelOption(refModelName, "alwaysSelectFields", null) ?? null,
13952
- modelPermissionPrefix: getModelOption(refModelName, "modelPermissionPrefix", "") ?? "",
13953
- requireExplicitSelect: getModelOption(refModelName, "requireExplicitSelect", false) ?? false,
13954
- documentPermissionField: getModelOption(refModelName, "documentPermissionField", "_permissions") ?? "_permissions",
13955
- exposedDocPermissionKeys: getModelOption(refModelName, "exposedDocPermissionKeys", void 0),
13956
- stripPermissionsField: getModelOption(refModelName, "stripPermissionsField", false) ?? false,
13957
- disableFieldPermissions: getModelOption(refModelName, "disableFieldPermissions", false) ?? false
13958
- };
13959
- const perms = this.getPermissions();
13960
- const globalForPlan = perms && typeof perms.has === "function" && typeof perms.hasKey === "function" ? perms : { has: () => false, hasKey: () => false };
13961
- const targetPlan = planVirtualProjection({
13962
- receivingModelName: refModelName,
13963
- snapshot: {
13964
- permissionSchema: targetSnapshot.permissionSchema,
13965
- virtuals: targetSnapshot.virtuals,
13966
- alwaysSelectFields: targetSnapshot.alwaysSelectFields,
13967
- modelPermissionPrefix: targetSnapshot.modelPermissionPrefix,
13968
- requireExplicitSelect: targetSnapshot.requireExplicitSelect,
13969
- documentPermissionField: targetSnapshot.documentPermissionField
13970
- },
13971
- virtualAccess: planAccess,
13972
- outputAccess: planAccess,
13973
- docPermissionsAccess: planAccess,
13974
- requestedSelect: originalSelect,
13975
- internalFetch: { baseFields: ["_id"] },
13976
- globalPermissions: globalForPlan
13977
- });
13978
- const merged = (0, import_utils38.uniq)([...normalizeSelect(ret.select), ...targetPlan.virtualOnlyDeps ?? []]).filter(
13979
- (t) => t !== "-_id" && !t.includes(".sub.")
13980
- );
13981
- if (!merged.includes("_id")) merged.push("_id");
13982
- ret.select = merged;
13983
- setPopulateTargetMeta(ret, {
13984
- targetModelName: refModelName,
13985
- virtualAccess: planAccess,
13986
- outputAccess: planAccess,
13987
- docPermissionsAccess: planAccess,
13988
- requestedSelect: originalSelect,
13989
- plan: targetPlan,
13990
- snapshot: targetSnapshot
13991
- });
13992
- } catch {
13993
- }
14302
+ ret.match = deepIsolateValue(intersectFilters(match, filter2));
14303
+ const planAccess = populateAccess;
14304
+ const targetSnapshot = targetConfiguration;
14305
+ const targetPlan = planVirtualProjection({
14306
+ receivingModelName: refModelName,
14307
+ snapshot: {
14308
+ permissionSchema: targetSnapshot.permissionSchema,
14309
+ virtuals: targetSnapshot.virtuals,
14310
+ alwaysSelectFields: targetSnapshot.alwaysSelectFields,
14311
+ modelPermissionPrefix: targetSnapshot.modelPermissionPrefix,
14312
+ requireExplicitSelect: targetSnapshot.requireExplicitSelect,
14313
+ documentPermissionField: targetSnapshot.documentPermissionField
14314
+ },
14315
+ virtualAccess: planAccess,
14316
+ outputAccess: planAccess,
14317
+ docPermissionsAccess: planAccess,
14318
+ requestedSelect: originalSelect,
14319
+ internalFetch: { baseFields: ["_id"] },
14320
+ globalPermissions: permissions
14321
+ });
14322
+ const merged = (0, import_utils39.uniq)([...normalizeSelect(ret.select), ...targetPlan.virtualOnlyDeps ?? []]).filter(
14323
+ (t) => t !== "-_id" && !t.includes(".sub.")
14324
+ );
14325
+ if (!merged.includes("_id")) merged.push("_id");
14326
+ ret.select = merged;
14327
+ setPopulateTargetMeta(ret, {
14328
+ targetModelName: refModelName,
14329
+ virtualAccess: planAccess,
14330
+ outputAccess: planAccess,
14331
+ docPermissionsAccess: planAccess,
14332
+ requestedSelect: originalSelect,
14333
+ plan: targetPlan,
14334
+ snapshot: targetSnapshot
14335
+ });
13994
14336
  return ret;
13995
14337
  })
13996
14338
  )
@@ -13999,10 +14341,10 @@ var Core = class {
13999
14341
  }
14000
14342
  async validate(modelName, allowedData, access, context) {
14001
14343
  const validate = getModelOption(modelName, `validate.${access}`, null);
14002
- if ((0, import_utils38.isFunction)(validate)) {
14344
+ if ((0, import_utils39.isFunction)(validate)) {
14003
14345
  const permissions = this.getGlobalPermissions();
14004
14346
  return validate.call(this.req, allowedData, permissions, context);
14005
- } else if ((0, import_utils38.isBoolean)(validate) || (0, import_utils38.isArray)(validate)) {
14347
+ } else if ((0, import_utils39.isBoolean)(validate) || (0, import_utils39.isArray)(validate)) {
14006
14348
  return validate;
14007
14349
  } else {
14008
14350
  return true;
@@ -14027,7 +14369,7 @@ var Core = class {
14027
14369
  const changeOptions = getModelOption(modelName, `onChange`, {});
14028
14370
  for (let x = 0; x < context.modifiedPaths.length; x++) {
14029
14371
  const mpath = context.modifiedPaths[x];
14030
- if ((0, import_utils38.isFunction)(changeOptions[mpath])) {
14372
+ if ((0, import_utils39.isFunction)(changeOptions[mpath])) {
14031
14373
  await changeOptions[mpath].call(
14032
14374
  this.req,
14033
14375
  context.originalDocumentSnapshot[mpath],
@@ -14051,7 +14393,7 @@ var Core = class {
14051
14393
  async genDocPermissions(modelName, doc, access, context) {
14052
14394
  const docPermissionsFn = getModelOption(modelName, `docPermissions.${access}`, null);
14053
14395
  let docPermissions = {};
14054
- if ((0, import_utils38.isFunction)(docPermissionsFn)) {
14396
+ if ((0, import_utils39.isFunction)(docPermissionsFn)) {
14055
14397
  const permissions = this.getGlobalPermissions();
14056
14398
  try {
14057
14399
  docPermissions = await getRequestWorkState(this.req, this.requestRuntime).hookGate.run(
@@ -14135,7 +14477,7 @@ var Core = class {
14135
14477
  readExists ? requestFields("read") : [],
14136
14478
  updateExists ? requestFields("update") : []
14137
14479
  ]);
14138
- const viewObj = (0, import_utils38.reduce)(
14480
+ const viewObj = (0, import_utils39.reduce)(
14139
14481
  views,
14140
14482
  (ret, view) => {
14141
14483
  ret[view] = true;
@@ -14143,7 +14485,7 @@ var Core = class {
14143
14485
  },
14144
14486
  {}
14145
14487
  );
14146
- const editObj = (0, import_utils38.reduce)(
14488
+ const editObj = (0, import_utils39.reduce)(
14147
14489
  edits,
14148
14490
  (ret, view) => {
14149
14491
  ret[view] = true;
@@ -14174,9 +14516,9 @@ var Core = class {
14174
14516
  });
14175
14517
  }
14176
14518
  runTasks(modelName, docObject, task) {
14177
- const tasks = (0, import_utils38.compact)((0, import_utils38.castArray)(task));
14519
+ const tasks = (0, import_utils39.compact)((0, import_utils39.castArray)(task));
14178
14520
  if (tasks.length === 0) return docObject;
14179
- (0, import_utils38.forEach)(tasks, (task2) => {
14521
+ (0, import_utils39.forEach)(tasks, (task2) => {
14180
14522
  const { type, args, options } = task2;
14181
14523
  switch (type) {
14182
14524
  case "COPY_AND_DEPOPULATE":
@@ -14246,9 +14588,9 @@ var Core = class {
14246
14588
  }
14247
14589
  const [, field, op] = keys2;
14248
14590
  const subOption = getExactModelOption(modelName, `operationAccess.${access}`);
14249
- if ((0, import_utils38.isUndefined)(subOption)) {
14591
+ if ((0, import_utils39.isUndefined)(subOption)) {
14250
14592
  const subFieldOption = getExactModelOption(modelName, `operationAccess.subs.${field}`);
14251
- if ((0, import_utils38.isUndefined)(subFieldOption)) {
14593
+ if ((0, import_utils39.isUndefined)(subFieldOption)) {
14252
14594
  const opOption = getModelOption(modelName, `operationAccess.${op}`);
14253
14595
  return this.canActivate(opOption);
14254
14596
  }
@@ -14314,25 +14656,25 @@ function guard(condition) {
14314
14656
  const permissions = req[PERMISSIONS];
14315
14657
  let cond = condition;
14316
14658
  let phas = (key) => permissions.has(key);
14317
- if ((0, import_utils39.isPlainObject)(condition)) {
14659
+ if ((0, import_utils40.isPlainObject)(condition)) {
14318
14660
  const { modelName, id, condition: _cond } = condition;
14319
14661
  const svc = req.macl.getPublicService(modelName);
14320
14662
  const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
14321
- let _id = (0, import_utils39.isString)(id) ? id : null;
14322
- if ((0, import_utils39.isPlainObject)(id)) {
14663
+ let _id = (0, import_utils40.isString)(id) ? id : null;
14664
+ if ((0, import_utils40.isPlainObject)(id)) {
14323
14665
  const { type = "param", key } = id;
14324
14666
  if (type === "param") {
14325
14667
  const paramValue = req.params[key];
14326
- _id = (0, import_utils39.isArray)(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
14668
+ _id = (0, import_utils40.isArray)(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
14327
14669
  } else if (type === "query") {
14328
14670
  const _qval = req.query[key];
14329
- if ((0, import_utils39.isArray)(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
14671
+ if ((0, import_utils40.isArray)(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
14330
14672
  else _id = _qval;
14331
14673
  } else {
14332
14674
  _id = null;
14333
14675
  }
14334
14676
  }
14335
- if (!(0, import_utils39.isString)(_id) || !_id) {
14677
+ if (!(0, import_utils40.isString)(_id) || !_id) {
14336
14678
  return next(new import_express_json_router5.default.clientErrors.BadRequestError());
14337
14679
  }
14338
14680
  const result2 = await svc._read(_id, { select });
@@ -14344,11 +14686,11 @@ function guard(condition) {
14344
14686
  cond = _cond;
14345
14687
  }
14346
14688
  const { stringHandler, arrayHandler } = createValidator(phas);
14347
- if ((0, import_utils39.isString)(cond)) {
14689
+ if ((0, import_utils40.isString)(cond)) {
14348
14690
  if (stringHandler(cond)) return next();
14349
- } else if ((0, import_utils39.isArray)(cond)) {
14691
+ } else if ((0, import_utils40.isArray)(cond)) {
14350
14692
  if (arrayHandler(cond)) return next();
14351
- } else if ((0, import_utils39.isFunction)(cond)) {
14693
+ } else if ((0, import_utils40.isFunction)(cond)) {
14352
14694
  const result2 = await cond.call(req, permissions);
14353
14695
  if (!!result2) return next();
14354
14696
  }
@@ -14361,7 +14703,7 @@ var import_express2 = __toESM(require("express"));
14361
14703
 
14362
14704
  // src/routers/model-router.ts
14363
14705
  var import_express_json_router8 = __toESM(require("@web-ts-toolkit/express-json-router"));
14364
- var import_utils43 = require("@web-ts-toolkit/utils");
14706
+ var import_utils44 = require("@web-ts-toolkit/utils");
14365
14707
  init_logger_helpers();
14366
14708
  init_runtime();
14367
14709
  init_runtime_context();
@@ -14495,7 +14837,7 @@ init_request_complexity();
14495
14837
 
14496
14838
  // src/validation/common.ts
14497
14839
  var import_zod2 = require("zod");
14498
- var import_utils40 = require("@web-ts-toolkit/utils");
14840
+ var import_utils41 = require("@web-ts-toolkit/utils");
14499
14841
 
14500
14842
  // src/validation/public-admission-schema.ts
14501
14843
  init_public_admission();
@@ -14624,7 +14966,7 @@ var correlatedFilterSchema = import_zod2.z.record(import_zod2.z.string(), import
14624
14966
  }
14625
14967
  });
14626
14968
  var correlatedEmptyOptionsSchema = import_zod2.z.unknown().refine(
14627
- (value) => value === void 0 || (0, import_utils40.isPlainObject)(value) && Object.keys(value).length === 0,
14969
+ (value) => value === void 0 || (0, import_utils41.isPlainObject)(value) && Object.keys(value).length === 0,
14628
14970
  "Correlated include options must be absent or empty"
14629
14971
  ).optional();
14630
14972
  var correlatedPathSchema = import_zod2.z.string().min(1).superRefine((path, ctx) => {
@@ -14661,7 +15003,7 @@ var correlatedListArgsSchema = import_zod2.z.object({
14661
15003
  include: import_zod2.z.lazy(() => correlatedNestedIncludeSchema).optional()
14662
15004
  }).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
14663
15005
  var correlatedCountArgsSchema = import_zod2.z.unknown().refine(
14664
- (value) => value === void 0 || (0, import_utils40.isPlainObject)(value) && Object.keys(value).length === 0,
15006
+ (value) => value === void 0 || (0, import_utils41.isPlainObject)(value) && Object.keys(value).length === 0,
14665
15007
  "Count includes accept no args"
14666
15008
  ).optional();
14667
15009
  var correlatedReadByIdSchema = import_zod2.z.object({
@@ -14714,7 +15056,7 @@ var correlatedNestedIncludeSchema = import_zod2.z.union([includeItemSchema, impo
14714
15056
  var rejectDuplicateCorrelatedPaths = (items, ctx) => {
14715
15057
  const seen = /* @__PURE__ */ new Set();
14716
15058
  items.forEach((item, index) => {
14717
- if ((0, import_utils40.isPlainObject)(item) && item.mode === "correlated" && typeof item.path === "string") {
15059
+ if ((0, import_utils41.isPlainObject)(item) && item.mode === "correlated" && typeof item.path === "string") {
14718
15060
  if (seen.has(item.path)) {
14719
15061
  ctx.addIssue({
14720
15062
  code: import_zod2.z.ZodIssueCode.custom,
@@ -15687,7 +16029,7 @@ var rootQuerySchema = publicBoundarySchema(
15687
16029
  init_helpers();
15688
16030
 
15689
16031
  // src/routers/shared.ts
15690
- var import_utils41 = require("@web-ts-toolkit/utils");
16032
+ var import_utils42 = require("@web-ts-toolkit/utils");
15691
16033
 
15692
16034
  // src/routers/model-router-collection-routes.ts
15693
16035
  init_schemas();
@@ -15718,15 +16060,15 @@ function setModelCollectionRoutes(context) {
15718
16060
  sort
15719
16061
  } = parseQuery(requestSchemas.listQuery, req.query);
15720
16062
  const svc = context.getPublicService(req);
15721
- const includeCount = (0, import_utils41.parseBooleanString)(include_count);
15722
- const includeExtraHeaders = (0, import_utils41.parseBooleanString)(include_extra_headers);
16063
+ const includeCount = (0, import_utils42.parseBooleanString)(include_count);
16064
+ const includeExtraHeaders = (0, import_utils42.parseBooleanString)(include_extra_headers);
15723
16065
  const result2 = await svc._list(
15724
16066
  {},
15725
16067
  { select: parseSelectParam(select), sort, skip, limit, page, pageSize: page_size },
15726
16068
  {
15727
- skim: (0, import_utils41.parseBooleanString)(skim),
15728
- includePermissions: (0, import_utils41.parseBooleanString)(include_permissions),
15729
- includeFieldPermissions: (0, import_utils41.parseBooleanString)(include_field_permissions),
16069
+ skim: (0, import_utils42.parseBooleanString)(skim),
16070
+ includePermissions: (0, import_utils42.parseBooleanString)(include_permissions),
16071
+ includeFieldPermissions: (0, import_utils42.parseBooleanString)(include_field_permissions),
15730
16072
  includeCount
15731
16073
  }
15732
16074
  );
@@ -15786,8 +16128,8 @@ function setModelCollectionRoutes(context) {
15786
16128
  data,
15787
16129
  {},
15788
16130
  {
15789
- includePermissions: (0, import_utils41.parseBooleanString)(include_permissions),
15790
- includeFieldPermissions: (0, import_utils41.parseBooleanString)(include_field_permissions)
16131
+ includePermissions: (0, import_utils42.parseBooleanString)(include_permissions),
16132
+ includeFieldPermissions: (0, import_utils42.parseBooleanString)(include_field_permissions)
15791
16133
  }
15792
16134
  );
15793
16135
  handleResultError(result2);
@@ -15825,8 +16167,8 @@ function setModelCollectionRoutes(context) {
15825
16167
  finalData,
15826
16168
  { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
15827
16169
  {
15828
- includePermissions: includePermissions ?? (0, import_utils41.parseBooleanString)(include_permissions),
15829
- includeFieldPermissions: includeFieldPermissions ?? (0, import_utils41.parseBooleanString)(include_field_permissions),
16170
+ includePermissions: includePermissions ?? (0, import_utils42.parseBooleanString)(include_permissions),
16171
+ includeFieldPermissions: includeFieldPermissions ?? (0, import_utils42.parseBooleanString)(include_field_permissions),
15830
16172
  populateAccess
15831
16173
  }
15832
16174
  );
@@ -15923,9 +16265,9 @@ function setModelDocumentRoutes(context) {
15923
16265
  id,
15924
16266
  { select: parseSelectParam(select) },
15925
16267
  {
15926
- includePermissions: (0, import_utils41.parseBooleanString)(include_permissions),
15927
- includeFieldPermissions: (0, import_utils41.parseBooleanString)(include_field_permissions),
15928
- tryList: (0, import_utils41.parseBooleanString)(try_list)
16268
+ includePermissions: (0, import_utils42.parseBooleanString)(include_permissions),
16269
+ includeFieldPermissions: (0, import_utils42.parseBooleanString)(include_field_permissions),
16270
+ tryList: (0, import_utils42.parseBooleanString)(try_list)
15929
16271
  }
15930
16272
  );
15931
16273
  handleResultError(result2);
@@ -16025,9 +16367,9 @@ function setModelDocumentRoutes(context) {
16025
16367
  data,
16026
16368
  { select: parseSelectParam(select) },
16027
16369
  {
16028
- returningAll: (0, import_utils41.parseBooleanString)(returning_all),
16029
- includePermissions: (0, import_utils41.parseBooleanString)(include_permissions),
16030
- includeFieldPermissions: (0, import_utils41.parseBooleanString)(include_field_permissions)
16370
+ returningAll: (0, import_utils42.parseBooleanString)(returning_all),
16371
+ includePermissions: (0, import_utils42.parseBooleanString)(include_permissions),
16372
+ includeFieldPermissions: (0, import_utils42.parseBooleanString)(include_field_permissions)
16031
16373
  }
16032
16374
  );
16033
16375
  handleResultError(result2);
@@ -16076,9 +16418,9 @@ function setModelDocumentRoutes(context) {
16076
16418
  finalData,
16077
16419
  { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
16078
16420
  {
16079
- returningAll: returningAll ?? (0, import_utils41.parseBooleanString)(returning_all),
16080
- includePermissions: includePermissions ?? (0, import_utils41.parseBooleanString)(include_permissions),
16081
- includeFieldPermissions: includeFieldPermissions ?? (0, import_utils41.parseBooleanString)(include_field_permissions),
16421
+ returningAll: returningAll ?? (0, import_utils42.parseBooleanString)(returning_all),
16422
+ includePermissions: includePermissions ?? (0, import_utils42.parseBooleanString)(include_permissions),
16423
+ includeFieldPermissions: includeFieldPermissions ?? (0, import_utils42.parseBooleanString)(include_field_permissions),
16082
16424
  populateAccess
16083
16425
  }
16084
16426
  );
@@ -16109,9 +16451,9 @@ function setModelDocumentRoutes(context) {
16109
16451
  body,
16110
16452
  {},
16111
16453
  {
16112
- returningAll: (0, import_utils41.parseBooleanString)(returning_all),
16113
- includePermissions: (0, import_utils41.parseBooleanString)(include_permissions),
16114
- includeFieldPermissions: (0, import_utils41.parseBooleanString)(include_field_permissions)
16454
+ returningAll: (0, import_utils42.parseBooleanString)(returning_all),
16455
+ includePermissions: (0, import_utils42.parseBooleanString)(include_permissions),
16456
+ includeFieldPermissions: (0, import_utils42.parseBooleanString)(include_field_permissions)
16115
16457
  }
16116
16458
  );
16117
16459
  handleResultError(result2);
@@ -16158,9 +16500,9 @@ function setModelDocumentRoutes(context) {
16158
16500
  finalData,
16159
16501
  { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
16160
16502
  {
16161
- returningAll: returningAll ?? (0, import_utils41.parseBooleanString)(returning_all),
16162
- includePermissions: includePermissions ?? (0, import_utils41.parseBooleanString)(include_permissions),
16163
- includeFieldPermissions: includeFieldPermissions ?? (0, import_utils41.parseBooleanString)(include_field_permissions),
16503
+ returningAll: returningAll ?? (0, import_utils42.parseBooleanString)(returning_all),
16504
+ includePermissions: includePermissions ?? (0, import_utils42.parseBooleanString)(include_permissions),
16505
+ includeFieldPermissions: includeFieldPermissions ?? (0, import_utils42.parseBooleanString)(include_field_permissions),
16164
16506
  populateAccess
16165
16507
  }
16166
16508
  );
@@ -16400,11 +16742,11 @@ function setModelSubDocumentRoutes(context) {
16400
16742
  }
16401
16743
 
16402
16744
  // src/openapi/route-registration.ts
16403
- var import_utils42 = require("@web-ts-toolkit/utils");
16745
+ var import_utils43 = require("@web-ts-toolkit/utils");
16404
16746
  function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
16405
16747
  runtime.registerOpenApiRoute({
16406
16748
  ...route,
16407
- path: (0, import_utils42.normalizeUrlPath)(basePath + route.path),
16749
+ path: (0, import_utils43.normalizeUrlPath)(basePath + route.path),
16408
16750
  tags: route.tags ?? [defaultTag]
16409
16751
  });
16410
16752
  }
@@ -16412,8 +16754,8 @@ function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
16412
16754
  // src/routers/model-router.ts
16413
16755
  var clientErrors2 = import_express_json_router8.default.clientErrors;
16414
16756
  function setOption(parentKey, optionKey, option) {
16415
- const key = (0, import_utils43.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
16416
- const value = (0, import_utils43.isUndefined)(option) ? optionKey : option;
16757
+ const key = (0, import_utils44.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
16758
+ const value = (0, import_utils44.isUndefined)(option) ? optionKey : option;
16417
16759
  assertMutableRouterOption("model", key);
16418
16760
  this.runtime.setModelOption(
16419
16761
  this.modelName,
@@ -16615,7 +16957,7 @@ var ModelRouter = class _ModelRouter {
16615
16957
  this.runtime = runtime;
16616
16958
  this.runtime.setModelOptions(modelName, initialOptions);
16617
16959
  this.options = this.runtime.getModelOptions(modelName);
16618
- this.fullBasePath = (0, import_utils43.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
16960
+ this.fullBasePath = (0, import_utils44.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
16619
16961
  this.modelName = modelName;
16620
16962
  this.router = new import_express_json_router8.default(this.options.basePath, createSetCore(this.runtime), accessRouterResponseHandler);
16621
16963
  this.model = new model_default(modelName, this.runtime);
@@ -16701,13 +17043,13 @@ var ModelRouter = class _ModelRouter {
16701
17043
  }
16702
17044
  logEndpoints() {
16703
17045
  runWithRuntime(this.runtime, () => {
16704
- (0, import_utils43.forEach)(this.router.getEndpoints(), ({ method, path }) => {
16705
- logInfoMessage(`${(0, import_utils43.padEnd)(method, 6)} ${(0, import_utils43.normalizeUrlPath)(this.options.parentPath + path)}`);
17046
+ (0, import_utils44.forEach)(this.router.getEndpoints(), ({ method, path }) => {
17047
+ logInfoMessage(`${(0, import_utils44.padEnd)(method, 6)} ${(0, import_utils44.normalizeUrlPath)(this.options.parentPath + path)}`);
16706
17048
  });
16707
17049
  });
16708
17050
  }
16709
17051
  set(keyOrOptions, value) {
16710
- if (arguments.length === 2 && (0, import_utils43.isString)(keyOrOptions)) {
17052
+ if (arguments.length === 2 && (0, import_utils44.isString)(keyOrOptions)) {
16711
17053
  assertMutableRouterOption("model", keyOrOptions);
16712
17054
  this.runtime.setModelOption(
16713
17055
  this.modelName,
@@ -16715,7 +17057,7 @@ var ModelRouter = class _ModelRouter {
16715
17057
  value
16716
17058
  );
16717
17059
  }
16718
- if (arguments.length === 1 && (0, import_utils43.isPlainObject)(keyOrOptions)) {
17060
+ if (arguments.length === 1 && (0, import_utils44.isPlainObject)(keyOrOptions)) {
16719
17061
  assertMutableRouterOptions("model", keyOrOptions);
16720
17062
  this.runtime.setModelOptions(
16721
17063
  this.modelName,
@@ -16743,7 +17085,7 @@ var ModelRouter = class _ModelRouter {
16743
17085
 
16744
17086
  // src/routers/root-router.ts
16745
17087
  var import_express_json_router9 = __toESM(require("@web-ts-toolkit/express-json-router"));
16746
- var import_utils44 = require("@web-ts-toolkit/utils");
17088
+ var import_utils45 = require("@web-ts-toolkit/utils");
16747
17089
 
16748
17090
  // src/core-data.ts
16749
17091
  init_options();
@@ -17051,7 +17393,7 @@ var RootRouter = class {
17051
17393
  this.setRoutes();
17052
17394
  }
17053
17395
  registerOpenApiRoute(route) {
17054
- registerOpenApiRoute(this.runtime, (0, import_utils44.normalizeUrlPath)(this.basename), "root", route);
17396
+ registerOpenApiRoute(this.runtime, (0, import_utils45.normalizeUrlPath)(this.basename), "root", route);
17055
17397
  }
17056
17398
  wrapResult(index, item, result2) {
17057
17399
  return {
@@ -17160,7 +17502,7 @@ var RootRouter = class {
17160
17502
  groupItemsByOrder(items) {
17161
17503
  const groups = /* @__PURE__ */ new Map();
17162
17504
  items.forEach((item, index) => {
17163
- const order = (0, import_utils44.isNumber)(item.order) ? item.order : 0;
17505
+ const order = (0, import_utils45.isNumber)(item.order) ? item.order : 0;
17164
17506
  const group = groups.get(order);
17165
17507
  if (group) {
17166
17508
  group.push({ index, item });
@@ -17231,7 +17573,7 @@ var RootRouter = class {
17231
17573
  const arrResult = await this.runGroup(req, groupedItems[x]);
17232
17574
  results.push(...arrResult);
17233
17575
  }
17234
- return (0, import_utils44.orderBy)(results, ["index"], ["asc"]);
17576
+ return (0, import_utils45.orderBy)(results, ["index"], ["asc"]);
17235
17577
  });
17236
17578
  this.registerOpenApiRoute({
17237
17579
  method: "post",
@@ -17249,14 +17591,14 @@ var RootRouter = class {
17249
17591
 
17250
17592
  // src/routers/data-router.ts
17251
17593
  var import_express_json_router10 = __toESM(require("@web-ts-toolkit/express-json-router"));
17252
- var import_utils45 = require("@web-ts-toolkit/utils");
17594
+ var import_utils46 = require("@web-ts-toolkit/utils");
17253
17595
  init_runtime();
17254
17596
  init_helpers();
17255
17597
  init_schemas();
17256
17598
  var clientErrors4 = import_express_json_router10.default.clientErrors;
17257
17599
  function setOption2(parentKey, optionKey, option) {
17258
- const key = (0, import_utils45.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
17259
- const value = (0, import_utils45.isUndefined)(option) ? optionKey : option;
17600
+ const key = (0, import_utils46.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
17601
+ const value = (0, import_utils46.isUndefined)(option) ? optionKey : option;
17260
17602
  assertMutableRouterOption("data", key);
17261
17603
  this.runtime.setDataOption(this.dataName, key, value);
17262
17604
  return this;
@@ -17305,7 +17647,7 @@ var DataRouter = class {
17305
17647
  this.runtime = runtime;
17306
17648
  this.runtime.setDataOptions(dataName, initialOptions);
17307
17649
  this.options = this.runtime.getDataOptions(dataName);
17308
- this.fullBasePath = (0, import_utils45.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
17650
+ this.fullBasePath = (0, import_utils46.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
17309
17651
  this.dataName = dataName;
17310
17652
  this.router = new import_express_json_router10.default(this.options.basePath, createSetDataCore(this.runtime), accessRouterResponseHandler);
17311
17653
  this.setCollectionRoutes();
@@ -17338,8 +17680,8 @@ var DataRouter = class {
17338
17680
  req.query
17339
17681
  );
17340
17682
  const svc = this.getService(req);
17341
- const includeCount = (0, import_utils41.parseBooleanString)(include_count);
17342
- const includeExtraHeaders = (0, import_utils41.parseBooleanString)(include_extra_headers);
17683
+ const includeCount = (0, import_utils42.parseBooleanString)(include_count);
17684
+ const includeExtraHeaders = (0, import_utils42.parseBooleanString)(include_extra_headers);
17343
17685
  const result2 = await svc.find(
17344
17686
  {},
17345
17687
  { select: parseSelectParam(select), sort, skip, limit, page, pageSize: page_size },
@@ -17457,7 +17799,7 @@ var DataRouter = class {
17457
17799
  });
17458
17800
  }
17459
17801
  set(keyOrOptions, value) {
17460
- if (arguments.length === 2 && (0, import_utils45.isString)(keyOrOptions)) {
17802
+ if (arguments.length === 2 && (0, import_utils46.isString)(keyOrOptions)) {
17461
17803
  assertMutableRouterOption("data", keyOrOptions);
17462
17804
  this.runtime.setDataOption(
17463
17805
  this.dataName,
@@ -17465,7 +17807,7 @@ var DataRouter = class {
17465
17807
  value
17466
17808
  );
17467
17809
  }
17468
- if (arguments.length === 1 && (0, import_utils45.isPlainObject)(keyOrOptions)) {
17810
+ if (arguments.length === 1 && (0, import_utils46.isPlainObject)(keyOrOptions)) {
17469
17811
  assertMutableRouterOptions("data", keyOrOptions);
17470
17812
  this.runtime.setDataOptions(this.dataName, keyOrOptions);
17471
17813
  }
@@ -17600,7 +17942,7 @@ function createRuntimeApi(runtime) {
17600
17942
  const wtt2 = runtimeMiddleware;
17601
17943
  wtt2.runtime = runtime;
17602
17944
  wtt2.createRouter = function(modelName, options) {
17603
- if ((0, import_utils46.isUndefined)(options)) {
17945
+ if ((0, import_utils47.isUndefined)(options)) {
17604
17946
  return new RootRouter(modelName, runtime);
17605
17947
  }
17606
17948
  if (typeof modelName === "string") {
@@ -17621,10 +17963,10 @@ function createRuntimeApi(runtime) {
17621
17963
  };
17622
17964
  wtt2.combineRoutes = combineRoutes;
17623
17965
  wtt2.set = function(keyOrOptions, value) {
17624
- if (arguments.length === 2 && (0, import_utils46.isString)(keyOrOptions)) {
17966
+ if (arguments.length === 2 && (0, import_utils47.isString)(keyOrOptions)) {
17625
17967
  return runtime.setGlobalOption(keyOrOptions, value);
17626
17968
  }
17627
- if (arguments.length === 1 && (0, import_utils46.isPlainObject)(keyOrOptions)) {
17969
+ if (arguments.length === 1 && (0, import_utils47.isPlainObject)(keyOrOptions)) {
17628
17970
  return runtime.setGlobalOptions(keyOrOptions);
17629
17971
  }
17630
17972
  };