@diister/quick-permission 0.9.0-beta.5

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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +747 -0
  3. package/aggregation.ts +114 -0
  4. package/core/filtering.ts +70 -0
  5. package/core/matching.ts +82 -0
  6. package/core/merging.ts +143 -0
  7. package/dist/aggregation.d.ts +62 -0
  8. package/dist/aggregation.d.ts.map +1 -0
  9. package/dist/aggregation.js +97 -0
  10. package/dist/aggregation.js.map +1 -0
  11. package/dist/core/filtering.d.ts +35 -0
  12. package/dist/core/filtering.d.ts.map +1 -0
  13. package/dist/core/filtering.js +62 -0
  14. package/dist/core/filtering.js.map +1 -0
  15. package/dist/core/matching.d.ts +31 -0
  16. package/dist/core/matching.d.ts.map +1 -0
  17. package/dist/core/matching.js +75 -0
  18. package/dist/core/matching.js.map +1 -0
  19. package/dist/core/merging.d.ts +29 -0
  20. package/dist/core/merging.d.ts.map +1 -0
  21. package/dist/core/merging.js +124 -0
  22. package/dist/core/merging.js.map +1 -0
  23. package/dist/indirect-aggregation.d.ts +41 -0
  24. package/dist/indirect-aggregation.d.ts.map +1 -0
  25. package/dist/indirect-aggregation.js +185 -0
  26. package/dist/indirect-aggregation.js.map +1 -0
  27. package/dist/indirect-resource.d.ts +126 -0
  28. package/dist/indirect-resource.d.ts.map +1 -0
  29. package/dist/indirect-resource.js +109 -0
  30. package/dist/indirect-resource.js.map +1 -0
  31. package/dist/mod.d.ts +25 -0
  32. package/dist/mod.d.ts.map +1 -0
  33. package/dist/mod.js +25 -0
  34. package/dist/mod.js.map +1 -0
  35. package/dist/mongo-query.d.ts +38 -0
  36. package/dist/mongo-query.d.ts.map +1 -0
  37. package/dist/mongo-query.js +88 -0
  38. package/dist/mongo-query.js.map +1 -0
  39. package/dist/permission.d.ts +57 -0
  40. package/dist/permission.d.ts.map +1 -0
  41. package/dist/permission.js +60 -0
  42. package/dist/permission.js.map +1 -0
  43. package/dist/resource.d.ts +48 -0
  44. package/dist/resource.d.ts.map +1 -0
  45. package/dist/resource.js +298 -0
  46. package/dist/resource.js.map +1 -0
  47. package/dist/rules.d.ts +106 -0
  48. package/dist/rules.d.ts.map +1 -0
  49. package/dist/rules.js +183 -0
  50. package/dist/rules.js.map +1 -0
  51. package/dist/sift/core.d.ts +104 -0
  52. package/dist/sift/core.d.ts.map +1 -0
  53. package/dist/sift/core.js +248 -0
  54. package/dist/sift/core.js.map +1 -0
  55. package/dist/sift/index.d.ts +10 -0
  56. package/dist/sift/index.d.ts.map +1 -0
  57. package/dist/sift/index.js +18 -0
  58. package/dist/sift/index.js.map +1 -0
  59. package/dist/sift/operations.d.ts +87 -0
  60. package/dist/sift/operations.d.ts.map +1 -0
  61. package/dist/sift/operations.js +257 -0
  62. package/dist/sift/operations.js.map +1 -0
  63. package/dist/sift/utils.d.ts +12 -0
  64. package/dist/sift/utils.d.ts.map +1 -0
  65. package/dist/sift/utils.js +80 -0
  66. package/dist/sift/utils.js.map +1 -0
  67. package/dist/system.d.ts +113 -0
  68. package/dist/system.d.ts.map +1 -0
  69. package/dist/system.js +712 -0
  70. package/dist/system.js.map +1 -0
  71. package/dist/target.d.ts +18 -0
  72. package/dist/target.d.ts.map +1 -0
  73. package/dist/target.js +41 -0
  74. package/dist/target.js.map +1 -0
  75. package/dist/types.d.ts +345 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +10 -0
  78. package/dist/types.js.map +1 -0
  79. package/indirect-aggregation.ts +216 -0
  80. package/indirect-resource.ts +205 -0
  81. package/mod.ts +81 -0
  82. package/mongo-query.ts +94 -0
  83. package/package.json +58 -0
  84. package/permission.ts +88 -0
  85. package/resource.ts +352 -0
  86. package/rules.ts +241 -0
  87. package/sift/MIT-LICENSE.txt +20 -0
  88. package/sift/core.ts +551 -0
  89. package/sift/index.ts +62 -0
  90. package/sift/operations.ts +449 -0
  91. package/sift/utils.ts +96 -0
  92. package/system.ts +974 -0
  93. package/target.ts +84 -0
  94. package/types.ts +408 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * `indirectResource` — déclaration d'une ressource atteignable via une
3
+ * jointure (DB-pushdown via aggregation pipeline). Pendant de `resource()`
4
+ * pour les cas où la donnée filtrante vit dans une autre collection (ou
5
+ * dans la même via un foreign field).
6
+ *
7
+ * Cas d'usage typique :
8
+ * - lister les `participant` qui ont une `org_membership` matching org=X
9
+ * - lister les `participant` dont l'`user` lié appartient à entreprise=Y
10
+ * - chaîner sur plusieurs niveaux (`participant → user → entreprise_member`)
11
+ *
12
+ * La méthode `.match()` retourne une `Rule` sentinelle (kind="indirect-match")
13
+ * que l'orchestrateur de `system.ts` détecte pour générer le pipeline
14
+ * d'aggregation correspondant — voir `indirect-aggregation.ts`.
15
+ *
16
+ * À la différence des ressources directes :
17
+ * - Pas de `fetch` ni de `dedupKey` : aucune donnée chargée à l'évaluation
18
+ * - Pas de rule `filter`/`include`/`require*` — uniquement `match` pour
19
+ * contribuer une contrainte au pipeline généré.
20
+ */
21
+ import { defineRule } from "./rules.js";
22
+ import { evaluateSpec, validateSpec } from "./mongo-query.js";
23
+ /**
24
+ * Sentinelle dans `Rule.descriptor` pour qu'`system.ts` reconnaisse
25
+ * cette rule comme "indirect" et collecte la resource correspondante.
26
+ *
27
+ * Le champ est typé `unknown` côté `RuleDescriptor` parce que
28
+ * `[extra: string]: unknown` est la signature catch-all des descriptors —
29
+ * on cast à l'extraction.
30
+ */
31
+ export const INDIRECT_RULE_KIND = "indirect-match";
32
+ export function indirectResource(spec) {
33
+ // Normalize `from`: a `Resource` (from `resource({...})`) has no `kind`
34
+ // field; we treat it as "direct". An `IndirectResource` has
35
+ // `kind: "indirect"` set by this very factory. Walking the chain in
36
+ // the orchestrator reads `from.kind` to know whether to descend.
37
+ const fromNormalized = {
38
+ id: spec.from.id,
39
+ kind: spec.from.kind === "indirect"
40
+ ? "indirect"
41
+ : "direct",
42
+ };
43
+ const impl = {
44
+ id: spec.id,
45
+ kind: "indirect",
46
+ from: fromNormalized,
47
+ on: spec.on,
48
+ to: spec.to,
49
+ cardinality: spec.cardinality,
50
+ fetcher: spec.fetch,
51
+ cacheKeyForTarget(target) {
52
+ // Mirror of Resource.cacheKeyForTarget — the indirect's cache key
53
+ // is target-derived (typically by source dedup). Useful for
54
+ // `CanContext.preseed()` when the caller wants to inject joined
55
+ // docs already returned by a pipeline aggregation.
56
+ return `${spec.id}::${JSON.stringify(target)}`;
57
+ },
58
+ match() {
59
+ return defineRule({
60
+ kind: INDIRECT_RULE_KIND,
61
+ needs: [],
62
+ describe: () => ({ indirectResource: impl }),
63
+ check: (_data, _payload, ctx) => {
64
+ // Cap-mode : sentinelle. `system.ts` reads the descriptor and
65
+ // emits the pipeline via `buildAggregationStages`. The rule
66
+ // itself contributes nothing.
67
+ if (ctx.capability)
68
+ return { ok: true };
69
+ // Concrete mode without fetcher : sentinelle aussi (legacy).
70
+ if (!impl.fetcher)
71
+ return { ok: true };
72
+ // Concrete mode with fetcher : evaluate `grant.with[id]` spec
73
+ // against the joined docs. The engine already populated the
74
+ // joined docs in the context cache (see system.ts) — we just
75
+ // read them from `_indirectFetched` (an opaque per-rule pass).
76
+ // If no spec is set, the rule passes (the grant trusts the
77
+ // indirect resource without further check).
78
+ const spec = ctx.grant.with?.[impl.id];
79
+ if (spec === undefined)
80
+ return { ok: true };
81
+ if (typeof spec === "object" && spec !== null)
82
+ validateSpec(spec);
83
+ const joined = ctx._indirectFetched?.get(impl.id) ?? [];
84
+ const passes = joined.some((d) => evaluateSpec(spec, d));
85
+ return passes
86
+ ? { ok: true }
87
+ : {
88
+ ok: false,
89
+ reason: `indirect[${impl.id}] no joined doc matches spec`,
90
+ };
91
+ },
92
+ });
93
+ },
94
+ };
95
+ return impl;
96
+ }
97
+ /**
98
+ * Extraction utilitaire pour `system.ts` : récupère la `IndirectResource`
99
+ * portée par une rule sentinelle, ou `null` si la rule n'en est pas une.
100
+ */
101
+ export function extractIndirectResource(rule) {
102
+ if (rule.descriptor.kind !== INDIRECT_RULE_KIND)
103
+ return null;
104
+ const ref = rule.descriptor.indirectResource;
105
+ if (!ref || typeof ref !== "object")
106
+ return null;
107
+ return ref;
108
+ }
109
+ //# sourceMappingURL=indirect-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indirect-resource.js","sourceRoot":"","sources":["../indirect-resource.ts"],"names":[],"mappings":"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"}
package/dist/mod.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@diister/quick-permission` — API publique.
3
+ *
4
+ * Expose une primitive unique `defineRule({ needs, check })` autour de
5
+ * laquelle sont organisés les `Resource`, méthodes de sucre, et le moteur
6
+ * d'orchestration avec dedup par contexte.
7
+ *
8
+ * Cf. `docs/rfc-resource-pipe-api.md` pour la motivation, les décisions
9
+ * de design et le plan de migration.
10
+ */
11
+ export { seg, target } from "./target.ts";
12
+ export type { AnySegment, AnyTarget, CanResult, FetchCtx, FilterContribution, Grant, IndirectResourceInfo, ListEntry, Permission, Resource, ResourceData, ResourcesData, Rule, RuleDescriptor, RuleResult, SegmentSpec, SerializableSegment, SerializableTarget, SpecToSegment, Subject, TargetArgs, TargetNone, TargetOptional, TargetPath, TargetRequired, TreeNode, } from "./types.ts";
13
+ export { resource } from "./resource.ts";
14
+ export { indirectResource } from "./indirect-resource.ts";
15
+ export type { IndirectResource, IndirectResourceJoin, } from "./indirect-resource.ts";
16
+ export type { AggregationStage } from "./indirect-aggregation.ts";
17
+ export { defineRule, inputMatch, matchPath, requireSelf } from "./rules.ts";
18
+ export type { DefineRuleOpts } from "./rules.ts";
19
+ export { intermediate, permission } from "./permission.ts";
20
+ export type { IntermediateBuilder, IntermediateConfig, PermissionBuilder, PermissionConfig, } from "./permission.ts";
21
+ export { createSystem } from "./system.ts";
22
+ export type { CanContext, Provider, ProviderFn, ProviderObject, System, } from "./system.ts";
23
+ export { applyFilter, pickFields } from "./core/filtering.ts";
24
+ export type { FilterSpec } from "./core/merging.ts";
25
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,YAAY,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,QAAQ,EACR,kBAAkB,EAClB,KAAK,EACL,oBAAoB,EACpB,SAAS,EACT,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,cAAc,EACd,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,OAAO,EACP,UAAU,EACV,UAAU,EACV,cAAc,EACd,UAAU,EACV,cAAc,EACd,QAAQ,GACT,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EACV,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC5E,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC3D,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,UAAU,EACV,QAAQ,EACR,UAAU,EACV,cAAc,EACd,MAAM,GACP,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/mod.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@diister/quick-permission` — API publique.
3
+ *
4
+ * Expose une primitive unique `defineRule({ needs, check })` autour de
5
+ * laquelle sont organisés les `Resource`, méthodes de sucre, et le moteur
6
+ * d'orchestration avec dedup par contexte.
7
+ *
8
+ * Cf. `docs/rfc-resource-pipe-api.md` pour la motivation, les décisions
9
+ * de design et le plan de migration.
10
+ */
11
+ // ─── Targets ─────────────────────────────────────────────────────────────
12
+ export { seg, target } from "./target.js";
13
+ // ─── Resource factory + sugar methods ────────────────────────────────────
14
+ export { resource } from "./resource.js";
15
+ // ─── Indirect resource (JOIN-based, generates aggregation pipeline) ──────
16
+ export { indirectResource } from "./indirect-resource.js";
17
+ // ─── defineRule + standalone helpers ─────────────────────────────────────
18
+ export { defineRule, inputMatch, matchPath, requireSelf } from "./rules.js";
19
+ // ─── Permission builders ─────────────────────────────────────────────────
20
+ export { intermediate, permission } from "./permission.js";
21
+ // ─── System ──────────────────────────────────────────────────────────────
22
+ export { createSystem } from "./system.js";
23
+ // ─── Field-projection helpers (used outside permission checks too) ───────
24
+ export { applyFilter, pickFields } from "./core/filtering.js";
25
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4EAA4E;AAC5E,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAgC1C,4EAA4E;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,4EAA4E;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO1D,4EAA4E;AAC5E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG5E,4EAA4E;AAC5E,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAQ3D,4EAA4E;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS3C,4EAA4E;AAC5E,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Évaluateur d'expressions style MongoDB query basé sur `sift`.
3
+ *
4
+ * Sift supporte ~100% du Mongo query language en pur JS. On délègue
5
+ * l'évaluation à sift mais on **valide la spec en amont** pour s'assurer
6
+ * qu'aucun opérateur dangereux n'est utilisé (`$where`, `$function`, etc.
7
+ * permettent de l'exécution de code arbitraire — interdits dans un grant
8
+ * qui peut venir de la DB).
9
+ *
10
+ * Whitelist :
11
+ * - Comparaisons : `$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`, `$lte`
12
+ * - Existence : `$exists`, `$type`
13
+ * - Logiques : `$and`, `$or`, `$nor`, `$not`
14
+ * - String : `$regex`, `$options`
15
+ * - Tableaux : `$all`, `$elemMatch`, `$size`
16
+ *
17
+ * Interdit (pas dans la whitelist, throw au boot ou au check) :
18
+ * - `$where`, `$function`, `$expr`, `$jsonSchema`, `$accumulator`
19
+ *
20
+ * La même spec sert à :
21
+ * 1. Évaluer en mémoire un document (per-doc check via sift)
22
+ * 2. Filtrer une collection MongoDB en pushdown (la spec EST déjà du Mongo)
23
+ */
24
+ export type MongoSpec = Record<string, unknown>;
25
+ /**
26
+ * Vérifie récursivement qu'aucun opérateur non-whitelisté n'est présent
27
+ * dans la spec. Throw au premier opérateur interdit (`$where`, etc.).
28
+ *
29
+ * À appeler à la création du grant (validation côté API) ou au pire au
30
+ * check time, pour empêcher l'exécution de code arbitraire.
31
+ */
32
+ export declare function validateSpec(spec: unknown, path?: string): void;
33
+ /**
34
+ * Évalue une spec contre un document. La spec est validée d'abord pour
35
+ * rejeter tout opérateur non-whitelisté.
36
+ */
37
+ export declare function evaluateSpec(spec: MongoSpec, doc: unknown): boolean;
38
+ //# sourceMappingURL=mongo-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongo-query.d.ts","sourceRoot":"","sources":["../mongo-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA+BH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,SAAM,GAAG,IAAI,CAqB5D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAInE"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Évaluateur d'expressions style MongoDB query basé sur `sift`.
3
+ *
4
+ * Sift supporte ~100% du Mongo query language en pur JS. On délègue
5
+ * l'évaluation à sift mais on **valide la spec en amont** pour s'assurer
6
+ * qu'aucun opérateur dangereux n'est utilisé (`$where`, `$function`, etc.
7
+ * permettent de l'exécution de code arbitraire — interdits dans un grant
8
+ * qui peut venir de la DB).
9
+ *
10
+ * Whitelist :
11
+ * - Comparaisons : `$eq`, `$ne`, `$in`, `$nin`, `$gt`, `$gte`, `$lt`, `$lte`
12
+ * - Existence : `$exists`, `$type`
13
+ * - Logiques : `$and`, `$or`, `$nor`, `$not`
14
+ * - String : `$regex`, `$options`
15
+ * - Tableaux : `$all`, `$elemMatch`, `$size`
16
+ *
17
+ * Interdit (pas dans la whitelist, throw au boot ou au check) :
18
+ * - `$where`, `$function`, `$expr`, `$jsonSchema`, `$accumulator`
19
+ *
20
+ * La même spec sert à :
21
+ * 1. Évaluer en mémoire un document (per-doc check via sift)
22
+ * 2. Filtrer une collection MongoDB en pushdown (la spec EST déjà du Mongo)
23
+ */
24
+ import sift from "./sift/index.js";
25
+ const ALLOWED_OPERATORS = new Set([
26
+ // Comparison
27
+ "$eq",
28
+ "$ne",
29
+ "$in",
30
+ "$nin",
31
+ "$gt",
32
+ "$gte",
33
+ "$lt",
34
+ "$lte",
35
+ // Existence / type
36
+ "$exists",
37
+ "$type",
38
+ // Logical
39
+ "$and",
40
+ "$or",
41
+ "$nor",
42
+ "$not",
43
+ // String
44
+ "$regex",
45
+ "$options",
46
+ // Array
47
+ "$all",
48
+ "$elemMatch",
49
+ "$size",
50
+ ]);
51
+ /**
52
+ * Vérifie récursivement qu'aucun opérateur non-whitelisté n'est présent
53
+ * dans la spec. Throw au premier opérateur interdit (`$where`, etc.).
54
+ *
55
+ * À appeler à la création du grant (validation côté API) ou au pire au
56
+ * check time, pour empêcher l'exécution de code arbitraire.
57
+ */
58
+ export function validateSpec(spec, path = "$") {
59
+ if (spec === null || typeof spec !== "object")
60
+ return;
61
+ if (Array.isArray(spec)) {
62
+ // A `for` loop, not `forEach`: the arrow's implicit return handed back
63
+ // validateSpec's value, which the iteration then discarded.
64
+ for (const [i, item] of spec.entries()) {
65
+ validateSpec(item, `${path}[${i}]`);
66
+ }
67
+ return;
68
+ }
69
+ for (const [key, value] of Object.entries(spec)) {
70
+ if (key.startsWith("$")) {
71
+ if (!ALLOWED_OPERATORS.has(key)) {
72
+ throw new Error(`Forbidden Mongo operator at ${path}: "${key}". ` +
73
+ `Allowed: ${[...ALLOWED_OPERATORS].join(", ")}`);
74
+ }
75
+ }
76
+ validateSpec(value, `${path}.${key}`);
77
+ }
78
+ }
79
+ /**
80
+ * Évalue une spec contre un document. La spec est validée d'abord pour
81
+ * rejeter tout opérateur non-whitelisté.
82
+ */
83
+ export function evaluateSpec(spec, doc) {
84
+ validateSpec(spec);
85
+ // deno-lint-ignore no-explicit-any
86
+ return sift(spec)(doc);
87
+ }
88
+ //# sourceMappingURL=mongo-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongo-query.js","sourceRoot":"","sources":["../mongo-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,IAAI,MAAM,iBAAiB,CAAC;AAEnC,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAC;IACrD,aAAa;IACb,KAAK;IACL,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,mBAAmB;IACnB,SAAS;IACT,OAAO;IACP,UAAU;IACV,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,SAAS;IACT,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,MAAM;IACN,YAAY;IACZ,OAAO;CACR,CAAC,CAAC;AAIH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAa,EAAE,IAAI,GAAG,GAAG;IACpD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO;IACtD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,uEAAuE;QACvE,4DAA4D;QAC5D,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACvC,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAc,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CACb,+BAA+B,IAAI,MAAM,GAAG,KAAK;oBAC/C,YAAY,CAAC,GAAG,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClD,CAAC;YACJ,CAAC;QACH,CAAC;QACD,YAAY,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAe,EAAE,GAAY;IACxD,YAAY,CAAC,IAAI,CAAC,CAAC;IACnB,mCAAmC;IACnC,OAAQ,IAAY,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Builder de permission pour resource-pipe.
3
+ *
4
+ * API en deux étapes (`permission(opts).rules([])`), même justification que
5
+ * `library/permission.ts` (cf. RFC declarative §"Décision two-call").
6
+ *
7
+ * Différence majeure vs l'API declarative actuelle : pas de slot `fetch` au
8
+ * niveau permission. Les fetches sont déclarés au niveau des `Resource`s
9
+ * que les rules consomment via `needs`.
10
+ */
11
+ import type { AnyTarget, Grant, Permission, Rule } from "./types.ts";
12
+ export type PermissionConfig<TMeta> = {
13
+ readonly metadata?: TMeta;
14
+ readonly target: AnyTarget;
15
+ };
16
+ export type PermissionBuilder<TMeta> = {
17
+ rules(rules: readonly Rule[]): Permission<TMeta>;
18
+ };
19
+ /**
20
+ * Déclare une permission "feuille" (vérifiable directement via `can()`).
21
+ *
22
+ * @example
23
+ * "users.read": permission({
24
+ * metadata: { description: "Lire un user" },
25
+ * target: target.required("user"),
26
+ * }).rules([
27
+ * userOf.match(),
28
+ * userOf.filter(),
29
+ * ]),
30
+ */
31
+ export declare function permission<TMeta = unknown>(config: PermissionConfig<TMeta>): PermissionBuilder<TMeta>;
32
+ export type IntermediateConfig<TMeta> = {
33
+ readonly metadata?: TMeta;
34
+ readonly target: AnyTarget;
35
+ /** Expand un grant sur cette intermediate en N grants sur les enfants. */
36
+ readonly expandsTo: (grant: Grant) => readonly Grant[];
37
+ };
38
+ export type IntermediateBuilder<TMeta> = {
39
+ /** Optionnellement attacher des rules au niveau intermediate. */
40
+ rules(rules: readonly Rule[]): Permission<TMeta>;
41
+ };
42
+ /**
43
+ * Déclare une permission "intermediate" (macro qui s'expand en grants
44
+ * enfants au moment du check).
45
+ *
46
+ * @example
47
+ * "users.manage": intermediate({
48
+ * target: target.required("user"),
49
+ * expandsTo: (grant) => [
50
+ * { ...grant, key: "users.read" },
51
+ * { ...grant, key: "users.update" },
52
+ * { ...grant, key: "users.delete" },
53
+ * ],
54
+ * }).rules([]),
55
+ */
56
+ export declare function intermediate<TMeta = unknown>(config: IntermediateConfig<TMeta>): IntermediateBuilder<TMeta>;
57
+ //# sourceMappingURL=permission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../permission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,MAAM,gBAAgB,CAAC,KAAK,IAAI;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,KAAK,IAAI;IACrC,KAAK,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;CAClD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,GAAG,OAAO,EACxC,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAC9B,iBAAiB,CAAC,KAAK,CAAC,CAU1B;AAED,MAAM,MAAM,kBAAkB,CAAC,KAAK,IAAI;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,KAAK,EAAE,CAAC;CACxD,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,KAAK,IAAI;IACvC,iEAAiE;IACjE,KAAK,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;CAClD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,GAAG,OAAO,EAC1C,MAAM,EAAE,kBAAkB,CAAC,KAAK,CAAC,GAChC,mBAAmB,CAAC,KAAK,CAAC,CAW5B"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Builder de permission pour resource-pipe.
3
+ *
4
+ * API en deux étapes (`permission(opts).rules([])`), même justification que
5
+ * `library/permission.ts` (cf. RFC declarative §"Décision two-call").
6
+ *
7
+ * Différence majeure vs l'API declarative actuelle : pas de slot `fetch` au
8
+ * niveau permission. Les fetches sont déclarés au niveau des `Resource`s
9
+ * que les rules consomment via `needs`.
10
+ */
11
+ /**
12
+ * Déclare une permission "feuille" (vérifiable directement via `can()`).
13
+ *
14
+ * @example
15
+ * "users.read": permission({
16
+ * metadata: { description: "Lire un user" },
17
+ * target: target.required("user"),
18
+ * }).rules([
19
+ * userOf.match(),
20
+ * userOf.filter(),
21
+ * ]),
22
+ */
23
+ export function permission(config) {
24
+ return {
25
+ rules(rules) {
26
+ return {
27
+ metadata: config.metadata,
28
+ target: config.target,
29
+ rules,
30
+ };
31
+ },
32
+ };
33
+ }
34
+ /**
35
+ * Déclare une permission "intermediate" (macro qui s'expand en grants
36
+ * enfants au moment du check).
37
+ *
38
+ * @example
39
+ * "users.manage": intermediate({
40
+ * target: target.required("user"),
41
+ * expandsTo: (grant) => [
42
+ * { ...grant, key: "users.read" },
43
+ * { ...grant, key: "users.update" },
44
+ * { ...grant, key: "users.delete" },
45
+ * ],
46
+ * }).rules([]),
47
+ */
48
+ export function intermediate(config) {
49
+ return {
50
+ rules(rules) {
51
+ return {
52
+ metadata: config.metadata,
53
+ target: config.target,
54
+ rules,
55
+ expandsTo: config.expandsTo,
56
+ };
57
+ },
58
+ };
59
+ }
60
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../permission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CACxB,MAA+B;IAE/B,OAAO;QACL,KAAK,CAAC,KAAK;YACT,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,KAAK;aACN,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAcD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAiC;IAEjC,OAAO;QACL,KAAK,CAAC,KAAK;YACT,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,KAAK;gBACL,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `Resource` : source de donnée fetchable, dédupliquée par contexte.
3
+ *
4
+ * Une Resource est conçue pour être déclarée une fois dans son domaine
5
+ * (`userOf`, `expositionInfoOf`, etc.) et réutilisée entre permissions.
6
+ * Le moteur dédup les fetches au sein d'un context via `(id, dedupKey)`.
7
+ *
8
+ * Les méthodes `match`/`filter`/`includes`/`require*` sont du sucre :
9
+ * elles produisent des Rule via `defineRule`, avec la resource comme need.
10
+ *
11
+ * ─── Capability-mode contract ────────────────────────────────────────────
12
+ * A request target containing a wildcard (`"*"` or `"xxx:*"`) is a capability
13
+ * query — "can the subject act on ANY resource of this shape?". The engine
14
+ * skips resource fetches in this mode (no concrete resource to fetch). Each
15
+ * rule must declare what it does when its data is unavailable:
16
+ *
17
+ * match : silent-pass (cannot verify spec against absent data),
18
+ * but exposes the spec as a constraint for DB pushdown.
19
+ * filter : silent-pass — no projection happens; `output.data`
20
+ * stays undefined.
21
+ * includes : deny — membership cannot be checked without the resource.
22
+ * requireTruthy : deny — no value to test for truthiness.
23
+ * requireOwner : deny — no owner field to compare.
24
+ * requireMembership: deny — no membership list to scan.
25
+ * requireCustom : deny — predicate cannot run without `data`.
26
+ *
27
+ * Pure rules (no `needs`) like `matchPath` and `requireSelf` are unaffected:
28
+ * they read `ctx.target` / `ctx.subject` only.
29
+ */
30
+ import type { FetchCtx, Grant, Resource } from "./types.ts";
31
+ /**
32
+ * Crée une Resource. À utiliser une fois par entité de domaine, partagée
33
+ * entre toutes les permissions qui la consomment.
34
+ *
35
+ * @example
36
+ * const userOf = resource({
37
+ * id: "user",
38
+ * fetch: ({ target }) => usersRepo.findById(target[0] as string),
39
+ * dedupKey: ({ target }) => target[0] as string,
40
+ * });
41
+ */
42
+ export declare function resource<T>(opts: {
43
+ readonly id: string;
44
+ readonly fetch: (ctx: FetchCtx) => T | Promise<T>;
45
+ readonly activeWhen?: (grant: Grant) => boolean;
46
+ readonly dedupKey?: (ctx: FetchCtx) => string;
47
+ }): Resource<T>;
48
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAQ,MAAM,YAAY,CAAC;AA0SlE;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,MAAM,CAAC;CAC/C,GAAG,QAAQ,CAAC,CAAC,CAAC,CAOd"}