@pthm/melange 0.6.7 → 0.7.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.
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Bulk permission check API for Melange TypeScript client.
3
+ *
4
+ * This module provides BulkCheckBuilder for batching multiple permission checks
5
+ * into a single SQL call via check_permission_bulk, with deduplication and caching.
6
+ */
7
+ import type { MelangeObject, Relation, Decision } from './types.js';
8
+ import type { Queryable } from './database.js';
9
+ import type { Cache } from './cache.js';
10
+ import { BulkCheckDeniedError } from './errors.js';
11
+ /**
12
+ * Maximum number of checks allowed in a single bulk operation.
13
+ * Prevents accidental resource exhaustion from unbounded batches.
14
+ */
15
+ export declare const MAX_BULK_CHECK_SIZE = 10000;
16
+ /** @internal Internals exposed by Checker for BulkCheckBuilder. */
17
+ export interface CheckerInternals {
18
+ db: Queryable;
19
+ cache: Cache;
20
+ decision?: Decision;
21
+ shouldValidate: boolean;
22
+ cacheKey(subject: MelangeObject, relation: Relation, object: MelangeObject): string;
23
+ }
24
+ /**
25
+ * BulkCheckBuilder accumulates permission checks and executes them in a single
26
+ * SQL call via check_permission_bulk.
27
+ *
28
+ * Use `checker.newBulkCheck()` to create one.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const results = await checker.newBulkCheck()
33
+ * .add(user, 'can_read', repo1)
34
+ * .add(user, 'can_read', repo2)
35
+ * .add(user, 'can_write', repo1)
36
+ * .execute();
37
+ *
38
+ * if (results.all()) {
39
+ * // All checks passed
40
+ * }
41
+ * ```
42
+ */
43
+ export declare class BulkCheckBuilder {
44
+ private readonly internals;
45
+ private readonly requests;
46
+ private readonly ids;
47
+ /** @internal Use `checker.newBulkCheck()` instead. */
48
+ constructor(internals: CheckerInternals);
49
+ /**
50
+ * Add a permission check with an auto-generated ID (string index).
51
+ * Returns the builder for chaining.
52
+ */
53
+ add(subject: MelangeObject, relation: Relation, object: MelangeObject): this;
54
+ /**
55
+ * Add a permission check with a caller-supplied ID.
56
+ * The ID must be non-empty and unique within the batch.
57
+ *
58
+ * @throws {Error} If id is empty or already used
59
+ */
60
+ addWithId(id: string, subject: MelangeObject, relation: Relation, object: MelangeObject): this;
61
+ /**
62
+ * Add checks for one subject+relation across multiple objects.
63
+ * Each check gets an auto-generated ID.
64
+ */
65
+ addMany(subject: MelangeObject, relation: Relation, ...objects: MelangeObject[]): this;
66
+ /**
67
+ * Execute all accumulated checks in a single SQL call.
68
+ * Results honour decision overrides, caching, and deduplication.
69
+ */
70
+ execute(): Promise<BulkCheckResults>;
71
+ private buildAllDecision;
72
+ }
73
+ /**
74
+ * BulkCheckResult holds the outcome of a single check within a bulk batch.
75
+ */
76
+ export declare class BulkCheckResult {
77
+ readonly id: string;
78
+ readonly index: number;
79
+ readonly subject: MelangeObject;
80
+ readonly relation: Relation;
81
+ readonly object: MelangeObject;
82
+ readonly allowed: boolean;
83
+ readonly error?: Error;
84
+ /** @internal */
85
+ constructor(id: string, index: number, subject: MelangeObject, relation: Relation, object: MelangeObject, allowed: boolean, error?: Error);
86
+ /** True if the permission was granted and no error occurred. */
87
+ isAllowed(): boolean;
88
+ }
89
+ /**
90
+ * BulkCheckResults holds the outcomes of a bulk permission check.
91
+ * Results are in the same order as the original requests.
92
+ */
93
+ export declare class BulkCheckResults {
94
+ private readonly _results;
95
+ private readonly _byId;
96
+ /** @internal */
97
+ constructor(results: BulkCheckResult[], byId: Map<string, BulkCheckResult>);
98
+ /** Number of results. */
99
+ get length(): number;
100
+ /** Get result at the given index. Throws if out of range. */
101
+ get(index: number): BulkCheckResult;
102
+ /** Get result by its ID, or undefined if not found. */
103
+ getById(id: string): BulkCheckResult | undefined;
104
+ /** True if every check was allowed (false for empty batch). */
105
+ all(): boolean;
106
+ /** True if at least one check was allowed. */
107
+ any(): boolean;
108
+ /** True if no check was allowed (true for empty batch). */
109
+ none(): boolean;
110
+ /** All results in request order. */
111
+ results(): BulkCheckResult[];
112
+ /** Only the results where the check was allowed. */
113
+ allowed(): BulkCheckResult[];
114
+ /** Only the results where the check was denied or errored. */
115
+ denied(): BulkCheckResult[];
116
+ /**
117
+ * Returns null if all checks were allowed, or a BulkCheckDeniedError
118
+ * describing the first denied check.
119
+ *
120
+ * For empty batches, returns null (no denials).
121
+ */
122
+ allOrError(): BulkCheckDeniedError | null;
123
+ }
124
+ //# sourceMappingURL=bulk-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-check.d.ts","sourceRoot":"","sources":["../src/bulk-check.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAgB,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGjE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAUzC,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;CACrF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;IACnD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IAEzC,sDAAsD;gBAC1C,SAAS,EAAE,gBAAgB;IAIvC;;;OAGG;IACH,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI;IAO5E;;;;;OAKG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI;IAY9F;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAOtF;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAoH1C,OAAO,CAAC,gBAAgB;CAazB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IAEvB,gBAAgB;gBAEd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,OAAO,EAChB,KAAK,CAAC,EAAE,KAAK;IAWf,gEAAgE;IAChE,SAAS,IAAI,OAAO;CAGrB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IAErD,gBAAgB;gBACJ,OAAO,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC;IAK1E,yBAAyB;IACzB,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,6DAA6D;IAC7D,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe;IAOnC,uDAAuD;IACvD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIhD,+DAA+D;IAC/D,GAAG,IAAI,OAAO;IAKd,8CAA8C;IAC9C,GAAG,IAAI,OAAO;IAId,2DAA2D;IAC3D,IAAI,IAAI,OAAO;IAIf,oCAAoC;IACpC,OAAO,IAAI,eAAe,EAAE;IAI5B,oDAAoD;IACpD,OAAO,IAAI,eAAe,EAAE;IAI5B,8DAA8D;IAC9D,MAAM,IAAI,eAAe,EAAE;IAI3B;;;;;OAKG;IACH,UAAU,IAAI,oBAAoB,GAAG,IAAI;CAe1C"}
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Bulk permission check API for Melange TypeScript client.
3
+ *
4
+ * This module provides BulkCheckBuilder for batching multiple permission checks
5
+ * into a single SQL call via check_permission_bulk, with deduplication and caching.
6
+ */
7
+ import { MelangeError, BulkCheckDeniedError } from './errors.js';
8
+ import { validateObject, validateRelation } from './validator.js';
9
+ /**
10
+ * Maximum number of checks allowed in a single bulk operation.
11
+ * Prevents accidental resource exhaustion from unbounded batches.
12
+ */
13
+ export const MAX_BULK_CHECK_SIZE = 10000;
14
+ /**
15
+ * BulkCheckBuilder accumulates permission checks and executes them in a single
16
+ * SQL call via check_permission_bulk.
17
+ *
18
+ * Use `checker.newBulkCheck()` to create one.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const results = await checker.newBulkCheck()
23
+ * .add(user, 'can_read', repo1)
24
+ * .add(user, 'can_read', repo2)
25
+ * .add(user, 'can_write', repo1)
26
+ * .execute();
27
+ *
28
+ * if (results.all()) {
29
+ * // All checks passed
30
+ * }
31
+ * ```
32
+ */
33
+ export class BulkCheckBuilder {
34
+ internals;
35
+ requests = [];
36
+ ids = new Set();
37
+ /** @internal Use `checker.newBulkCheck()` instead. */
38
+ constructor(internals) {
39
+ this.internals = internals;
40
+ }
41
+ /**
42
+ * Add a permission check with an auto-generated ID (string index).
43
+ * Returns the builder for chaining.
44
+ */
45
+ add(subject, relation, object) {
46
+ const id = String(this.requests.length);
47
+ this.ids.add(id);
48
+ this.requests.push({ id, subject, relation, object });
49
+ return this;
50
+ }
51
+ /**
52
+ * Add a permission check with a caller-supplied ID.
53
+ * The ID must be non-empty and unique within the batch.
54
+ *
55
+ * @throws {Error} If id is empty or already used
56
+ */
57
+ addWithId(id, subject, relation, object) {
58
+ if (!id) {
59
+ throw new Error('melange: BulkCheckBuilder.addWithId: id must not be empty');
60
+ }
61
+ if (this.ids.has(id)) {
62
+ throw new Error(`melange: BulkCheckBuilder.addWithId: duplicate id "${id}"`);
63
+ }
64
+ this.ids.add(id);
65
+ this.requests.push({ id, subject, relation, object });
66
+ return this;
67
+ }
68
+ /**
69
+ * Add checks for one subject+relation across multiple objects.
70
+ * Each check gets an auto-generated ID.
71
+ */
72
+ addMany(subject, relation, ...objects) {
73
+ for (const obj of objects) {
74
+ this.add(subject, relation, obj);
75
+ }
76
+ return this;
77
+ }
78
+ /**
79
+ * Execute all accumulated checks in a single SQL call.
80
+ * Results honour decision overrides, caching, and deduplication.
81
+ */
82
+ async execute() {
83
+ const { db, cache, decision, shouldValidate, cacheKey } = this.internals;
84
+ // 1. Decision override -- return all-same results without DB call.
85
+ if (decision) {
86
+ return this.buildAllDecision(decision.allowed);
87
+ }
88
+ // 2. Empty batch.
89
+ if (this.requests.length === 0) {
90
+ return new BulkCheckResults([], new Map());
91
+ }
92
+ // 3. Batch size guard.
93
+ if (this.requests.length > MAX_BULK_CHECK_SIZE) {
94
+ throw new MelangeError(`bulk check size ${this.requests.length} exceeds maximum ${MAX_BULK_CHECK_SIZE}`);
95
+ }
96
+ // 4. Validation.
97
+ if (shouldValidate) {
98
+ for (const r of this.requests) {
99
+ validateObject(r.subject, 'subject');
100
+ validateRelation(r.relation);
101
+ validateObject(r.object, 'object');
102
+ }
103
+ }
104
+ // 5. Deduplication -- map unique checks to original request indices.
105
+ // The cacheKey format doubles as the dedup key since both encode
106
+ // the same (subject, relation, object) triple.
107
+ const dedup = new Map();
108
+ for (let i = 0; i < this.requests.length; i++) {
109
+ const r = this.requests[i];
110
+ const key = cacheKey(r.subject, r.relation, r.object);
111
+ const existing = dedup.get(key);
112
+ if (existing) {
113
+ existing.push(i);
114
+ }
115
+ else {
116
+ dedup.set(key, [i]);
117
+ }
118
+ }
119
+ // 6. Cache lookup -- partition unique keys into cached and uncached.
120
+ const outcomes = new Map();
121
+ const uncachedKeys = [];
122
+ for (const [key, indices] of dedup) {
123
+ const r = this.requests[indices[0]];
124
+ const cached = await cache.get(cacheKey(r.subject, r.relation, r.object));
125
+ if (cached !== undefined) {
126
+ outcomes.set(key, cached);
127
+ }
128
+ else {
129
+ uncachedKeys.push(key);
130
+ }
131
+ }
132
+ // 7. SQL call for uncached checks.
133
+ if (uncachedKeys.length > 0) {
134
+ const subjectTypes = [];
135
+ const subjectIds = [];
136
+ const relations = [];
137
+ const objectTypes = [];
138
+ const objectIds = [];
139
+ for (const key of uncachedKeys) {
140
+ const r = this.requests[dedup.get(key)[0]];
141
+ subjectTypes.push(r.subject.type);
142
+ subjectIds.push(r.subject.id);
143
+ relations.push(r.relation);
144
+ objectTypes.push(r.object.type);
145
+ objectIds.push(r.object.id);
146
+ }
147
+ const result = await db.query('SELECT idx, allowed FROM check_permission_bulk($1, $2, $3, $4, $5)', [subjectTypes, subjectIds, relations, objectTypes, objectIds]);
148
+ for (const row of result.rows) {
149
+ // SQL idx is 1-based (WITH ORDINALITY), convert to 0-based.
150
+ const zeroIdx = row.idx - 1;
151
+ if (zeroIdx < 0 || zeroIdx >= uncachedKeys.length) {
152
+ continue;
153
+ }
154
+ outcomes.set(uncachedKeys[zeroIdx], { allowed: row.allowed === 1 });
155
+ }
156
+ // 8. Cache store for DB results.
157
+ for (const key of uncachedKeys) {
158
+ const outcome = outcomes.get(key);
159
+ if (outcome) {
160
+ await cache.set(key, outcome);
161
+ }
162
+ }
163
+ }
164
+ // 9. Result assembly -- fan out deduplicated results to all original indices.
165
+ const results = new Array(this.requests.length);
166
+ const byId = new Map();
167
+ for (const [key, indices] of dedup) {
168
+ const allowed = outcomes.get(key)?.allowed ?? false;
169
+ for (const origIdx of indices) {
170
+ const r = this.requests[origIdx];
171
+ const result = new BulkCheckResult(r.id, origIdx, r.subject, r.relation, r.object, allowed);
172
+ results[origIdx] = result;
173
+ byId.set(r.id, result);
174
+ }
175
+ }
176
+ return new BulkCheckResults(results, byId);
177
+ }
178
+ buildAllDecision(allowed) {
179
+ const results = [];
180
+ const byId = new Map();
181
+ for (let i = 0; i < this.requests.length; i++) {
182
+ const r = this.requests[i];
183
+ const result = new BulkCheckResult(r.id, i, r.subject, r.relation, r.object, allowed);
184
+ results.push(result);
185
+ byId.set(r.id, result);
186
+ }
187
+ return new BulkCheckResults(results, byId);
188
+ }
189
+ }
190
+ /**
191
+ * BulkCheckResult holds the outcome of a single check within a bulk batch.
192
+ */
193
+ export class BulkCheckResult {
194
+ id;
195
+ index;
196
+ subject;
197
+ relation;
198
+ object;
199
+ allowed;
200
+ error;
201
+ /** @internal */
202
+ constructor(id, index, subject, relation, object, allowed, error) {
203
+ this.id = id;
204
+ this.index = index;
205
+ this.subject = subject;
206
+ this.relation = relation;
207
+ this.object = object;
208
+ this.allowed = allowed;
209
+ this.error = error;
210
+ }
211
+ /** True if the permission was granted and no error occurred. */
212
+ isAllowed() {
213
+ return this.allowed && !this.error;
214
+ }
215
+ }
216
+ /**
217
+ * BulkCheckResults holds the outcomes of a bulk permission check.
218
+ * Results are in the same order as the original requests.
219
+ */
220
+ export class BulkCheckResults {
221
+ _results;
222
+ _byId;
223
+ /** @internal */
224
+ constructor(results, byId) {
225
+ this._results = results;
226
+ this._byId = byId;
227
+ }
228
+ /** Number of results. */
229
+ get length() {
230
+ return this._results.length;
231
+ }
232
+ /** Get result at the given index. Throws if out of range. */
233
+ get(index) {
234
+ if (index < 0 || index >= this._results.length) {
235
+ throw new RangeError(`index ${index} out of range [0, ${this._results.length})`);
236
+ }
237
+ return this._results[index];
238
+ }
239
+ /** Get result by its ID, or undefined if not found. */
240
+ getById(id) {
241
+ return this._byId.get(id);
242
+ }
243
+ /** True if every check was allowed (false for empty batch). */
244
+ all() {
245
+ if (this._results.length === 0)
246
+ return false;
247
+ return this._results.every((r) => r.isAllowed());
248
+ }
249
+ /** True if at least one check was allowed. */
250
+ any() {
251
+ return this._results.some((r) => r.isAllowed());
252
+ }
253
+ /** True if no check was allowed (true for empty batch). */
254
+ none() {
255
+ return !this.any();
256
+ }
257
+ /** All results in request order. */
258
+ results() {
259
+ return [...this._results];
260
+ }
261
+ /** Only the results where the check was allowed. */
262
+ allowed() {
263
+ return this._results.filter((r) => r.isAllowed());
264
+ }
265
+ /** Only the results where the check was denied or errored. */
266
+ denied() {
267
+ return this._results.filter((r) => !r.isAllowed());
268
+ }
269
+ /**
270
+ * Returns null if all checks were allowed, or a BulkCheckDeniedError
271
+ * describing the first denied check.
272
+ *
273
+ * For empty batches, returns null (no denials).
274
+ */
275
+ allOrError() {
276
+ let first;
277
+ let deniedCount = 0;
278
+ for (const r of this._results) {
279
+ if (!r.isAllowed()) {
280
+ if (!first)
281
+ first = r;
282
+ deniedCount++;
283
+ }
284
+ }
285
+ if (!first)
286
+ return null;
287
+ return new BulkCheckDeniedError(first.subject, first.relation, first.object, first.index, deniedCount);
288
+ }
289
+ }
290
+ //# sourceMappingURL=bulk-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-check.js","sourceRoot":"","sources":["../src/bulk-check.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAmBzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,gBAAgB;IACV,SAAS,CAAmB;IAC5B,QAAQ,GAAuB,EAAE,CAAC;IAClC,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAEzC,sDAAsD;IACtD,YAAY,SAA2B;QACrC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,GAAG,CAAC,OAAsB,EAAE,QAAkB,EAAE,MAAqB;QACnE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,EAAU,EAAE,OAAsB,EAAE,QAAkB,EAAE,MAAqB;QACrF,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,sDAAsD,EAAE,GAAG,CAAC,CAAC;QAC/E,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,OAAsB,EAAE,QAAkB,EAAE,GAAG,OAAwB;QAC7E,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAEzE,mEAAmE;QACnE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC;QAED,kBAAkB;QAClB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,IAAI,gBAAgB,CAAC,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,uBAAuB;QACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;YAC/C,MAAM,IAAI,YAAY,CACpB,mBAAmB,IAAI,CAAC,QAAQ,CAAC,MAAM,oBAAoB,mBAAmB,EAAE,CACjF,CAAC;QACJ,CAAC;QAED,iBAAiB;QACjB,IAAI,cAAc,EAAE,CAAC;YACnB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC9B,cAAc,CAAC,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;gBACrC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAC7B,cAAc,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,qEAAqE;QACrE,oEAAoE;QACpE,kDAAkD;QAClD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;YACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QAED,qEAAqE;QACrE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC7C,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YAC1E,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,mCAAmC;QACnC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAa,EAAE,CAAC;YAClC,MAAM,UAAU,GAAa,EAAE,CAAC;YAChC,MAAM,SAAS,GAAa,EAAE,CAAC;YAC/B,MAAM,WAAW,GAAa,EAAE,CAAC;YACjC,MAAM,SAAS,GAAa,EAAE,CAAC;YAE/B,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;gBAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5C,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAClC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC9B,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAC3B,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAChC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC9B,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,oEAAoE,EACpE,CAAC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,CAAC,CAC9D,CAAC;YAEF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC9B,4DAA4D;gBAC5D,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;gBAC5B,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;oBAClD,SAAS;gBACX,CAAC;gBACD,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC;YACtE,CAAC;YAED,iCAAiC;YACjC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QAED,8EAA8E;QAC9E,MAAM,OAAO,GAAsB,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,GAAG,EAA2B,CAAC;QAEhD,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC;YAEpD,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACjC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC5F,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;gBAC1B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAEO,gBAAgB,CAAC,OAAgB;QACvC,MAAM,OAAO,GAAsB,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,GAAG,EAA2B,CAAC;QAEhD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACtF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACzB,CAAC;QAED,OAAO,IAAI,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAe;IACjB,EAAE,CAAS;IACX,KAAK,CAAS;IACd,OAAO,CAAgB;IACvB,QAAQ,CAAW;IACnB,MAAM,CAAgB;IACtB,OAAO,CAAU;IACjB,KAAK,CAAS;IAEvB,gBAAgB;IAChB,YACE,EAAU,EACV,KAAa,EACb,OAAsB,EACtB,QAAkB,EAClB,MAAqB,EACrB,OAAgB,EAChB,KAAa;QAEb,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,gEAAgE;IAChE,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IACrC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACV,QAAQ,CAAoB;IAC5B,KAAK,CAA+B;IAErD,gBAAgB;IAChB,YAAY,OAA0B,EAAE,IAAkC;QACxE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,yBAAyB;IACzB,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,6DAA6D;IAC7D,GAAG,CAAC,KAAa;QACf,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC/C,MAAM,IAAI,UAAU,CAAC,SAAS,KAAK,qBAAqB,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,uDAAuD;IACvD,OAAO,CAAC,EAAU;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,+DAA+D;IAC/D,GAAG;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,8CAA8C;IAC9C,GAAG;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,2DAA2D;IAC3D,IAAI;QACF,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,oCAAoC;IACpC,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,oDAAoD;IACpD,OAAO;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,8DAA8D;IAC9D,MAAM;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACR,IAAI,KAAkC,CAAC;QACvC,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;gBACnB,IAAI,CAAC,KAAK;oBAAE,KAAK,GAAG,CAAC,CAAC;gBACtB,WAAW,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,OAAO,IAAI,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACzG,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Unit tests for BulkCheckBuilder, BulkCheckResult, and BulkCheckResults.
3
+ *
4
+ * All tests use decision overrides so no database is needed.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=bulk-check.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-check.test.d.ts","sourceRoot":"","sources":["../src/bulk-check.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Unit tests for BulkCheckBuilder, BulkCheckResult, and BulkCheckResults.
3
+ *
4
+ * All tests use decision overrides so no database is needed.
5
+ */
6
+ import { describe, test, expect } from 'vitest';
7
+ import { Checker, DecisionAllow, DecisionDeny } from './checker.js';
8
+ import { BulkCheckDeniedError, isBulkCheckDeniedError } from './errors.js';
9
+ import { MAX_BULK_CHECK_SIZE } from './bulk-check.js';
10
+ // Helpers — null db is fine because decision overrides bypass SQL.
11
+ const allowChecker = new Checker(null, { decision: DecisionAllow });
12
+ const denyChecker = new Checker(null, { decision: DecisionDeny });
13
+ const user = { type: 'user', id: '1' };
14
+ const repo = (id) => ({ type: 'repository', id });
15
+ describe('BulkCheckBuilder', () => {
16
+ test('add() assigns sequential auto-IDs', async () => {
17
+ const results = await allowChecker
18
+ .newBulkCheck()
19
+ .add(user, 'can_read', repo('1'))
20
+ .add(user, 'can_read', repo('2'))
21
+ .add(user, 'can_read', repo('3'))
22
+ .execute();
23
+ expect(results.length).toBe(3);
24
+ expect(results.get(0).id).toBe('0');
25
+ expect(results.get(1).id).toBe('1');
26
+ expect(results.get(2).id).toBe('2');
27
+ });
28
+ test('addWithId() stores custom IDs', async () => {
29
+ const results = await allowChecker
30
+ .newBulkCheck()
31
+ .addWithId('alpha', user, 'can_read', repo('10'))
32
+ .addWithId('beta', user, 'can_read', repo('20'))
33
+ .execute();
34
+ const alpha = results.getById('alpha');
35
+ expect(alpha).toBeDefined();
36
+ expect(alpha.object.id).toBe('10');
37
+ const beta = results.getById('beta');
38
+ expect(beta).toBeDefined();
39
+ expect(beta.object.id).toBe('20');
40
+ });
41
+ test('addWithId() throws on empty ID', () => {
42
+ const builder = allowChecker.newBulkCheck();
43
+ expect(() => builder.addWithId('', user, 'can_read', repo('1'))).toThrow('id must not be empty');
44
+ });
45
+ test('addWithId() throws on duplicate ID', () => {
46
+ const builder = allowChecker.newBulkCheck();
47
+ builder.addWithId('dup', user, 'can_read', repo('1'));
48
+ expect(() => builder.addWithId('dup', user, 'can_read', repo('2'))).toThrow('duplicate id "dup"');
49
+ });
50
+ test('addMany() adds multiple objects', async () => {
51
+ const results = await allowChecker
52
+ .newBulkCheck()
53
+ .addMany(user, 'can_read', repo('1'), repo('2'), repo('3'))
54
+ .execute();
55
+ expect(results.length).toBe(3);
56
+ expect(results.get(0).object.id).toBe('1');
57
+ expect(results.get(1).object.id).toBe('2');
58
+ expect(results.get(2).object.id).toBe('3');
59
+ });
60
+ test('chaining returns builder', () => {
61
+ const builder = allowChecker.newBulkCheck();
62
+ const result = builder
63
+ .add(user, 'can_read', repo('1'))
64
+ .addWithId('custom', user, 'can_read', repo('2'))
65
+ .addMany(user, 'can_read', repo('3'));
66
+ expect(result).toBe(builder);
67
+ });
68
+ });
69
+ describe('BulkCheckResults — DecisionAllow', () => {
70
+ test('all results allowed', async () => {
71
+ const results = await allowChecker
72
+ .newBulkCheck()
73
+ .add(user, 'can_read', repo('1'))
74
+ .add(user, 'can_read', repo('2'))
75
+ .add(user, 'can_read', repo('3'))
76
+ .execute();
77
+ expect(results.all()).toBe(true);
78
+ expect(results.none()).toBe(false);
79
+ expect(results.any()).toBe(true);
80
+ expect(results.allowed().length).toBe(3);
81
+ expect(results.denied().length).toBe(0);
82
+ });
83
+ });
84
+ describe('BulkCheckResults — DecisionDeny', () => {
85
+ test('all results denied', async () => {
86
+ const results = await denyChecker
87
+ .newBulkCheck()
88
+ .add(user, 'can_read', repo('1'))
89
+ .add(user, 'can_read', repo('2'))
90
+ .add(user, 'can_read', repo('3'))
91
+ .execute();
92
+ expect(results.all()).toBe(false);
93
+ expect(results.none()).toBe(true);
94
+ expect(results.any()).toBe(false);
95
+ expect(results.allowed().length).toBe(0);
96
+ expect(results.denied().length).toBe(3);
97
+ });
98
+ });
99
+ describe('BulkCheckResults — empty batch', () => {
100
+ test('empty batch returns correct aggregates', async () => {
101
+ const results = await allowChecker.newBulkCheck().execute();
102
+ expect(results.length).toBe(0);
103
+ expect(results.all()).toBe(false);
104
+ expect(results.none()).toBe(true);
105
+ expect(results.any()).toBe(false);
106
+ expect(results.allOrError()).toBeNull();
107
+ });
108
+ });
109
+ describe('BulkCheckResult accessors', () => {
110
+ test('result fields are correct', async () => {
111
+ const subject = { type: 'user', id: '42' };
112
+ const object = { type: 'repository', id: '99' };
113
+ const results = await allowChecker
114
+ .newBulkCheck()
115
+ .addWithId('check-1', subject, 'can_write', object)
116
+ .execute();
117
+ const r = results.get(0);
118
+ expect(r.id).toBe('check-1');
119
+ expect(r.index).toBe(0);
120
+ expect(r.subject.type).toBe('user');
121
+ expect(r.subject.id).toBe('42');
122
+ expect(r.relation).toBe('can_write');
123
+ expect(r.object.type).toBe('repository');
124
+ expect(r.object.id).toBe('99');
125
+ expect(r.allowed).toBe(true);
126
+ expect(r.error).toBeUndefined();
127
+ });
128
+ });
129
+ describe('BulkCheckResults.getById', () => {
130
+ test('returns undefined for unknown ID', async () => {
131
+ const results = await allowChecker
132
+ .newBulkCheck()
133
+ .add(user, 'can_read', repo('1'))
134
+ .execute();
135
+ expect(results.getById('nonexistent')).toBeUndefined();
136
+ });
137
+ });
138
+ describe('BulkCheckResults.get', () => {
139
+ test('throws on out-of-range index', async () => {
140
+ const results = await allowChecker
141
+ .newBulkCheck()
142
+ .add(user, 'can_read', repo('1'))
143
+ .execute();
144
+ expect(() => results.get(5)).toThrow(RangeError);
145
+ expect(() => results.get(-1)).toThrow(RangeError);
146
+ });
147
+ });
148
+ describe('BulkCheckResults.allOrError', () => {
149
+ test('returns null when all allowed', async () => {
150
+ const results = await allowChecker
151
+ .newBulkCheck()
152
+ .add(user, 'can_read', repo('1'))
153
+ .add(user, 'can_read', repo('2'))
154
+ .execute();
155
+ expect(results.allOrError()).toBeNull();
156
+ });
157
+ test('returns BulkCheckDeniedError when denied', async () => {
158
+ const results = await denyChecker
159
+ .newBulkCheck()
160
+ .add(user, 'can_read', repo('1'))
161
+ .add(user, 'can_read', repo('2'))
162
+ .add(user, 'can_read', repo('3'))
163
+ .execute();
164
+ const err = results.allOrError();
165
+ expect(err).toBeInstanceOf(BulkCheckDeniedError);
166
+ expect(err.total).toBe(3);
167
+ expect(err.index).toBe(0);
168
+ expect(err.subject.type).toBe('user');
169
+ expect(err.relation).toBe('can_read');
170
+ expect(isBulkCheckDeniedError(err)).toBe(true);
171
+ });
172
+ });
173
+ describe('allowed() and denied() filtering', () => {
174
+ test('results() returns all results in order', async () => {
175
+ const results = await allowChecker
176
+ .newBulkCheck()
177
+ .add(user, 'can_read', repo('1'))
178
+ .add(user, 'can_read', repo('2'))
179
+ .execute();
180
+ const all = results.results();
181
+ expect(all.length).toBe(2);
182
+ expect(all[0].object.id).toBe('1');
183
+ expect(all[1].object.id).toBe('2');
184
+ });
185
+ });
186
+ describe('MAX_BULK_CHECK_SIZE', () => {
187
+ test('equals 10000', () => {
188
+ expect(MAX_BULK_CHECK_SIZE).toBe(10000);
189
+ });
190
+ });
191
+ //# sourceMappingURL=bulk-check.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-check.test.js","sourceRoot":"","sources":["../src/bulk-check.test.ts"],"names":[],"mappings":"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"}
package/dist/checker.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  import type { MelangeObject, Relation, ObjectType, Decision, ContextualTuple, PageOptions, ListResult } from './types.js';
8
8
  import type { Queryable } from './database.js';
9
9
  import { Cache } from './cache.js';
10
+ import { BulkCheckBuilder } from './bulk-check.js';
10
11
  /**
11
12
  * CheckerOptions configures a Checker instance.
12
13
  */
@@ -197,6 +198,23 @@ export declare class Checker {
197
198
  * ```
198
199
  */
199
200
  checkWithContextualTuples(subject: MelangeObject, relation: Relation, object: MelangeObject, contextualTuples: ContextualTuple[]): Promise<Decision>;
201
+ /**
202
+ * Create a new BulkCheckBuilder for batching multiple permission checks
203
+ * into a single SQL call via check_permission_bulk.
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * const results = await checker.newBulkCheck()
208
+ * .add(user, 'can_read', repo1)
209
+ * .add(user, 'can_read', repo2)
210
+ * .execute();
211
+ *
212
+ * if (results.all()) {
213
+ * // All checks passed
214
+ * }
215
+ * ```
216
+ */
217
+ newBulkCheck(): BulkCheckBuilder;
200
218
  /**
201
219
  * Generate a cache key for a check operation.
202
220
  */
@@ -1 +1 @@
1
- {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,eAAe,EACf,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAa,MAAM,YAAY,CAAC;AAI9C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,QAA4B,CAAC;AACzD,eAAO,MAAM,YAAY,EAAE,QAA6B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C;;;;;OAKG;gBACS,EAAE,EAAE,SAAS,EAAE,OAAO,GAAE,cAAmB;IAQvD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,KAAK,CACT,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,gBAAgB,CAAC,EAAE,eAAe,EAAE,GACnC,OAAO,CAAC,QAAQ,CAAC;IA6CpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACG,WAAW,CACf,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAyB9B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,YAAY,CAChB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAyB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,yBAAyB,CAC7B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,gBAAgB,EAAE,eAAe,EAAE,GAClC,OAAO,CAAC,QAAQ,CAAC;IAIpB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
1
+ {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,eAAe,EACf,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAa,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,QAA4B,CAAC;AACzD,eAAO,MAAM,YAAY,EAAE,QAA6B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C;;;;;OAKG;gBACS,EAAE,EAAE,SAAS,EAAE,OAAO,GAAE,cAAmB;IAQvD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,KAAK,CACT,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,gBAAgB,CAAC,EAAE,eAAe,EAAE,GACnC,OAAO,CAAC,QAAQ,CAAC;IA6CpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACG,WAAW,CACf,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAyB9B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,YAAY,CAChB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAyB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,yBAAyB,CAC7B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,gBAAgB,EAAE,eAAe,EAAE,GAClC,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,IAAI,gBAAgB;IAUhC;;OAEG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
package/dist/checker.js CHANGED
@@ -7,6 +7,7 @@
7
7
  import { NoopCache } from './cache.js';
8
8
  import { validateObject, validateRelation } from './validator.js';
9
9
  import { MelangeError } from './errors.js';
10
+ import { BulkCheckBuilder } from './bulk-check.js';
10
11
  /**
11
12
  * Decision constants for testing.
12
13
  */
@@ -242,6 +243,31 @@ export class Checker {
242
243
  async checkWithContextualTuples(subject, relation, object, contextualTuples) {
243
244
  return this.check(subject, relation, object, contextualTuples);
244
245
  }
246
+ /**
247
+ * Create a new BulkCheckBuilder for batching multiple permission checks
248
+ * into a single SQL call via check_permission_bulk.
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * const results = await checker.newBulkCheck()
253
+ * .add(user, 'can_read', repo1)
254
+ * .add(user, 'can_read', repo2)
255
+ * .execute();
256
+ *
257
+ * if (results.all()) {
258
+ * // All checks passed
259
+ * }
260
+ * ```
261
+ */
262
+ newBulkCheck() {
263
+ return new BulkCheckBuilder({
264
+ db: this.db,
265
+ cache: this.cache,
266
+ decision: this.decision,
267
+ shouldValidate: this.validateRequest,
268
+ cacheKey: this.cacheKey.bind(this),
269
+ });
270
+ }
245
271
  /**
246
272
  * Generate a cache key for a check operation.
247
273
  */
@@ -1 +1 @@
1
- {"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EAAS,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkC3C;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACzD,MAAM,CAAC,MAAM,YAAY,GAAa,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,OAAO;IACD,EAAE,CAAY;IACd,KAAK,CAAQ;IACb,QAAQ,CAAY;IACpB,eAAe,CAAU;IACzB,eAAe,CAAU;IAE1C;;;;;OAKG;IACH,YAAY,EAAa,EAAE,UAA0B,EAAE;QACrD,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;QACvD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,KAAK,CAAC,KAAK,CACT,OAAsB,EACtB,QAAkB,EAClB,MAAqB,EACrB,gBAAoC;QAEpC,gCAAgC;QAChC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED,kBAAkB;QAClB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACnC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC3B,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,6CAA6C;QAC7C,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,2BAA2B;QAC3B,2EAA2E;QAC3E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC,wDAAwD,EACxD,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAC7D,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,YAAY,CAAC,mCAAmC,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAa,EAAE,OAAO,EAAE,CAAC;QAEvC,gDAAgD;QAChD,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC1D,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,KAAK,CAAC,WAAW,CACf,OAAsB,EACtB,QAAkB,EAClB,UAAsB,EACtB,OAAqB;QAErB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACnC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC3B,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,YAAY,CAAC,wBAAwB,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;QAE7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC,+DAA+D,EAC/D,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,CACvE,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YACvC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM;YAC5C,CAAC,CAAC,SAAS,CAAC;QAEd,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,KAAK,CAAC,YAAY,CAChB,WAAuB,EACvB,QAAkB,EAClB,MAAqB,EACrB,OAAqB;QAErB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,IAAI,YAAY,CAAC,yBAAyB,CAAC,CAAC;YACpD,CAAC;YACD,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC3B,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;QAE7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC,gEAAgE,EAChE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,CACtE,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YACvC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM;YAC5C,CAAC,CAAC,SAAS,CAAC;QAEd,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAK,CAAC,yBAAyB,CAC7B,OAAsB,EACtB,QAAkB,EAClB,MAAqB,EACrB,gBAAmC;QAEnC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACjE,CAAC;IAED;;OAEG;IACK,QAAQ,CAAC,OAAsB,EAAE,QAAkB,EAAE,MAAqB;QAChF,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,IAAI,QAAQ,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;IACjF,CAAC;CACF"}
1
+ {"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * This module provides structured error types for authorization operations,
5
5
  * making it easier to handle different failure modes.
6
6
  */
7
+ import type { MelangeObject, Relation } from './types.js';
7
8
  /**
8
9
  * MelangeError is the base error class for all melange errors.
9
10
  *
@@ -42,4 +43,20 @@ export declare class NotFoundError extends MelangeError {
42
43
  export declare class ValidationError extends MelangeError {
43
44
  constructor(message: string);
44
45
  }
46
+ /**
47
+ * BulkCheckDeniedError is thrown by BulkCheckResults.allOrError() when at least
48
+ * one check in the batch was denied. It carries the details of the first denied check.
49
+ */
50
+ export declare class BulkCheckDeniedError extends MelangeError {
51
+ readonly subject: MelangeObject;
52
+ readonly relation: Relation;
53
+ readonly object: MelangeObject;
54
+ readonly index: number;
55
+ readonly total: number;
56
+ constructor(subject: MelangeObject, relation: Relation, object: MelangeObject, index: number, total: number);
57
+ }
58
+ /**
59
+ * Type guard for BulkCheckDeniedError.
60
+ */
61
+ export declare function isBulkCheckDeniedError(err: unknown): err is BulkCheckDeniedError;
45
62
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAM5B;AAED;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,YAAY;gBACjC,OAAO,EAAE,MAAM;CAK5B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,OAAO,EAAE,MAAM;CAK5B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAM5B;AAED;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,YAAY;gBACjC,OAAO,EAAE,MAAM;CAK5B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,OAAO,EAAE,MAAM;CAK5B;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAGrB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM;CAahB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,oBAAoB,CAEhF"}
package/dist/errors.js CHANGED
@@ -55,4 +55,31 @@ export class ValidationError extends MelangeError {
55
55
  Object.setPrototypeOf(this, ValidationError.prototype);
56
56
  }
57
57
  }
58
+ /**
59
+ * BulkCheckDeniedError is thrown by BulkCheckResults.allOrError() when at least
60
+ * one check in the batch was denied. It carries the details of the first denied check.
61
+ */
62
+ export class BulkCheckDeniedError extends MelangeError {
63
+ subject;
64
+ relation;
65
+ object;
66
+ index;
67
+ total;
68
+ constructor(subject, relation, object, index, total) {
69
+ super(`permission denied: ${subject.type}:${subject.id} ${relation} ${object.type}:${object.id} (${total} checks denied, first at index ${index})`);
70
+ this.name = 'BulkCheckDeniedError';
71
+ this.subject = subject;
72
+ this.relation = relation;
73
+ this.object = object;
74
+ this.index = index;
75
+ this.total = total;
76
+ Object.setPrototypeOf(this, BulkCheckDeniedError.prototype);
77
+ }
78
+ }
79
+ /**
80
+ * Type guard for BulkCheckDeniedError.
81
+ */
82
+ export function isBulkCheckDeniedError(err) {
83
+ return err instanceof BulkCheckDeniedError;
84
+ }
58
85
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,gDAAgD;QAChD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,YAAY;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;CACF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;GAKG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,gDAAgD;QAChD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,YAAY;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;CACF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IAC3C,OAAO,CAAgB;IACvB,QAAQ,CAAW;IACnB,MAAM,CAAgB;IACtB,KAAK,CAAS;IACd,KAAK,CAAS;IAEvB,YACE,OAAsB,EACtB,QAAkB,EAClB,MAAqB,EACrB,KAAa,EACb,KAAa;QAEb,KAAK,CACH,sBAAsB,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,IAAI,QAAQ,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK,KAAK,kCAAkC,KAAK,GAAG,CAC7I,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC9D,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,OAAO,GAAG,YAAY,oBAAoB,CAAC;AAC7C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -10,7 +10,8 @@
10
10
  export { Checker, DecisionAllow, DecisionDeny } from './checker.js';
11
11
  export type { CheckerOptions } from './checker.js';
12
12
  export { Cache, NoopCache, MemoryCache } from './cache.js';
13
- export { MelangeError, NotFoundError, ValidationError } from './errors.js';
13
+ export { MelangeError, NotFoundError, ValidationError, BulkCheckDeniedError, isBulkCheckDeniedError } from './errors.js';
14
+ export { BulkCheckBuilder, BulkCheckResult, BulkCheckResults, MAX_BULK_CHECK_SIZE } from './bulk-check.js';
14
15
  export type { Queryable, QueryResult } from './database.js';
15
16
  export { validateObject, validateRelation } from './validator.js';
16
17
  export type { ObjectType, Relation, MelangeObject, Decision, CheckRequest, ContextualTuple, PageOptions, ListResult, } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EACV,UAAU,EACV,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACzH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3G,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EACV,UAAU,EACV,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -9,7 +9,8 @@
9
9
  */
10
10
  export { Checker, DecisionAllow, DecisionDeny } from './checker.js';
11
11
  export { NoopCache, MemoryCache } from './cache.js';
12
- export { MelangeError, NotFoundError, ValidationError } from './errors.js';
12
+ export { MelangeError, NotFoundError, ValidationError, BulkCheckDeniedError, isBulkCheckDeniedError } from './errors.js';
13
+ export { BulkCheckBuilder, BulkCheckResult, BulkCheckResults, MAX_BULK_CHECK_SIZE } from './bulk-check.js';
13
14
  export { validateObject, validateRelation } from './validator.js';
14
15
  // Re-export adapters for convenience
15
16
  export * from './adapters/index.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAS,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAYlE,qCAAqC;AACrC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAS,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACzH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3G,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAYlE,qCAAqC;AACrC,cAAc,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pthm/melange",
3
- "version": "0.6.7",
3
+ "version": "0.7.0",
4
4
  "description": "TypeScript client for Melange PostgreSQL authorization",
5
5
  "keywords": [
6
6
  "authorization",
@@ -34,6 +34,7 @@
34
34
  "@types/pg": "^8.11.0",
35
35
  "@vitest/coverage-v8": "^2.1.8",
36
36
  "pg": "^8.13.1",
37
+ "postgres": "^3.4.8",
37
38
  "typescript": "^5.0.0",
38
39
  "vitest": "^2.1.8"
39
40
  },
@@ -53,6 +54,7 @@
53
54
  "clean": "rm -rf dist",
54
55
  "typecheck": "tsc --noEmit",
55
56
  "test": "vitest run",
57
+ "test:unit": "vitest run src/",
56
58
  "test:watch": "vitest",
57
59
  "test:coverage": "vitest run --coverage"
58
60
  }