@fitzzero/quickdraw-core 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -17
- package/dist/{admin-BiU4LRZS.d.ts → admin-CBlTY0do.d.ts} +24 -9
- package/dist/{adminSchemas-k5a1Ov7b.d.ts → adminSchemas-zji11V74.d.ts} +14 -8
- package/dist/{caller-DHzK3oiI.d.ts → caller-BqCYnpmC.d.ts} +1 -1
- package/dist/{chunk-5IC5VZ7T.js → chunk-3JNMVB2V.js} +3 -3
- package/dist/{chunk-DNCAI3KY.js → chunk-4DIQHASK.js} +1 -1
- package/dist/chunk-4DIQHASK.js.map +1 -0
- package/dist/{chunk-B2UQ27GH.js → chunk-CDKG4TFA.js} +25 -5
- package/dist/{chunk-B2UQ27GH.js.map → chunk-CDKG4TFA.js.map} +1 -1
- package/dist/{chunk-XFDOGTMC.js → chunk-HQ6WIRE2.js} +3 -3
- package/dist/{chunk-LLLYILEP.js → chunk-NS5PUHIZ.js} +283 -22
- package/dist/chunk-NS5PUHIZ.js.map +1 -0
- package/dist/{chunk-XUSN5WKU.js → chunk-O2PWC2YW.js} +3 -3
- package/dist/{chunk-7QIY2DDX.js → chunk-WVIRXYI4.js} +2 -2
- package/dist/{chunk-7QIY2DDX.js.map → chunk-WVIRXYI4.js.map} +1 -1
- package/dist/{chunk-IRGAGRVG.js → chunk-ZACRRYWL.js} +3 -3
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +4 -4
- package/dist/{clientTypes-B3o_99Xq.d.ts → clientTypes-DuZW_8mb.d.ts} +1 -1
- package/dist/{createServer-DUacdfKl.d.ts → createServer-CGsJlxM8.d.ts} +2 -2
- package/dist/{createTestApp-DFLXoTKm.d.ts → createTestApp-Cg2CZa1g.d.ts} +3 -3
- package/dist/{http--uoooIH5.d.ts → http-D3fSFQ57.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/prisma/index.d.ts +1 -1
- package/dist/prisma/index.js +1 -1
- package/dist/server/auth/index.d.ts +11 -2
- package/dist/server/auth/index.js +0 -5
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/index.d.ts +83 -17
- package/dist/server/index.js +87 -160
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +2 -2
- package/dist/{sharing-n4ss-ONM.d.ts → sharing-CoreezAq.d.ts} +2 -2
- package/dist/testing/client.d.ts +7 -7
- package/dist/testing/client.js +5 -5
- package/dist/testing/index.d.ts +6 -6
- package/dist/testing/index.js +3 -3
- package/dist/testing/mock.d.ts +3 -3
- package/dist/testing/mock.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-DNCAI3KY.js.map +0 -1
- package/dist/chunk-LLLYILEP.js.map +0 -1
- /package/dist/{chunk-5IC5VZ7T.js.map → chunk-3JNMVB2V.js.map} +0 -0
- /package/dist/{chunk-XFDOGTMC.js.map → chunk-HQ6WIRE2.js.map} +0 -0
- /package/dist/{chunk-XUSN5WKU.js.map → chunk-O2PWC2YW.js.map} +0 -0
- /package/dist/{chunk-IRGAGRVG.js.map → chunk-ZACRRYWL.js.map} +0 -0
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
searchTargetOf,
|
|
14
14
|
streamMaxOf,
|
|
15
15
|
viewPredicate
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-O2PWC2YW.js";
|
|
17
17
|
import {
|
|
18
18
|
MAX_SUBSCRIBE_IDS,
|
|
19
19
|
PROTOCOL_VERSION,
|
|
20
20
|
QUICKDRAW_VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-WVIRXYI4.js";
|
|
22
22
|
import {
|
|
23
23
|
isScopedStream
|
|
24
24
|
} from "./chunk-5YRQ26LF.js";
|
|
@@ -849,4 +849,4 @@ function createMockClient(contracts, options = {}) {
|
|
|
849
849
|
export {
|
|
850
850
|
createMockClient
|
|
851
851
|
};
|
|
852
|
-
//# sourceMappingURL=chunk-
|
|
852
|
+
//# sourceMappingURL=chunk-HQ6WIRE2.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
quietly,
|
|
11
11
|
strictWarningsOf,
|
|
12
12
|
versionTime
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4DIQHASK.js";
|
|
14
14
|
import {
|
|
15
15
|
isCallEnvelope,
|
|
16
16
|
isCancel
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
streamRoom,
|
|
78
78
|
topicRoom,
|
|
79
79
|
userRoom
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-WVIRXYI4.js";
|
|
81
81
|
import {
|
|
82
82
|
hasJsonSchema
|
|
83
83
|
} from "./chunk-47AEXVDM.js";
|
|
@@ -424,9 +424,9 @@ function addBranchKeys(node, root, keys, seen) {
|
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
for (const keyword of BRANCHES) {
|
|
427
|
-
const
|
|
428
|
-
if (Array.isArray(
|
|
429
|
-
for (const member of
|
|
427
|
+
const members2 = target2[keyword];
|
|
428
|
+
if (Array.isArray(members2)) {
|
|
429
|
+
for (const member of members2) {
|
|
430
430
|
addBranchKeys(member, root, keys, seen);
|
|
431
431
|
}
|
|
432
432
|
}
|
|
@@ -673,12 +673,12 @@ async function membersAmong(storage, collection, scope, ids) {
|
|
|
673
673
|
}
|
|
674
674
|
async function readItemsById(storage, collection, scope, ids, rev) {
|
|
675
675
|
const wanted = [...new Set(ids)];
|
|
676
|
-
const
|
|
677
|
-
if (
|
|
676
|
+
const members2 = wanted.length === 0 ? void 0 : await membersAmong(storage, collection, scope, wanted);
|
|
677
|
+
if (members2 === void 0) {
|
|
678
678
|
return { rev, items: [] };
|
|
679
679
|
}
|
|
680
680
|
const rows = await storage.findMany(collection.model, {
|
|
681
|
-
where:
|
|
681
|
+
where: members2,
|
|
682
682
|
select: collection.item.select
|
|
683
683
|
});
|
|
684
684
|
const found = new Map(rows.flatMap((row) => typeof row.id === "string" ? [[row.id, row]] : []));
|
|
@@ -792,10 +792,10 @@ function indexRowFrom(collection, item, row, rev) {
|
|
|
792
792
|
const id = typeof row.id === "string" ? row.id : "";
|
|
793
793
|
return [id, version ?? rev, ...(collection.index ?? []).map((field) => values[field] ?? null)];
|
|
794
794
|
}
|
|
795
|
-
async function readIndex(storage, collection,
|
|
795
|
+
async function readIndex(storage, collection, members2, nullable, rev, max = INDEX_MAX_ROWS) {
|
|
796
796
|
const fields = collection.index ?? [];
|
|
797
797
|
const rows = await storage.findMany(collection.model, {
|
|
798
|
-
where:
|
|
798
|
+
where: members2,
|
|
799
799
|
select: indexSelect(collection, fields),
|
|
800
800
|
orderBy: orderByOf(collection.order, nullable),
|
|
801
801
|
take: max + 1
|
|
@@ -1056,8 +1056,17 @@ function bindingOf(state, contract) {
|
|
|
1056
1056
|
}
|
|
1057
1057
|
return binding;
|
|
1058
1058
|
}
|
|
1059
|
+
function sameRead(a, b) {
|
|
1060
|
+
const columns = ["model", "entry", "user", "level"].every((key) => a[key] === b[key]);
|
|
1061
|
+
if (!columns || a.levels === void 0 || b.levels === void 0) {
|
|
1062
|
+
return columns && a.levels === b.levels;
|
|
1063
|
+
}
|
|
1064
|
+
const mapped = b.levels;
|
|
1065
|
+
const roles = Object.entries(a.levels);
|
|
1066
|
+
return roles.length === Object.keys(mapped).length && roles.every(([role, level]) => Object.hasOwn(mapped, role) && mapped[role] === level);
|
|
1067
|
+
}
|
|
1059
1068
|
function membershipScope(scope, binding, read) {
|
|
1060
|
-
const declared = binding.memberships.get(read);
|
|
1069
|
+
const declared = binding.memberships.get(read) ?? [...binding.memberships].find(([known]) => sameRead(known, read))?.[1];
|
|
1061
1070
|
if (declared !== void 0) {
|
|
1062
1071
|
return [scope.cached, declared];
|
|
1063
1072
|
}
|
|
@@ -2336,6 +2345,220 @@ function createBasicAccessEngine(options = {}) {
|
|
|
2336
2345
|
});
|
|
2337
2346
|
}
|
|
2338
2347
|
|
|
2348
|
+
// src/server/access/policies/anyOf.ts
|
|
2349
|
+
function unionReads(policies) {
|
|
2350
|
+
const memberships = /* @__PURE__ */ new Set();
|
|
2351
|
+
for (const policy of policies) {
|
|
2352
|
+
for (const read of policy.reads.memberships) {
|
|
2353
|
+
memberships.add(read);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
return {
|
|
2357
|
+
columns: [...new Set(policies.flatMap((policy) => policy.reads.columns))],
|
|
2358
|
+
memberships: [...memberships],
|
|
2359
|
+
inherits: [...new Set(policies.flatMap((policy) => policy.reads.inherits))],
|
|
2360
|
+
parents: policies.flatMap((policy) => policy.reads.parents ?? []),
|
|
2361
|
+
storage: policies.some((policy) => policy.reads.storage)
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
var MEMBERS = /* @__PURE__ */ new WeakMap();
|
|
2365
|
+
function anyOfPolicies(policy) {
|
|
2366
|
+
return MEMBERS.get(policy);
|
|
2367
|
+
}
|
|
2368
|
+
function anyOf(...policies) {
|
|
2369
|
+
if (policies.length === 0 || !policies.every(isAccessPolicy)) {
|
|
2370
|
+
throw new TypeError("anyOf(...policies): pass one or more access policies");
|
|
2371
|
+
}
|
|
2372
|
+
const all = Object.freeze([...policies]);
|
|
2373
|
+
const combined = definePolicy({
|
|
2374
|
+
kind: "anyOf",
|
|
2375
|
+
policies: all,
|
|
2376
|
+
reads: unionReads(all),
|
|
2377
|
+
async levelsFor(principal, ids, tools) {
|
|
2378
|
+
const each = await Promise.all(all.map((policy) => policy.levelsFor(principal, ids, tools)));
|
|
2379
|
+
const levels = /* @__PURE__ */ new Map();
|
|
2380
|
+
for (const id of ids) {
|
|
2381
|
+
levels.set(
|
|
2382
|
+
id,
|
|
2383
|
+
each.reduce((level, granted) => maxLevel(level, granted.get(id)), null)
|
|
2384
|
+
);
|
|
2385
|
+
}
|
|
2386
|
+
return levels;
|
|
2387
|
+
},
|
|
2388
|
+
async accessWhere(principal, level, tools) {
|
|
2389
|
+
const filters = await Promise.all(
|
|
2390
|
+
all.map((policy) => policy.accessWhere(principal, level, tools))
|
|
2391
|
+
);
|
|
2392
|
+
const matching = filters.filter((filter) => filter !== "none");
|
|
2393
|
+
if (matching.some((filter) => Object.keys(filter).length === 0)) {
|
|
2394
|
+
return {};
|
|
2395
|
+
}
|
|
2396
|
+
if (matching.length <= 1) {
|
|
2397
|
+
return matching[0] ?? "none";
|
|
2398
|
+
}
|
|
2399
|
+
return { OR: matching };
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
MEMBERS.set(combined, all);
|
|
2403
|
+
return combined;
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
// src/server/access/policies/members.ts
|
|
2407
|
+
function membershipOf(policy) {
|
|
2408
|
+
return policy.kind === "members" ? policy.reads.memberships[0] : void 0;
|
|
2409
|
+
}
|
|
2410
|
+
function checkLevels(owner, levels) {
|
|
2411
|
+
if (levels === void 0) {
|
|
2412
|
+
return void 0;
|
|
2413
|
+
}
|
|
2414
|
+
const valid = typeof levels === "object" && levels !== null && !Array.isArray(levels) && Object.values(levels).every(isAccessLevel);
|
|
2415
|
+
if (!valid) {
|
|
2416
|
+
throw new TypeError(
|
|
2417
|
+
`${owner}: levels must map stored roles to access levels, as in { editor: "Moderate" }`
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
return Object.freeze({ ...levels });
|
|
2421
|
+
}
|
|
2422
|
+
function membershipRead(options, owner) {
|
|
2423
|
+
const { model, entry, user, level, levels } = options;
|
|
2424
|
+
return Object.freeze({
|
|
2425
|
+
model: checkName(owner("model"), "model", model),
|
|
2426
|
+
entry: checkName(owner("entry"), "entry", entry),
|
|
2427
|
+
user: checkName(owner("user"), "user", user),
|
|
2428
|
+
level: checkName(owner("level"), "level", level),
|
|
2429
|
+
levels: checkLevels(owner("levels"), levels)
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
function rolesAtLeast(read, level) {
|
|
2433
|
+
const enough = new Set(levelsAtLeast(level));
|
|
2434
|
+
if (read.levels === void 0) {
|
|
2435
|
+
return [...enough];
|
|
2436
|
+
}
|
|
2437
|
+
return Object.entries(read.levels).filter(([, mapped]) => enough.has(mapped)).map(([role]) => role);
|
|
2438
|
+
}
|
|
2439
|
+
function members(options) {
|
|
2440
|
+
if (typeof options !== "object" || options === null) {
|
|
2441
|
+
throw new TypeError("members(options): options must be { model, entry, user, level }");
|
|
2442
|
+
}
|
|
2443
|
+
const read = membershipRead(options, (option) => `members({ ${option} })`);
|
|
2444
|
+
return definePolicy({
|
|
2445
|
+
kind: "members",
|
|
2446
|
+
membership: read,
|
|
2447
|
+
reads: { columns: [], memberships: [read], inherits: [], storage: true },
|
|
2448
|
+
levelsFor: (principal, ids, tools) => tools.memberships(read, principal.userId, ids),
|
|
2449
|
+
async accessWhere(principal, level, tools) {
|
|
2450
|
+
const roles = rolesAtLeast(read, level);
|
|
2451
|
+
if (roles.length === 0) {
|
|
2452
|
+
return "none";
|
|
2453
|
+
}
|
|
2454
|
+
const rows = await tools.storage.findMany(read.model, {
|
|
2455
|
+
where: { [read.user]: principal.userId, [read.level]: { in: roles } },
|
|
2456
|
+
select: { [read.entry]: true }
|
|
2457
|
+
});
|
|
2458
|
+
const entries = rows.map((row) => row[read.entry]);
|
|
2459
|
+
return idFilter(entries.filter((entry) => typeof entry === "string"));
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
// src/server/access/policies/resolver.ts
|
|
2465
|
+
var UNDECLARED = /* @__PURE__ */ new WeakSet();
|
|
2466
|
+
function declaresNoReads(policy) {
|
|
2467
|
+
return UNDECLARED.has(policy);
|
|
2468
|
+
}
|
|
2469
|
+
function noReads() {
|
|
2470
|
+
return { columns: [], memberships: [], inherits: [], storage: false };
|
|
2471
|
+
}
|
|
2472
|
+
var READ_KEYS = ["columns", "memberships"];
|
|
2473
|
+
function readsProblem(message) {
|
|
2474
|
+
throw new TypeError(`resolver({ reads }): ${message}`);
|
|
2475
|
+
}
|
|
2476
|
+
function checkColumns(columns) {
|
|
2477
|
+
if (columns === void 0) {
|
|
2478
|
+
return [];
|
|
2479
|
+
}
|
|
2480
|
+
const valid = Array.isArray(columns) && columns.every((column) => typeof column === "string" && column.length > 0);
|
|
2481
|
+
return valid ? Object.freeze([...new Set(columns)]) : readsProblem("columns must be a list of column names");
|
|
2482
|
+
}
|
|
2483
|
+
function checkMemberships(memberships) {
|
|
2484
|
+
if (memberships === void 0) {
|
|
2485
|
+
return [];
|
|
2486
|
+
}
|
|
2487
|
+
const valid = Array.isArray(memberships) && memberships.every((each) => typeof each === "object" && each !== null && !Array.isArray(each));
|
|
2488
|
+
if (!valid) {
|
|
2489
|
+
readsProblem("memberships must be a list of { model, entry, user, level, levels? }");
|
|
2490
|
+
}
|
|
2491
|
+
return Object.freeze(
|
|
2492
|
+
memberships.map(
|
|
2493
|
+
(each) => membershipRead(each, () => "resolver({ reads: { memberships } })")
|
|
2494
|
+
)
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2497
|
+
function checkReads(reads) {
|
|
2498
|
+
if (reads === void 0) {
|
|
2499
|
+
return void 0;
|
|
2500
|
+
}
|
|
2501
|
+
if (reads === "none") {
|
|
2502
|
+
return noReads();
|
|
2503
|
+
}
|
|
2504
|
+
if (typeof reads !== "object" || reads === null || Array.isArray(reads)) {
|
|
2505
|
+
readsProblem('reads must be { columns?, memberships? } or "none"');
|
|
2506
|
+
}
|
|
2507
|
+
const given = reads;
|
|
2508
|
+
const unknownKey = Object.keys(given).find((key) => !READ_KEYS.includes(key));
|
|
2509
|
+
if (unknownKey !== void 0) {
|
|
2510
|
+
readsProblem(`reads has an unknown key "${unknownKey}"; the keys are columns and memberships`);
|
|
2511
|
+
}
|
|
2512
|
+
const columns = checkColumns(given.columns);
|
|
2513
|
+
const memberships = checkMemberships(given.memberships);
|
|
2514
|
+
if (columns.length === 0 && memberships.length === 0) {
|
|
2515
|
+
readsProblem('name a column or a membership table, or pass "none"');
|
|
2516
|
+
}
|
|
2517
|
+
return { columns, memberships, inherits: [], storage: true };
|
|
2518
|
+
}
|
|
2519
|
+
function levelIn(levels, id) {
|
|
2520
|
+
let level;
|
|
2521
|
+
if (levels instanceof Map) {
|
|
2522
|
+
level = levels.get(id);
|
|
2523
|
+
} else if (Object.hasOwn(levels, id)) {
|
|
2524
|
+
level = levels[id];
|
|
2525
|
+
}
|
|
2526
|
+
return isAccessLevel(level) ? level : null;
|
|
2527
|
+
}
|
|
2528
|
+
function resolver(options) {
|
|
2529
|
+
const valid = typeof options === "object" && options !== null && typeof options.levelsFor === "function" && (options.where === void 0 || typeof options.where === "function");
|
|
2530
|
+
if (!valid) {
|
|
2531
|
+
throw new TypeError(
|
|
2532
|
+
"resolver({ levelsFor, where?, reads? }): levelsFor and where must be functions"
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
const { levelsFor, where: where2 } = options;
|
|
2536
|
+
const reads = checkReads(options.reads);
|
|
2537
|
+
const policy = definePolicy({
|
|
2538
|
+
kind: "resolver",
|
|
2539
|
+
reads: reads ?? noReads(),
|
|
2540
|
+
async levelsFor(principal, ids, tools) {
|
|
2541
|
+
const levels = await levelsFor(principal, ids, tools);
|
|
2542
|
+
if (typeof levels !== "object" || levels === null) {
|
|
2543
|
+
return levelsById(ids, () => null);
|
|
2544
|
+
}
|
|
2545
|
+
return levelsById(ids, (id) => levelIn(levels, id));
|
|
2546
|
+
},
|
|
2547
|
+
async accessWhere(principal, level, tools) {
|
|
2548
|
+
if (where2 === void 0) {
|
|
2549
|
+
return "none";
|
|
2550
|
+
}
|
|
2551
|
+
const filter = await where2(principal, level, tools);
|
|
2552
|
+
const isFilter = typeof filter === "object" && filter !== null && !Array.isArray(filter);
|
|
2553
|
+
return isFilter ? filter : "none";
|
|
2554
|
+
}
|
|
2555
|
+
});
|
|
2556
|
+
if (reads === void 0) {
|
|
2557
|
+
UNDECLARED.add(policy);
|
|
2558
|
+
}
|
|
2559
|
+
return policy;
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2339
2562
|
// src/server/collections/scopes.ts
|
|
2340
2563
|
function serviceOf2(anchor) {
|
|
2341
2564
|
return anchor.slice(0, anchor.indexOf("\0"));
|
|
@@ -4211,9 +4434,9 @@ async function nullableColumns(storage, collection, values) {
|
|
|
4211
4434
|
return nullable;
|
|
4212
4435
|
}
|
|
4213
4436
|
async function readRows4(storage, collection, read) {
|
|
4214
|
-
const { members, values, nullable, limit } = read;
|
|
4437
|
+
const { members: members2, values, nullable, limit } = read;
|
|
4215
4438
|
const { order } = collection;
|
|
4216
|
-
const where2 = values === void 0 ?
|
|
4439
|
+
const where2 = values === void 0 ? members2 : { AND: [members2, afterCursor(order, values, nullable)] };
|
|
4217
4440
|
const [rows, total] = await Promise.allSettled([
|
|
4218
4441
|
storage.findMany(collection.model, {
|
|
4219
4442
|
where: where2,
|
|
@@ -4224,7 +4447,7 @@ async function readRows4(storage, collection, read) {
|
|
|
4224
4447
|
orderBy: orderByOf(order, nullable),
|
|
4225
4448
|
take: limit + 1
|
|
4226
4449
|
}),
|
|
4227
|
-
storage.count(collection.model, { where:
|
|
4450
|
+
storage.count(collection.model, { where: members2 })
|
|
4228
4451
|
]);
|
|
4229
4452
|
if (total.status === "rejected") {
|
|
4230
4453
|
throw total.reason;
|
|
@@ -4244,15 +4467,15 @@ async function readPage(storage, collection, request, rev) {
|
|
|
4244
4467
|
const { order } = collection;
|
|
4245
4468
|
const values = request.cursor === void 0 ? void 0 : decodeCursor(order, request.cursor);
|
|
4246
4469
|
const nullable = await nullableColumns(storage, collection, values);
|
|
4247
|
-
const
|
|
4470
|
+
const members2 = await membersWhere(storage, collection, request.scope);
|
|
4248
4471
|
const page = { ok: true, rev, limit, ...clamped ? { clamped: true } : {} };
|
|
4249
4472
|
const indexed = values === void 0 && collection.index !== void 0;
|
|
4250
|
-
if (
|
|
4473
|
+
if (members2 === void 0) {
|
|
4251
4474
|
return { ...page, items: [], total: 0, cursor: null, ...indexed ? { index: [] } : {} };
|
|
4252
4475
|
}
|
|
4253
4476
|
const [{ rows, total }, index] = await Promise.all([
|
|
4254
|
-
readRows4(storage, collection, { members, values, nullable, limit }),
|
|
4255
|
-
indexed ? readIndex(storage, collection,
|
|
4477
|
+
readRows4(storage, collection, { members: members2, values, nullable, limit }),
|
|
4478
|
+
indexed ? readIndex(storage, collection, members2, nullable, rev) : void 0
|
|
4256
4479
|
]);
|
|
4257
4480
|
const shown = rows.slice(0, limit);
|
|
4258
4481
|
const last = shown.at(-1);
|
|
@@ -6477,11 +6700,41 @@ function createPolicyEngine(options) {
|
|
|
6477
6700
|
});
|
|
6478
6701
|
}
|
|
6479
6702
|
|
|
6703
|
+
// src/server/access/resolverReads.ts
|
|
6704
|
+
function hasUndeclaredResolver(policy) {
|
|
6705
|
+
return declaresNoReads(policy) || (anyOfPolicies(policy)?.some(hasUndeclaredResolver) ?? false);
|
|
6706
|
+
}
|
|
6707
|
+
function resolverReadsWarnings(service) {
|
|
6708
|
+
const { access, model } = service;
|
|
6709
|
+
if (access === void 0 || model === void 0 || !hasUndeclaredResolver(access)) {
|
|
6710
|
+
return [];
|
|
6711
|
+
}
|
|
6712
|
+
return [
|
|
6713
|
+
{
|
|
6714
|
+
kind: "resolver-without-reads",
|
|
6715
|
+
service: service.name,
|
|
6716
|
+
message: `${service.name}: its access policy uses a resolver that declares no reads, so no tracked write re-checks the levels it gives: a live row, collection scope or change topic it authorized stays open after the user loses access. Declare what the levels depend on, resolver({ levelsFor, reads: { columns, memberships } }) (columns of the ${model} model, membership tables as members() takes them), or reads: "none" when nothing a tracked write changes can change them`,
|
|
6717
|
+
meta: { model }
|
|
6718
|
+
}
|
|
6719
|
+
];
|
|
6720
|
+
}
|
|
6721
|
+
function warnResolverReads(registry, warnings) {
|
|
6722
|
+
if (!warnings.enabled) {
|
|
6723
|
+
return;
|
|
6724
|
+
}
|
|
6725
|
+
for (const service of registry.services.values()) {
|
|
6726
|
+
for (const warning of resolverReadsWarnings(service)) {
|
|
6727
|
+
warnings.warn(warning);
|
|
6728
|
+
}
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
|
|
6480
6732
|
// src/server/pipeline/accessSettings.ts
|
|
6481
6733
|
function isAccessEngine(value) {
|
|
6482
6734
|
return typeof value === "object" && value !== null && typeof value.authorize === "function";
|
|
6483
6735
|
}
|
|
6484
|
-
function resolveAccess2(access, registry, storage,
|
|
6736
|
+
function resolveAccess2(access, registry, storage, report) {
|
|
6737
|
+
const { logger, warnings } = report;
|
|
6485
6738
|
if (access !== void 0 && (typeof access !== "object" || access === null)) {
|
|
6486
6739
|
throw new TypeError("createDispatcher: access must be an access engine or { cacheMs }");
|
|
6487
6740
|
}
|
|
@@ -6493,6 +6746,7 @@ function resolveAccess2(access, registry, storage, logger) {
|
|
|
6493
6746
|
logger,
|
|
6494
6747
|
cacheMs: accessOptions.cacheMs
|
|
6495
6748
|
});
|
|
6749
|
+
warnResolverReads(registry, warnings);
|
|
6496
6750
|
return { access: engine ?? createBasicAccessEngine({ rows: policies.rows }), policies };
|
|
6497
6751
|
}
|
|
6498
6752
|
|
|
@@ -6626,7 +6880,10 @@ function resolveSettings(options, registry, db, accessSinks = []) {
|
|
|
6626
6880
|
const warnings = createDevWarnings({ logger, development, strict: strictWarningsOf(options) });
|
|
6627
6881
|
warnTieredOutputs(registry, warnings);
|
|
6628
6882
|
const storage = storageFor(options, db);
|
|
6629
|
-
const { access, policies } = resolveAccess2(options.access, registry, storage,
|
|
6883
|
+
const { access, policies } = resolveAccess2(options.access, registry, storage, {
|
|
6884
|
+
logger,
|
|
6885
|
+
warnings
|
|
6886
|
+
});
|
|
6630
6887
|
const outputValidation = options.outputValidation ?? development;
|
|
6631
6888
|
const live = createLive({
|
|
6632
6889
|
registry,
|
|
@@ -8806,7 +9063,6 @@ export {
|
|
|
8806
9063
|
isAccessPolicy,
|
|
8807
9064
|
checkName,
|
|
8808
9065
|
levelsById,
|
|
8809
|
-
idFilter,
|
|
8810
9066
|
encodeCursor,
|
|
8811
9067
|
decodeCursor,
|
|
8812
9068
|
orderByOf,
|
|
@@ -8823,6 +9079,11 @@ export {
|
|
|
8823
9079
|
STREAM_MAX_SCOPES,
|
|
8824
9080
|
MAX_STREAMS_PER_SOCKET,
|
|
8825
9081
|
createBasicAccessEngine,
|
|
9082
|
+
anyOfPolicies,
|
|
9083
|
+
anyOf,
|
|
9084
|
+
membershipOf,
|
|
9085
|
+
members,
|
|
9086
|
+
resolver,
|
|
8826
9087
|
DEFAULT_LIMITS,
|
|
8827
9088
|
MAX_TIMEOUT_MS,
|
|
8828
9089
|
createDispatcher,
|
|
@@ -8838,4 +9099,4 @@ export {
|
|
|
8838
9099
|
watchAdapterClients,
|
|
8839
9100
|
createServer
|
|
8840
9101
|
};
|
|
8841
|
-
//# sourceMappingURL=chunk-
|
|
9102
|
+
//# sourceMappingURL=chunk-NS5PUHIZ.js.map
|