@nmnmcc/ability 2.0.7 → 2.1.2

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 (87) hide show
  1. package/README.md +52 -29
  2. package/dist/Authorizer.d.ts +21 -12
  3. package/dist/Authorizer.d.ts.map +1 -1
  4. package/dist/Authorizer.js.map +1 -1
  5. package/dist/Condition.d.ts +121 -19
  6. package/dist/Condition.d.ts.map +1 -1
  7. package/dist/Condition.js +32 -21
  8. package/dist/Condition.js.map +1 -1
  9. package/dist/Domain.d.ts +9 -0
  10. package/dist/Domain.d.ts.map +1 -1
  11. package/dist/Domain.js +5 -0
  12. package/dist/Domain.js.map +1 -1
  13. package/dist/Limits.d.ts +44 -13
  14. package/dist/Limits.d.ts.map +1 -1
  15. package/dist/Limits.js +52 -22
  16. package/dist/Limits.js.map +1 -1
  17. package/dist/Policy.d.ts +102 -18
  18. package/dist/Policy.d.ts.map +1 -1
  19. package/dist/Policy.js +7 -6
  20. package/dist/Policy.js.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/internal/algebra.js +2 -2
  23. package/dist/internal/analysis.js +81 -75
  24. package/dist/internal/analysis.js.map +1 -1
  25. package/dist/internal/authorizer.d.ts.map +1 -1
  26. package/dist/internal/authorizer.js +11 -7
  27. package/dist/internal/authorizer.js.map +1 -1
  28. package/dist/internal/bind.js +86 -64
  29. package/dist/internal/bind.js.map +1 -1
  30. package/dist/internal/catalog.d.ts +33 -10
  31. package/dist/internal/catalog.d.ts.map +1 -1
  32. package/dist/internal/catalog.js +170 -77
  33. package/dist/internal/catalog.js.map +1 -1
  34. package/dist/internal/decide.js +27 -31
  35. package/dist/internal/decide.js.map +1 -1
  36. package/dist/internal/domain.js +19 -5
  37. package/dist/internal/domain.js.map +1 -1
  38. package/dist/internal/json.js +120 -76
  39. package/dist/internal/json.js.map +1 -1
  40. package/dist/internal/limits.d.ts +28 -0
  41. package/dist/internal/limits.d.ts.map +1 -1
  42. package/dist/internal/limits.js +75 -16
  43. package/dist/internal/limits.js.map +1 -1
  44. package/dist/internal/nodes.d.ts.map +1 -1
  45. package/dist/internal/nodes.js +53 -44
  46. package/dist/internal/nodes.js.map +1 -1
  47. package/dist/internal/patterns.js +50 -15
  48. package/dist/internal/patterns.js.map +1 -1
  49. package/dist/internal/policy-json.js +169 -127
  50. package/dist/internal/policy-json.js.map +1 -1
  51. package/dist/internal/policy-nodes.d.ts.map +1 -1
  52. package/dist/internal/policy-nodes.js +16 -13
  53. package/dist/internal/policy-nodes.js.map +1 -1
  54. package/dist/internal/policy.d.ts.map +1 -1
  55. package/dist/internal/policy.js +38 -22
  56. package/dist/internal/policy.js.map +1 -1
  57. package/dist/internal/resource.js +1 -1
  58. package/dist/internal/rule.d.ts.map +1 -1
  59. package/dist/internal/rule.js +6 -5
  60. package/dist/internal/rule.js.map +1 -1
  61. package/dist/internal/search.d.ts.map +1 -1
  62. package/dist/internal/search.js +106 -92
  63. package/dist/internal/search.js.map +1 -1
  64. package/dist/internal/trampoline.js +19 -5
  65. package/dist/internal/trampoline.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/Authorizer.ts +30 -20
  68. package/src/Condition.ts +317 -86
  69. package/src/Domain.ts +9 -0
  70. package/src/Limits.ts +69 -29
  71. package/src/Policy.ts +143 -21
  72. package/src/internal/analysis.ts +154 -124
  73. package/src/internal/authorizer.ts +29 -14
  74. package/src/internal/bind.ts +193 -136
  75. package/src/internal/catalog.ts +320 -118
  76. package/src/internal/decide.ts +61 -50
  77. package/src/internal/domain.ts +39 -3
  78. package/src/internal/json.ts +212 -117
  79. package/src/internal/limits.ts +101 -31
  80. package/src/internal/nodes.ts +65 -47
  81. package/src/internal/patterns.ts +79 -25
  82. package/src/internal/policy-json.ts +266 -197
  83. package/src/internal/policy-nodes.ts +31 -23
  84. package/src/internal/policy.ts +63 -23
  85. package/src/internal/rule.ts +16 -3
  86. package/src/internal/search.ts +186 -136
  87. package/src/internal/trampoline.ts +51 -4
@@ -1,11 +1,12 @@
1
- import { LimitExceeded, policyNodes } from "./limits.js";
2
- import { Problem, decodeAt, problem, read, readList, readString, spend, toJson as toJson$1 } from "./json.js";
1
+ import { LimitExceeded } from "./limits.js";
2
+ import { adaptive, run } from "./trampoline.js";
3
3
  import { at, pathOf } from "./place.js";
4
+ import { Problem, decodeAt, problemAt, read, readList, readString, spend, toJson as toJson$1 } from "./json.js";
4
5
  import { make } from "./rule.js";
5
6
  import { Ceiling, Rules, isAlgorithm, policy_nodes_exports, rulesOf } from "./policy-nodes.js";
7
+ import * as Arr from "effect/Array";
6
8
  import * as Effect from "effect/Effect";
7
9
  import * as Schema from "effect/Schema";
8
- import * as Arr from "effect/Array";
9
10
  // src/internal/policy-json.ts
10
11
  /**
11
12
  * The JSON form of a policy tree. The decoder is a plain walk, as the decoder
@@ -49,27 +50,32 @@ const ruleJson = (rule, metaJson, whenJson) => {
49
50
  */
50
51
  const toJson = (node, meta) => {
51
52
  const whens = new Map(Arr.map(rulesOf(node), (rule) => [rule, rule.when === void 0 ? void 0 : toJson$1(rule.when)]));
52
- const tree = (current) => {
53
- switch (current._tag) {
54
- case "Rules": return {
55
- _tag: "Rules",
56
- algorithm: current.algorithm,
57
- rules: Arr.map(current.rules, (rule) => ruleJson(rule, meta, (found) => whens.get(found)))
58
- };
59
- case "All":
60
- case "Any":
61
- case "FirstOf": return {
62
- _tag: current._tag,
63
- policies: Arr.map(current.policies, tree)
64
- };
65
- case "Ceiling": return {
66
- _tag: "Ceiling",
67
- cap: tree(current.cap),
68
- policy: tree(current.policy)
69
- };
70
- }
71
- };
72
- return tree(node);
53
+ return run(node.depth, (R) => {
54
+ const tree = (current) => {
55
+ switch (current._tag) {
56
+ case "Rules": return R.succeed({
57
+ _tag: "Rules",
58
+ algorithm: current.algorithm,
59
+ rules: Arr.map(current.rules, (rule) => ruleJson(rule, meta, (found) => whens.get(found)))
60
+ });
61
+ case "All":
62
+ case "Any":
63
+ case "FirstOf": {
64
+ const tag = current._tag;
65
+ return R.map(R.forEach(current.policies, (child) => R.suspend(() => tree(child))), (policies) => ({
66
+ _tag: tag,
67
+ policies
68
+ }));
69
+ }
70
+ case "Ceiling": return R.flatMap(R.suspend(() => tree(current.cap)), (cap) => R.map(R.suspend(() => tree(current.policy)), (policy) => ({
71
+ _tag: "Ceiling",
72
+ cap,
73
+ policy
74
+ })));
75
+ }
76
+ };
77
+ return tree(node);
78
+ });
73
79
  };
74
80
  /**
75
81
  * Gives the JSON form of a policy tree as an Effect. The `meta` Schema of the
@@ -82,12 +88,8 @@ const encode = (node, meta) => Effect.gen(function* () {
82
88
  return toJson(node, (rule) => forms.get(rule));
83
89
  });
84
90
  /** Reads a list of strings. */
85
- const readStrings = (input, name, path, budget) => Arr.map(readList(input, name, path, budget), (value, index) => {
86
- if (typeof value !== "string") throw problem([
87
- ...path,
88
- name,
89
- index
90
- ], "The item must be a string");
91
+ const readStrings = (input, name, at$2, budget) => Arr.map(readList(input, name, at$2, budget), (value, index) => {
92
+ if (typeof value !== "string") throw problemAt(at(at$2, name, index), "The item must be a string");
91
93
  return value;
92
94
  });
93
95
  /**
@@ -95,57 +97,65 @@ const readStrings = (input, name, path, budget) => Arr.map(readList(input, name,
95
97
  * from the prototype of a class is not plain data, so it is a problem: a rule
96
98
  * that loses its condition would allow more.
97
99
  */
98
- const readOptional = (input, name, path, read$1) => {
100
+ const readOptional = (input, name, at, read$1) => {
99
101
  if (Object.hasOwn(input, name)) return read$1();
100
- read(input, name, path);
102
+ read(input, name, at);
101
103
  };
102
- /** Counts the objects and items of a value, as the other parts count. */
103
- const spendValue = (value, path, depth, budget) => {
104
- if (typeof value !== "object" || value === null) return;
105
- if (depth > 500) throw problem(path, `The value has more than ${String(500)} levels. The most is ${String(500)}.`);
104
+ /**
105
+ * Counts the objects and items of a value, as the other parts count. It is a
106
+ * walk of the trampoline, so a deep value does not fill the stack.
107
+ */
108
+ const spendValue = (budget) => adaptive((R, child) => (part, depth) => {
109
+ const { value, at: at$3 } = part;
110
+ if (typeof value !== "object" || value === null) return R.succeed(void 0);
106
111
  const list = Array.isArray(value);
107
- if (list) spend(budget, 1 + value.length, path);
112
+ if (list) spend(budget, 1 + value.length, at$3);
108
113
  const names = list ? Array.from({ length: value.length }, (_, index) => String(index)) : Object.keys(value);
109
- if (!list) spend(budget, 1 + names.length, path);
110
- Arr.forEach(names, (name) => spendValue(read(value, name, path), [...path, name], depth + 1, budget));
111
- };
114
+ if (!list) spend(budget, 1 + names.length, at$3);
115
+ return R.map(R.forEach(names, (name) => child({
116
+ value: read(value, name, at$3),
117
+ at: at(at$3, name)
118
+ }, depth + 1)), () => void 0);
119
+ });
112
120
  /**
113
121
  * Decodes the `meta` of a rule with the `meta` Schema of the domain, as an
114
122
  * Effect, since the Schema may need an async step. The Schema reads all of
115
123
  * the value, so its size counts in the budget.
116
124
  */
117
125
  const decodeMeta = (rule, domain, budget) => {
118
- const { input } = rule;
119
- const path = pathOf(rule.place);
120
- return Effect.flatMap(attempt(() => typeof input !== "object" || input === null ? void 0 : readOptional(input, "meta", path, () => {
121
- const value = read(input, "meta", path);
122
- if (domain.meta === void 0) throw problem([...path, "meta"], "The domain has no Schema for meta");
123
- spendValue(value, [...path, "meta"], 1, budget);
126
+ const { input, place } = rule;
127
+ const at$4 = at(place, "meta");
128
+ return Effect.flatMap(attempt(() => typeof input !== "object" || input === null ? void 0 : readOptional(input, "meta", place, () => {
129
+ const value = read(input, "meta", place);
130
+ if (domain.meta === void 0) throw problemAt(at$4, "The domain has no Schema for meta");
131
+ spendValue(budget)({
132
+ value,
133
+ at: at$4
134
+ });
124
135
  return {
125
136
  value,
126
137
  schema: domain.meta
127
138
  };
128
139
  })), (found) => found === void 0 ? Effect.void : Effect.mapError(Schema.decodeUnknownEffect(metaCodec(found.schema))(found.value), (error) => new Problem({
129
- path: [...path, "meta"],
140
+ path: pathOf(at$4),
130
141
  message: error.message,
131
142
  issue: error.issue
132
143
  })));
133
144
  };
134
145
  const decodeRule = (rule, meta, budget, domain) => {
135
- const { input } = rule;
136
- const path = pathOf(rule.place);
137
- if (typeof input !== "object" || input === null) throw problem(path, "A rule must be an object");
138
- spend(budget, 1, path);
139
- const tag = read(input, "_tag", path);
140
- if (tag !== "Allow" && tag !== "Deny") throw problem([...path, "_tag"], "A rule must be \"Allow\" or \"Deny\"");
141
- const when = readOptional(input, "when", path, () => decodeAt(read(input, "when", path), [...path, "when"], budget));
142
- const fields = readOptional(input, "fields", path, () => readStrings(input, "fields", path, budget));
143
- const reason = readOptional(input, "reason", path, () => readString(input, "reason", path));
144
- const id = readOptional(input, "id", path, () => readString(input, "id", path));
146
+ const { input, place } = rule;
147
+ if (typeof input !== "object" || input === null) throw problemAt(place, "A rule must be an object");
148
+ spend(budget, 1, place);
149
+ const tag = read(input, "_tag", place);
150
+ if (tag !== "Allow" && tag !== "Deny") throw problemAt(at(place, "_tag"), "A rule must be \"Allow\" or \"Deny\"");
151
+ const when = readOptional(input, "when", place, () => decodeAt(read(input, "when", place), at(place, "when"), budget));
152
+ const fields = readOptional(input, "fields", place, () => readStrings(input, "fields", place, budget));
153
+ const reason = readOptional(input, "reason", place, () => readString(input, "reason", place));
154
+ const id = readOptional(input, "id", place, () => readString(input, "id", place));
145
155
  return make({
146
156
  _tag: tag,
147
- actions: readStrings(input, "actions", path, budget),
148
- resources: readStrings(input, "resources", path, budget),
157
+ actions: readStrings(input, "actions", place, budget),
158
+ resources: readStrings(input, "resources", place, budget),
149
159
  ...when === void 0 ? {} : { when },
150
160
  ...fields === void 0 ? {} : { fields },
151
161
  ...reason === void 0 ? {} : { reason },
@@ -157,82 +167,108 @@ const decodeRule = (rule, meta, budget, domain) => {
157
167
  /**
158
168
  * Reads the shape of a tree, and puts each rule in `rules`. The rules are
159
169
  * decoded after the walk: a condition has levels of its own, and a decode
160
- * inside the walk would go down the levels of both. `path` is the path of the
161
- * node for a problem here, and `place` keeps it for later.
170
+ * inside the walk would go down the levels of both. The walk is a walk of
171
+ * the trampoline, so a deep tree does not fill the stack.
162
172
  */
163
- const readTree = (input, path, place, depth, budget, rules) => {
164
- if (depth > 256) throw problem(path, `A policy tree has more than ${String(256)} levels. The most is ${String(256)}.`);
165
- if (typeof input !== "object" || input === null) throw problem(path, "A node of a policy must be an object");
166
- spend(budget, 1, path);
167
- const child = (value, ...parts) => readTree(value, [...path, ...parts], at(place, ...parts), depth + 1, budget, rules);
168
- const tag = read(input, "_tag", path);
169
- switch (tag) {
170
- case "Rules": {
171
- const algorithm = readString(input, "algorithm", path);
172
- if (!isAlgorithm(algorithm)) throw problem([...path, "algorithm"], `"${algorithm}" is not a combining algorithm`);
173
- const found = Arr.map(readList(input, "rules", path, budget), (rule, index) => rules.push({
174
- input: rule,
175
- place: at(place, "rules", index)
176
- }) - 1);
177
- return sized(path, {
178
- _tag: "Rules",
179
- place,
180
- size: 1 + found.length,
181
- algorithm,
182
- rules: found
183
- });
184
- }
185
- case "All":
186
- case "Any":
187
- case "FirstOf": {
188
- const policies = Arr.map(readList(input, "policies", path, budget), (value, index) => child(value, "policies", index));
189
- return sized(path, {
173
+ const readTree = (budget, rules) => {
174
+ const count = { nodes: 0 };
175
+ const counted = (more, at) => {
176
+ count.nodes += more;
177
+ const most = budget.limits.policyNodes;
178
+ if (count.nodes > most) throw problemAt(at, `A policy tree has more than ${String(most)} nodes. The most is ${String(most)}.`);
179
+ };
180
+ return adaptive((R, child) => (part, depth) => {
181
+ const { input, at: at$1 } = part;
182
+ if (typeof input !== "object" || input === null) throw problemAt(at$1, "A node of a policy must be an object");
183
+ spend(budget, 1, at$1);
184
+ counted(1, at$1);
185
+ const below = (value, ...parts) => child({
186
+ input: value,
187
+ at: at(at$1, ...parts)
188
+ }, depth + 1);
189
+ const tag = read(input, "_tag", at$1);
190
+ switch (tag) {
191
+ case "Rules": {
192
+ const algorithm = readString(input, "algorithm", at$1);
193
+ if (!isAlgorithm(algorithm)) throw problemAt(at(at$1, "algorithm"), `"${algorithm}" is not a combining algorithm`);
194
+ const list = readList(input, "rules", at$1, budget);
195
+ counted(list.length, at$1);
196
+ const found = Arr.map(list, (rule, index) => rules.push({
197
+ input: rule,
198
+ place: at(at$1, "rules", index)
199
+ }) - 1);
200
+ return R.succeed(sized(budget, {
201
+ _tag: "Rules",
202
+ place: at$1,
203
+ size: 1 + found.length,
204
+ algorithm,
205
+ rules: found
206
+ }));
207
+ }
208
+ case "All":
209
+ case "Any":
210
+ case "FirstOf": return R.map(R.forEach(readList(input, "policies", at$1, budget), (value, index) => below(value, "policies", index)), (policies) => sized(budget, {
190
211
  _tag: tag,
191
- place,
212
+ place: at$1,
192
213
  size: Arr.reduce(policies, 1, (total, plan) => total + plan.size),
193
214
  policies
194
- });
195
- }
196
- case "Ceiling": {
197
- const cap = child(read(input, "cap", path), "cap");
198
- const policy = child(read(input, "policy", path), "policy");
199
- return sized(path, {
200
- _tag: "Ceiling",
201
- place,
202
- size: 1 + cap.size + policy.size,
203
- cap,
204
- policy
205
- });
215
+ }));
216
+ case "Ceiling": {
217
+ const cap = read(input, "cap", at$1);
218
+ const policy = read(input, "policy", at$1);
219
+ return R.flatMap(below(cap, "cap"), (capPlan) => R.map(below(policy, "policy"), (policyPlan) => sized(budget, {
220
+ _tag: "Ceiling",
221
+ place: at$1,
222
+ size: 1 + capPlan.size + policyPlan.size,
223
+ cap: capPlan,
224
+ policy: policyPlan
225
+ })));
226
+ }
227
+ default: throw problemAt(at(at$1, "_tag"), "The node has no known tag");
206
228
  }
207
- default: throw problem([...path, "_tag"], "The node has no known tag");
208
- }
229
+ });
209
230
  };
210
231
  /** Stops a plan past `policyNodes`, with the message of the nodes. */
211
- const sized = (path, plan) => {
212
- if (plan.size > 1e5) throw problem(path, `A policy tree has ${String(plan.size)} nodes. The most is ${String(policyNodes)}.`);
232
+ const sized = (budget, plan) => {
233
+ const most = budget.limits.policyNodes;
234
+ if (plan.size > most) throw problemAt(plan.place, `A policy tree has ${String(plan.size)} nodes. The most is ${String(most)}.`);
213
235
  return plan;
214
236
  };
215
- /** Makes the nodes of a plan, with the decoded rules. */
216
- const build = (plan, rules) => {
217
- try {
218
- switch (plan._tag) {
219
- case "Rules": return new Rules({
220
- algorithm: plan.algorithm,
221
- rules: Arr.map(plan.rules, (index) => rules[index])
222
- });
223
- case "All":
224
- case "Any":
225
- case "FirstOf": return new policy_nodes_exports[plan._tag](Arr.map(plan.policies, (child) => build(child, rules)));
226
- case "Ceiling": return new Ceiling({
227
- cap: build(plan.cap, rules),
228
- policy: build(plan.policy, rules)
229
- });
237
+ /**
238
+ * Makes the nodes of a plan, with the decoded rules. It is a walk of the
239
+ * trampoline, so a deep plan does not fill the stack.
240
+ */
241
+ const build = (budget, rules) => adaptive((R, child) => (plan, depth) => {
242
+ const make = (made) => {
243
+ const node = (() => {
244
+ try {
245
+ return made();
246
+ } catch (error) {
247
+ if (error instanceof LimitExceeded) throw problemAt(plan.place, error.message);
248
+ throw error;
249
+ }
250
+ })();
251
+ const most = budget.limits.policyNodes;
252
+ if (node.size > most) throw problemAt(plan.place, `A policy tree has ${String(node.size)} nodes. The most is ${String(most)}.`);
253
+ return node;
254
+ };
255
+ switch (plan._tag) {
256
+ case "Rules": return R.succeed(make(() => new Rules({
257
+ algorithm: plan.algorithm,
258
+ rules: Arr.map(plan.rules, (index) => rules[index])
259
+ })));
260
+ case "All":
261
+ case "Any":
262
+ case "FirstOf": {
263
+ const tag = plan._tag;
264
+ return R.map(R.forEach(plan.policies, (inner) => child(inner, depth + 1)), (policies) => make(() => new policy_nodes_exports[tag](policies)));
230
265
  }
231
- } catch (error) {
232
- if (error instanceof LimitExceeded) throw problem(pathOf(plan.place), error.message);
233
- throw error;
266
+ case "Ceiling": return R.flatMap(child(plan.cap, depth + 1), (cap) => R.map(child(plan.policy, depth + 1), (policy) => make(() => new Ceiling({
267
+ cap,
268
+ policy
269
+ }))));
234
270
  }
235
- };
271
+ });
236
272
  /** Runs a step that may throw a problem, and puts the problem in the error. */
237
273
  const attempt = (step) => Effect.try({
238
274
  try: step,
@@ -247,12 +283,18 @@ const attempt = (step) => Effect.try({
247
283
  * makes the nodes last.
248
284
  */
249
285
  const decode = (input, domain) => Effect.gen(function* () {
250
- const budget = { used: 0 };
286
+ const budget = {
287
+ used: 0,
288
+ limits: domain.limits
289
+ };
251
290
  const inputs = [];
252
- const plan = yield* attempt(() => readTree(input, [], void 0, 1, budget, inputs));
291
+ const plan = yield* attempt(() => readTree(budget, inputs)({
292
+ input,
293
+ at: void 0
294
+ }));
253
295
  const metas = yield* Effect.forEach(inputs, (rule) => decodeMeta(rule, domain, budget));
254
296
  const rules = yield* attempt(() => Arr.map(inputs, (rule, index) => decodeRule(rule, metas[index], budget, domain)));
255
- return yield* attempt(() => build(plan, rules));
297
+ return yield* attempt(() => build(budget, rules)(plan));
256
298
  });
257
299
  export { decode, encode, syncMeta, toJson };
258
300
 
@@ -1 +1 @@
1
- {"version":3,"file":"policy-json.js","names":["PolicyNodes.rulesOf","Json.toJson","Json.readList","Json.problem","read","Json.read","Place.pathOf","Json.Problem","Json.decodeAt","Json.readString","Rule.make","Place.at","PolicyNodes.isAlgorithm","Limits.policyNodes","PolicyNodes.Rules","PolicyNodes","PolicyNodes.Ceiling","Limits.LimitExceeded"],"sources":["../../src/internal/policy-json.ts"],"sourcesContent":["/**\n * The JSON form of a policy tree. The decoder is a plain walk, as the decoder\n * of a condition is, and the two share one budget of `jsonNodes` objects and\n * items. It makes each node with its constructor, so the limits of the nodes\n * hold for a decoded tree too. The binding of the rules to the domain comes\n * after, in the Schema of the policy.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Domain from \"../Domain.ts\"\nimport * as Rule from \"./rule.ts\"\nimport * as Json from \"./json.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Place from \"./place.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\n\ntype Node = PolicyNodes.Node\n\n/**\n * The codec of the `meta` Schema of a domain to and from JSON. The JSON form\n * of a policy must stay the same through `JSON.stringify` and `JSON.parse`, so\n * a `Date` in `meta`, for one, becomes a string in it, and a string again\n * when it is decoded.\n */\nconst metaCodec = (meta: Schema.Top): Schema.Codec<unknown, unknown> =>\n\tSchema.toCodecJson(meta) as unknown as Schema.Codec<unknown, unknown>\n\n/** Gives the JSON form of the `meta` of a rule, or `undefined` for none. */\nexport type MetaJson = (rule: Rule.Rule) => unknown\n\n/**\n * Gives the JSON form of the `meta` of each rule at once, with the `meta`\n * Schema of a domain. `JSON.stringify` needs it at once, so the Schema must\n * not need an async step there.\n */\nexport const syncMeta =\n\t(meta: Schema.Top | undefined): MetaJson =>\n\t(rule) =>\n\t\trule.meta === undefined || meta === undefined\n\t\t\t? undefined\n\t\t\t: Schema.encodeUnknownSync(metaCodec(meta))(rule.meta)\n\n/** The JSON form of the condition of each rule, by the rule. */\ntype WhenJson = (rule: Rule.Rule) => unknown\n\n/** Gives the JSON form of a rule. The domain of a rule is not in it. */\nconst ruleJson = (\n\trule: Rule.Rule,\n\tmetaJson: MetaJson,\n\twhenJson: WhenJson,\n): unknown => {\n\tconst meta = metaJson(rule)\n\treturn {\n\t\t_tag: rule._tag,\n\t\tactions: rule.actions,\n\t\tresources: rule.resources,\n\t\t...(rule.when === undefined ? {} : { when: whenJson(rule) }),\n\t\t...(rule.fields === undefined ? {} : { fields: rule.fields }),\n\t\t...(rule.reason === undefined ? {} : { reason: rule.reason }),\n\t\t...(rule.id === undefined ? {} : { id: rule.id }),\n\t\t// The `meta` of a rule is a decoded value. Its JSON form comes from the\n\t\t// `meta` Schema of the domain.\n\t\t...(meta === undefined ? {} : { meta }),\n\t}\n}\n\n/**\n * Gives the JSON form of a policy tree with no predicate. A condition with a\n * predicate has no JSON form, so it throws an error. The conditions are\n * written first, from a flat list of the rules, so the walk does not go down\n * the levels of the tree and of a condition at once.\n */\nexport const toJson = (node: Node, meta: MetaJson): unknown => {\n\t// A native map, because it must find a rule by the rule itself: two rules\n\t// with the same data are `Equal`.\n\tconst whens = new Map<Rule.Rule, unknown>(\n\t\tArr.map(PolicyNodes.rulesOf(node), (rule) => [\n\t\t\trule,\n\t\t\trule.when === undefined ? undefined : Json.toJson(rule.when as never),\n\t\t]),\n\t)\n\tconst tree = (current: Node): unknown => {\n\t\tswitch (current._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: \"Rules\",\n\t\t\t\t\talgorithm: current.algorithm,\n\t\t\t\t\trules: Arr.map(current.rules, (rule) =>\n\t\t\t\t\t\truleJson(rule, meta, (found) => whens.get(found)),\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: current._tag,\n\t\t\t\t\tpolicies: Arr.map(current.policies, tree),\n\t\t\t\t}\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn {\n\t\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\t\tcap: tree(current.cap),\n\t\t\t\t\tpolicy: tree(current.policy),\n\t\t\t\t}\n\t\t}\n\t}\n\treturn tree(node)\n}\n\n/**\n * Gives the JSON form of a policy tree as an Effect. The `meta` Schema of the\n * domain may need an async step, so each `meta` is encoded as an Effect.\n */\nexport const encode = (\n\tnode: Node,\n\tmeta: Schema.Top | undefined,\n): Effect.Effect<unknown, Schema.SchemaError> =>\n\tEffect.gen(function* () {\n\t\tconst rules = Arr.filter(\n\t\t\tPolicyNodes.rulesOf(node),\n\t\t\t(rule) => rule.meta !== undefined,\n\t\t)\n\t\tconst encoded =\n\t\t\tmeta === undefined\n\t\t\t\t? []\n\t\t\t\t: yield* Effect.forEach(rules, (rule) =>\n\t\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t\tSchema.encodeUnknownEffect(metaCodec(meta))(rule.meta),\n\t\t\t\t\t\t\t(json) => [rule, json] as const,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t// A native map, because it must find a rule by the rule itself: two\n\t\t// rules with the same data are `Equal`, and each keeps its own form.\n\t\tconst forms = new Map<Rule.Rule, unknown>(encoded)\n\t\treturn toJson(node, (rule) => forms.get(rule))\n\t})\n\n/** Reads a list of strings. */\nconst readStrings = (\n\tinput: object,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tbudget: Json.Budget,\n): ReadonlyArray<string> =>\n\tArr.map(Json.readList(input, name, path, budget), (value, index) => {\n\t\tif (typeof value !== \"string\") {\n\t\t\tthrow Json.problem([...path, name, index], \"The item must be a string\")\n\t\t}\n\t\treturn value\n\t})\n\n/**\n * Reads a field that a rule may leave out. A field that the input has only\n * from the prototype of a class is not plain data, so it is a problem: a rule\n * that loses its condition would allow more.\n */\nconst readOptional = <A>(\n\tinput: object,\n\tname: string,\n\tpath: ReadonlyArray<PropertyKey>,\n\tread: () => A,\n): A | undefined => {\n\tif (Object.hasOwn(input, name)) {\n\t\treturn read()\n\t}\n\t// A read of the field throws a problem when the field is not data.\n\tJson.read(input, name, path)\n\treturn undefined\n}\n\n/** Counts the objects and items of a value, as the other parts count. */\nconst spendValue = (\n\tvalue: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tdepth: number,\n\tbudget: Json.Budget,\n): void => {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn\n\t}\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`The value has more than ${String(Limits.conditionDepth)} levels. The most is ${String(Limits.conditionDepth)}.`,\n\t\t)\n\t}\n\t// The length of an array counts before its items are read, so a sparse\n\t// array with a very large length stops at once.\n\tconst list = Array.isArray(value)\n\tif (list) {\n\t\tJson.spend(budget, 1 + value.length, path)\n\t}\n\tconst names = list\n\t\t? Array.from({ length: value.length }, (_, index) => String(index))\n\t\t: Object.keys(value)\n\tif (!list) {\n\t\tJson.spend(budget, 1 + names.length, path)\n\t}\n\tArr.forEach(names, (name) =>\n\t\tspendValue(\n\t\t\tJson.read(value, name, path),\n\t\t\t[...path, name],\n\t\t\tdepth + 1,\n\t\t\tbudget,\n\t\t),\n\t)\n}\n\n/**\n * A rule in the input, with its place, to decode after the tree. The place\n * shares the path of its parent: see `Place`.\n */\ninterface RuleInput {\n\treadonly input: unknown\n\treadonly place: Place.Place\n}\n\n/**\n * The shape of a tree, with the place of each rule in a flat list. `size`\n * counts the nodes and the rules, as the nodes do, so a shared input stops\n * at `policyNodes` before the decoder reads all of it.\n */\ntype Plan =\n\t| {\n\t\t\treadonly _tag: \"Rules\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly algorithm: PolicyNodes.Algorithm\n\t\t\treadonly rules: ReadonlyArray<number>\n\t }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly policies: ReadonlyArray<Plan>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly cap: Plan\n\t\t\treadonly policy: Plan\n\t }\n\n/**\n * Decodes the `meta` of a rule with the `meta` Schema of the domain, as an\n * Effect, since the Schema may need an async step. The Schema reads all of\n * the value, so its size counts in the budget.\n */\nconst decodeMeta = (\n\trule: RuleInput,\n\tdomain: Domain.Any,\n\tbudget: Json.Budget,\n): Effect.Effect<unknown, Json.Problem> => {\n\tconst { input } = rule\n\tconst path = Place.pathOf(rule.place)\n\treturn Effect.flatMap(\n\t\tattempt(() =>\n\t\t\ttypeof input !== \"object\" || input === null\n\t\t\t\t? undefined\n\t\t\t\t: readOptional(input, \"meta\", path, () => {\n\t\t\t\t\t\tconst value = Json.read(input, \"meta\", path)\n\t\t\t\t\t\tif (domain.meta === undefined) {\n\t\t\t\t\t\t\tthrow Json.problem(\n\t\t\t\t\t\t\t\t[...path, \"meta\"],\n\t\t\t\t\t\t\t\t\"The domain has no Schema for meta\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tspendValue(value, [...path, \"meta\"], 1, budget)\n\t\t\t\t\t\treturn { value, schema: domain.meta }\n\t\t\t\t\t}),\n\t\t),\n\t\t(found) =>\n\t\t\tfound === undefined\n\t\t\t\t? Effect.void\n\t\t\t\t: Effect.mapError(\n\t\t\t\t\t\tSchema.decodeUnknownEffect(metaCodec(found.schema))(found.value),\n\t\t\t\t\t\t(error) =>\n\t\t\t\t\t\t\tnew Json.Problem({\n\t\t\t\t\t\t\t\tpath: [...path, \"meta\"],\n\t\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\t\tissue: error.issue,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t)\n}\n\nconst decodeRule = (\n\trule: RuleInput,\n\tmeta: unknown,\n\tbudget: Json.Budget,\n\tdomain: Domain.Any,\n): Rule.Rule => {\n\tconst { input } = rule\n\tconst path = Place.pathOf(rule.place)\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problem(path, \"A rule must be an object\")\n\t}\n\tJson.spend(budget, 1, path)\n\tconst tag = Json.read(input, \"_tag\", path)\n\tif (tag !== \"Allow\" && tag !== \"Deny\") {\n\t\tthrow Json.problem([...path, \"_tag\"], 'A rule must be \"Allow\" or \"Deny\"')\n\t}\n\tconst when = readOptional(input, \"when\", path, () =>\n\t\tJson.decodeAt(Json.read(input, \"when\", path), [...path, \"when\"], budget),\n\t)\n\tconst fields = readOptional(input, \"fields\", path, () =>\n\t\treadStrings(input, \"fields\", path, budget),\n\t)\n\tconst reason = readOptional(input, \"reason\", path, () =>\n\t\tJson.readString(input, \"reason\", path),\n\t)\n\tconst id = readOptional(input, \"id\", path, () =>\n\t\tJson.readString(input, \"id\", path),\n\t)\n\t// Each rule gets the domain of the Schema, as a rule from the builder of\n\t// the domain has it.\n\treturn Rule.make({\n\t\t_tag: tag,\n\t\tactions: readStrings(input, \"actions\", path, budget),\n\t\tresources: readStrings(input, \"resources\", path, budget),\n\t\t...(when === undefined ? {} : { when: when as never }),\n\t\t...(fields === undefined ? {} : { fields }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(meta === undefined ? {} : { meta }),\n\t\tdomain,\n\t})\n}\n\n/**\n * Reads the shape of a tree, and puts each rule in `rules`. The rules are\n * decoded after the walk: a condition has levels of its own, and a decode\n * inside the walk would go down the levels of both. `path` is the path of the\n * node for a problem here, and `place` keeps it for later.\n */\nconst readTree = (\n\tinput: unknown,\n\tpath: ReadonlyArray<PropertyKey>,\n\tplace: Place.Place | undefined,\n\tdepth: number,\n\tbudget: Json.Budget,\n\t// The rules of one decode. The list belongs to that decode.\n\trules: Array<RuleInput>,\n): Plan => {\n\tif (depth > Limits.policyDepth) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`A policy tree has more than ${String(Limits.policyDepth)} levels. The most is ${String(Limits.policyDepth)}.`,\n\t\t)\n\t}\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problem(path, \"A node of a policy must be an object\")\n\t}\n\tJson.spend(budget, 1, path)\n\tconst child = (value: unknown, ...parts: ReadonlyArray<PropertyKey>) =>\n\t\treadTree(\n\t\t\tvalue,\n\t\t\t[...path, ...parts],\n\t\t\tPlace.at(place, ...parts),\n\t\t\tdepth + 1,\n\t\t\tbudget,\n\t\t\trules,\n\t\t)\n\tconst tag = Json.read(input, \"_tag\", path)\n\tswitch (tag) {\n\t\tcase \"Rules\": {\n\t\t\tconst algorithm = Json.readString(input, \"algorithm\", path)\n\t\t\tif (!PolicyNodes.isAlgorithm(algorithm)) {\n\t\t\t\tthrow Json.problem(\n\t\t\t\t\t[...path, \"algorithm\"],\n\t\t\t\t\t`\"${algorithm}\" is not a combining algorithm`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst found = Arr.map(\n\t\t\t\tJson.readList(input, \"rules\", path, budget),\n\t\t\t\t(rule, index) =>\n\t\t\t\t\trules.push({\n\t\t\t\t\t\tinput: rule,\n\t\t\t\t\t\tplace: Place.at(place, \"rules\", index),\n\t\t\t\t\t}) - 1,\n\t\t\t)\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: \"Rules\",\n\t\t\t\tplace,\n\t\t\t\tsize: 1 + found.length,\n\t\t\t\talgorithm,\n\t\t\t\trules: found,\n\t\t\t})\n\t\t}\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\": {\n\t\t\tconst policies = Arr.map(\n\t\t\t\tJson.readList(input, \"policies\", path, budget),\n\t\t\t\t(value, index) => child(value, \"policies\", index),\n\t\t\t)\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: tag,\n\t\t\t\tplace,\n\t\t\t\tsize: Arr.reduce(policies, 1, (total, plan) => total + plan.size),\n\t\t\t\tpolicies,\n\t\t\t})\n\t\t}\n\t\tcase \"Ceiling\": {\n\t\t\tconst cap = child(Json.read(input, \"cap\", path), \"cap\")\n\t\t\tconst policy = child(Json.read(input, \"policy\", path), \"policy\")\n\t\t\treturn sized(path, {\n\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\tplace,\n\t\t\t\tsize: 1 + cap.size + policy.size,\n\t\t\t\tcap,\n\t\t\t\tpolicy,\n\t\t\t})\n\t\t}\n\t\tdefault:\n\t\t\tthrow Json.problem([...path, \"_tag\"], \"The node has no known tag\")\n\t}\n}\n\n/** Stops a plan past `policyNodes`, with the message of the nodes. */\nconst sized = (path: ReadonlyArray<PropertyKey>, plan: Plan): Plan => {\n\tif (plan.size > Limits.policyNodes) {\n\t\tthrow Json.problem(\n\t\t\tpath,\n\t\t\t`A policy tree has ${String(plan.size)} nodes. The most is ${String(Limits.policyNodes)}.`,\n\t\t)\n\t}\n\treturn plan\n}\n\n/** Makes the nodes of a plan, with the decoded rules. */\nconst build = (plan: Plan, rules: ReadonlyArray<Rule.Rule>): Node => {\n\ttry {\n\t\tswitch (plan._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn new PolicyNodes.Rules({\n\t\t\t\t\talgorithm: plan.algorithm,\n\t\t\t\t\trules: Arr.map(plan.rules, (index) => rules[index]!),\n\t\t\t\t})\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn new PolicyNodes[plan._tag](\n\t\t\t\t\tArr.map(plan.policies, (child) => build(child, rules)),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn new PolicyNodes.Ceiling({\n\t\t\t\t\tcap: build(plan.cap, rules),\n\t\t\t\t\tpolicy: build(plan.policy, rules),\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\t// A node past a limit is a problem at its place.\n\t\tif (error instanceof Limits.LimitExceeded) {\n\t\t\tthrow Json.problem(Place.pathOf(plan.place), error.message)\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/** Runs a step that may throw a problem, and puts the problem in the error. */\nconst attempt = <A>(step: () => A): Effect.Effect<A, Json.Problem> =>\n\tEffect.try({\n\t\ttry: step,\n\t\tcatch: (error) => {\n\t\t\tif (error instanceof Json.Problem) {\n\t\t\t\treturn error\n\t\t\t}\n\t\t\tthrow error\n\t\t},\n\t})\n\n/**\n * Decodes the JSON form of a policy tree for a domain. It reads the tree\n * first, then the `meta` of each rule as an Effect, then the rules, and it\n * makes the nodes last.\n */\nexport const decode = (\n\tinput: unknown,\n\tdomain: Domain.Any,\n): Effect.Effect<Node, Json.Problem> =>\n\tEffect.gen(function* () {\n\t\tconst budget: Json.Budget = { used: 0 }\n\t\t// The rules of this decode. The list belongs to this decode.\n\t\tconst inputs: Array<RuleInput> = []\n\t\tconst plan = yield* attempt(() =>\n\t\t\treadTree(input, [], undefined, 1, budget, inputs),\n\t\t)\n\t\tconst metas = yield* Effect.forEach(inputs, (rule) =>\n\t\t\tdecodeMeta(rule, domain, budget),\n\t\t)\n\t\tconst rules = yield* attempt(() =>\n\t\t\tArr.map(inputs, (rule, index) =>\n\t\t\t\tdecodeRule(rule, metas[index], budget, domain),\n\t\t\t),\n\t\t)\n\t\treturn yield* attempt(() => build(plan, rules))\n\t})\n"],"mappings":"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"}
1
+ {"version":3,"file":"policy-json.js","names":["PolicyNodes.rulesOf","Json.toJson","Trampoline.run","Json.readList","at","Json.problemAt","Place.at","read","Trampoline.adaptive","Json.read","Json.Problem","Place.pathOf","Json.decodeAt","Json.readString","Rule.make","PolicyNodes.isAlgorithm","Limits.LimitExceeded","PolicyNodes.Rules","PolicyNodes","PolicyNodes.Ceiling"],"sources":["../../src/internal/policy-json.ts"],"sourcesContent":["/**\n * The JSON form of a policy tree. The decoder is a plain walk, as the decoder\n * of a condition is, and the two share one budget of `jsonNodes` objects and\n * items. It makes each node with its constructor, so the limits of the nodes\n * hold for a decoded tree too. The binding of the rules to the domain comes\n * after, in the Schema of the policy.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Domain from \"../Domain.ts\"\nimport * as Rule from \"./rule.ts\"\nimport * as Json from \"./json.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Place from \"./place.ts\"\nimport * as PolicyNodes from \"./policy-nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\ntype Node = PolicyNodes.Node\n\n/**\n * The codec of the `meta` Schema of a domain to and from JSON. The JSON form\n * of a policy must stay the same through `JSON.stringify` and `JSON.parse`, so\n * a `Date` in `meta`, for one, becomes a string in it, and a string again\n * when it is decoded.\n */\nconst metaCodec = (meta: Schema.Top): Schema.Codec<unknown, unknown> =>\n\tSchema.toCodecJson(meta) as unknown as Schema.Codec<unknown, unknown>\n\n/** Gives the JSON form of the `meta` of a rule, or `undefined` for none. */\nexport type MetaJson = (rule: Rule.Rule) => unknown\n\n/**\n * Gives the JSON form of the `meta` of each rule at once, with the `meta`\n * Schema of a domain. `JSON.stringify` needs it at once, so the Schema must\n * not need an async step there.\n */\nexport const syncMeta =\n\t(meta: Schema.Top | undefined): MetaJson =>\n\t(rule) =>\n\t\trule.meta === undefined || meta === undefined\n\t\t\t? undefined\n\t\t\t: Schema.encodeUnknownSync(metaCodec(meta))(rule.meta)\n\n/** The JSON form of the condition of each rule, by the rule. */\ntype WhenJson = (rule: Rule.Rule) => unknown\n\n/** Gives the JSON form of a rule. The domain of a rule is not in it. */\nconst ruleJson = (\n\trule: Rule.Rule,\n\tmetaJson: MetaJson,\n\twhenJson: WhenJson,\n): unknown => {\n\tconst meta = metaJson(rule)\n\treturn {\n\t\t_tag: rule._tag,\n\t\tactions: rule.actions,\n\t\tresources: rule.resources,\n\t\t...(rule.when === undefined ? {} : { when: whenJson(rule) }),\n\t\t...(rule.fields === undefined ? {} : { fields: rule.fields }),\n\t\t...(rule.reason === undefined ? {} : { reason: rule.reason }),\n\t\t...(rule.id === undefined ? {} : { id: rule.id }),\n\t\t// The `meta` of a rule is a decoded value. Its JSON form comes from the\n\t\t// `meta` Schema of the domain.\n\t\t...(meta === undefined ? {} : { meta }),\n\t}\n}\n\n/**\n * Gives the JSON form of a policy tree with no predicate. A condition with a\n * predicate has no JSON form, so it throws an error. The conditions are\n * written first, from a flat list of the rules, so the walk does not go down\n * the levels of the tree and of a condition at once.\n */\nexport const toJson = (node: Node, meta: MetaJson): unknown => {\n\t// A native map, because it must find a rule by the rule itself: two rules\n\t// with the same data are `Equal`.\n\tconst whens = new Map<Rule.Rule, unknown>(\n\t\tArr.map(PolicyNodes.rulesOf(node), (rule) => [\n\t\t\trule,\n\t\t\trule.when === undefined ? undefined : Json.toJson(rule.when as never),\n\t\t]),\n\t)\n\t// The walk of the tree takes the steps of a trampoline, so a deep tree\n\t// does not fill the stack.\n\treturn Trampoline.run(node.depth, (R) => {\n\t\tconst tree = (current: Node): Trampoline.Step<unknown> => {\n\t\t\tswitch (current._tag) {\n\t\t\t\tcase \"Rules\":\n\t\t\t\t\treturn R.succeed({\n\t\t\t\t\t\t_tag: \"Rules\",\n\t\t\t\t\t\talgorithm: current.algorithm,\n\t\t\t\t\t\trules: Arr.map(current.rules, (rule) =>\n\t\t\t\t\t\t\truleJson(rule, meta, (found) => whens.get(found)),\n\t\t\t\t\t\t),\n\t\t\t\t\t})\n\t\t\t\tcase \"All\":\n\t\t\t\tcase \"Any\":\n\t\t\t\tcase \"FirstOf\": {\n\t\t\t\t\tconst tag = current._tag\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(current.policies, (child) =>\n\t\t\t\t\t\t\tR.suspend(() => tree(child)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\t(policies) => ({ _tag: tag, policies }),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"Ceiling\":\n\t\t\t\t\treturn R.flatMap(\n\t\t\t\t\t\tR.suspend(() => tree(current.cap)),\n\t\t\t\t\t\t(cap) =>\n\t\t\t\t\t\t\tR.map(\n\t\t\t\t\t\t\t\tR.suspend(() => tree(current.policy)),\n\t\t\t\t\t\t\t\t(policy) => ({ _tag: \"Ceiling\", cap, policy }),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn tree(node)\n\t})\n}\n\n/**\n * Gives the JSON form of a policy tree as an Effect. The `meta` Schema of the\n * domain may need an async step, so each `meta` is encoded as an Effect.\n */\nexport const encode = (\n\tnode: Node,\n\tmeta: Schema.Top | undefined,\n): Effect.Effect<unknown, Schema.SchemaError> =>\n\tEffect.gen(function* () {\n\t\tconst rules = Arr.filter(\n\t\t\tPolicyNodes.rulesOf(node),\n\t\t\t(rule) => rule.meta !== undefined,\n\t\t)\n\t\tconst encoded =\n\t\t\tmeta === undefined\n\t\t\t\t? []\n\t\t\t\t: yield* Effect.forEach(rules, (rule) =>\n\t\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t\tSchema.encodeUnknownEffect(metaCodec(meta))(rule.meta),\n\t\t\t\t\t\t\t(json) => [rule, json] as const,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t// A native map, because it must find a rule by the rule itself: two\n\t\t// rules with the same data are `Equal`, and each keeps its own form.\n\t\tconst forms = new Map<Rule.Rule, unknown>(encoded)\n\t\treturn toJson(node, (rule) => forms.get(rule))\n\t})\n\n/** Reads a list of strings. */\nconst readStrings = (\n\tinput: object,\n\tname: string,\n\tat: Place.Place | undefined,\n\tbudget: Json.Budget,\n): ReadonlyArray<string> =>\n\tArr.map(Json.readList(input, name, at, budget), (value, index) => {\n\t\tif (typeof value !== \"string\") {\n\t\t\tthrow Json.problemAt(\n\t\t\t\tPlace.at(at, name, index),\n\t\t\t\t\"The item must be a string\",\n\t\t\t)\n\t\t}\n\t\treturn value\n\t})\n\n/**\n * Reads a field that a rule may leave out. A field that the input has only\n * from the prototype of a class is not plain data, so it is a problem: a rule\n * that loses its condition would allow more.\n */\nconst readOptional = <A>(\n\tinput: object,\n\tname: string,\n\tat: Place.Place | undefined,\n\tread: () => A,\n): A | undefined => {\n\tif (Object.hasOwn(input, name)) {\n\t\treturn read()\n\t}\n\t// A read of the field throws a problem when the field is not data.\n\tJson.read(input, name, at)\n\treturn undefined\n}\n\n/** A value of the input to count, with its place. */\ninterface Part {\n\treadonly value: unknown\n\treadonly at: Place.Place | undefined\n}\n\n/**\n * Counts the objects and items of a value, as the other parts count. It is a\n * walk of the trampoline, so a deep value does not fill the stack.\n */\nconst spendValue = (budget: Json.Budget): ((part: Part) => void) =>\n\tTrampoline.adaptive<Part, void>((R, child) => (part, depth) => {\n\t\tconst { value, at } = part\n\t\tif (typeof value !== \"object\" || value === null) {\n\t\t\treturn R.succeed(undefined)\n\t\t}\n\t\t// The length of an array counts before its items are read, so a sparse\n\t\t// array with a very large length stops at once.\n\t\tconst list = Array.isArray(value)\n\t\tif (list) {\n\t\t\tJson.spend(budget, 1 + value.length, at)\n\t\t}\n\t\tconst names = list\n\t\t\t? Array.from({ length: value.length }, (_, index) => String(index))\n\t\t\t: Object.keys(value)\n\t\tif (!list) {\n\t\t\tJson.spend(budget, 1 + names.length, at)\n\t\t}\n\t\treturn R.map(\n\t\t\tR.forEach(names, (name) =>\n\t\t\t\tchild(\n\t\t\t\t\t{ value: Json.read(value, name, at), at: Place.at(at, name) },\n\t\t\t\t\tdepth + 1,\n\t\t\t\t),\n\t\t\t),\n\t\t\t() => undefined,\n\t\t)\n\t})\n\n/**\n * A rule in the input, with its place, to decode after the tree. The place\n * shares the path of its parent: see `Place`.\n */\ninterface RuleInput {\n\treadonly input: unknown\n\treadonly place: Place.Place\n}\n\n/**\n * The shape of a tree, with the place of each rule in a flat list. `size`\n * counts the nodes and the rules, as the nodes do, so a shared input stops\n * at `policyNodes` before the decoder reads all of it.\n */\ntype Plan =\n\t| {\n\t\t\treadonly _tag: \"Rules\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly algorithm: PolicyNodes.Algorithm\n\t\t\treadonly rules: ReadonlyArray<number>\n\t }\n\t| {\n\t\t\treadonly _tag: \"All\" | \"Any\" | \"FirstOf\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly policies: ReadonlyArray<Plan>\n\t }\n\t| {\n\t\t\treadonly _tag: \"Ceiling\"\n\t\t\treadonly place: Place.Place | undefined\n\t\t\treadonly size: number\n\t\t\treadonly cap: Plan\n\t\t\treadonly policy: Plan\n\t }\n\n/**\n * Decodes the `meta` of a rule with the `meta` Schema of the domain, as an\n * Effect, since the Schema may need an async step. The Schema reads all of\n * the value, so its size counts in the budget.\n */\nconst decodeMeta = (\n\trule: RuleInput,\n\tdomain: Domain.Any,\n\tbudget: Json.Budget,\n): Effect.Effect<unknown, Json.Problem> => {\n\tconst { input, place } = rule\n\tconst at = Place.at(place, \"meta\")\n\treturn Effect.flatMap(\n\t\tattempt(() =>\n\t\t\ttypeof input !== \"object\" || input === null\n\t\t\t\t? undefined\n\t\t\t\t: readOptional(input, \"meta\", place, () => {\n\t\t\t\t\t\tconst value = Json.read(input, \"meta\", place)\n\t\t\t\t\t\tif (domain.meta === undefined) {\n\t\t\t\t\t\t\tthrow Json.problemAt(at, \"The domain has no Schema for meta\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tspendValue(budget)({ value, at })\n\t\t\t\t\t\treturn { value, schema: domain.meta }\n\t\t\t\t\t}),\n\t\t),\n\t\t(found) =>\n\t\t\tfound === undefined\n\t\t\t\t? Effect.void\n\t\t\t\t: Effect.mapError(\n\t\t\t\t\t\tSchema.decodeUnknownEffect(metaCodec(found.schema))(found.value),\n\t\t\t\t\t\t(error) =>\n\t\t\t\t\t\t\tnew Json.Problem({\n\t\t\t\t\t\t\t\tpath: Place.pathOf(at),\n\t\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\t\tissue: error.issue,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t)\n}\n\nconst decodeRule = (\n\trule: RuleInput,\n\tmeta: unknown,\n\tbudget: Json.Budget,\n\tdomain: Domain.Any,\n): Rule.Rule => {\n\tconst { input, place } = rule\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow Json.problemAt(place, \"A rule must be an object\")\n\t}\n\tJson.spend(budget, 1, place)\n\tconst tag = Json.read(input, \"_tag\", place)\n\tif (tag !== \"Allow\" && tag !== \"Deny\") {\n\t\tthrow Json.problemAt(\n\t\t\tPlace.at(place, \"_tag\"),\n\t\t\t'A rule must be \"Allow\" or \"Deny\"',\n\t\t)\n\t}\n\tconst when = readOptional(input, \"when\", place, () =>\n\t\tJson.decodeAt(\n\t\t\tJson.read(input, \"when\", place),\n\t\t\tPlace.at(place, \"when\"),\n\t\t\tbudget,\n\t\t),\n\t)\n\tconst fields = readOptional(input, \"fields\", place, () =>\n\t\treadStrings(input, \"fields\", place, budget),\n\t)\n\tconst reason = readOptional(input, \"reason\", place, () =>\n\t\tJson.readString(input, \"reason\", place),\n\t)\n\tconst id = readOptional(input, \"id\", place, () =>\n\t\tJson.readString(input, \"id\", place),\n\t)\n\t// Each rule gets the domain of the Schema, as a rule from the builder of\n\t// the domain has it.\n\treturn Rule.make({\n\t\t_tag: tag,\n\t\tactions: readStrings(input, \"actions\", place, budget),\n\t\tresources: readStrings(input, \"resources\", place, budget),\n\t\t...(when === undefined ? {} : { when: when as never }),\n\t\t...(fields === undefined ? {} : { fields }),\n\t\t...(reason === undefined ? {} : { reason }),\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(meta === undefined ? {} : { meta }),\n\t\tdomain,\n\t})\n}\n\n/** A node of the input of a tree, with its place. */\ninterface TreePart {\n\treadonly input: unknown\n\treadonly at: Place.Place | undefined\n}\n\n/**\n * Reads the shape of a tree, and puts each rule in `rules`. The rules are\n * decoded after the walk: a condition has levels of its own, and a decode\n * inside the walk would go down the levels of both. The walk is a walk of\n * the trampoline, so a deep tree does not fill the stack.\n */\nconst readTree = (\n\tbudget: Json.Budget,\n\t// The rules of one decode. The list belongs to that decode.\n\trules: Array<RuleInput>,\n): ((part: TreePart) => Plan) => {\n\t// The nodes and rules of the tree that the walk has read. A tree with more\n\t// of them than `policyNodes` does not load, so the walk stops at the first\n\t// one past it, and it does not read the rest of a very large input. The\n\t// count belongs to one decode.\n\tconst count = { nodes: 0 }\n\tconst counted = (more: number, at: Place.Place | undefined): void => {\n\t\tcount.nodes += more\n\t\tconst most = budget.limits.policyNodes\n\t\tif (count.nodes > most) {\n\t\t\tthrow Json.problemAt(\n\t\t\t\tat,\n\t\t\t\t`A policy tree has more than ${String(most)} nodes. The most is ${String(most)}.`,\n\t\t\t)\n\t\t}\n\t}\n\treturn Trampoline.adaptive<TreePart, Plan>((R, child) => (part, depth) => {\n\t\tconst { input, at } = part\n\t\tif (typeof input !== \"object\" || input === null) {\n\t\t\tthrow Json.problemAt(at, \"A node of a policy must be an object\")\n\t\t}\n\t\tJson.spend(budget, 1, at)\n\t\tcounted(1, at)\n\t\tconst below = (value: unknown, ...parts: ReadonlyArray<PropertyKey>) =>\n\t\t\tchild({ input: value, at: Place.at(at, ...parts) }, depth + 1)\n\t\tconst tag = Json.read(input, \"_tag\", at)\n\t\tswitch (tag) {\n\t\t\tcase \"Rules\": {\n\t\t\t\tconst algorithm = Json.readString(input, \"algorithm\", at)\n\t\t\t\tif (!PolicyNodes.isAlgorithm(algorithm)) {\n\t\t\t\t\tthrow Json.problemAt(\n\t\t\t\t\t\tPlace.at(at, \"algorithm\"),\n\t\t\t\t\t\t`\"${algorithm}\" is not a combining algorithm`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst list = Json.readList(input, \"rules\", at, budget)\n\t\t\t\tcounted(list.length, at)\n\t\t\t\tconst found = Arr.map(\n\t\t\t\t\tlist,\n\t\t\t\t\t(rule, index) =>\n\t\t\t\t\t\trules.push({\n\t\t\t\t\t\t\tinput: rule,\n\t\t\t\t\t\t\tplace: Place.at(at, \"rules\", index),\n\t\t\t\t\t\t}) - 1,\n\t\t\t\t)\n\t\t\t\treturn R.succeed(\n\t\t\t\t\tsized(budget, {\n\t\t\t\t\t\t_tag: \"Rules\",\n\t\t\t\t\t\tplace: at,\n\t\t\t\t\t\tsize: 1 + found.length,\n\t\t\t\t\t\talgorithm,\n\t\t\t\t\t\trules: found,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t}\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\":\n\t\t\t\treturn R.map(\n\t\t\t\t\tR.forEach(\n\t\t\t\t\t\tJson.readList(input, \"policies\", at, budget),\n\t\t\t\t\t\t(value, index) => below(value, \"policies\", index),\n\t\t\t\t\t),\n\t\t\t\t\t(policies) =>\n\t\t\t\t\t\tsized(budget, {\n\t\t\t\t\t\t\t_tag: tag,\n\t\t\t\t\t\t\tplace: at,\n\t\t\t\t\t\t\tsize: Arr.reduce(policies, 1, (total, plan) => total + plan.size),\n\t\t\t\t\t\t\tpolicies,\n\t\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\tcase \"Ceiling\": {\n\t\t\t\tconst cap = Json.read(input, \"cap\", at)\n\t\t\t\tconst policy = Json.read(input, \"policy\", at)\n\t\t\t\treturn R.flatMap(below(cap, \"cap\"), (capPlan) =>\n\t\t\t\t\tR.map(below(policy, \"policy\"), (policyPlan) =>\n\t\t\t\t\t\tsized(budget, {\n\t\t\t\t\t\t\t_tag: \"Ceiling\",\n\t\t\t\t\t\t\tplace: at,\n\t\t\t\t\t\t\tsize: 1 + capPlan.size + policyPlan.size,\n\t\t\t\t\t\t\tcap: capPlan,\n\t\t\t\t\t\t\tpolicy: policyPlan,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow Json.problemAt(Place.at(at, \"_tag\"), \"The node has no known tag\")\n\t\t}\n\t})\n}\n\n/** Stops a plan past `policyNodes`, with the message of the nodes. */\nconst sized = (budget: Json.Budget, plan: Plan): Plan => {\n\tconst most = budget.limits.policyNodes\n\tif (plan.size > most) {\n\t\tthrow Json.problemAt(\n\t\t\tplan.place,\n\t\t\t`A policy tree has ${String(plan.size)} nodes. The most is ${String(most)}.`,\n\t\t)\n\t}\n\treturn plan\n}\n\n/**\n * Makes the nodes of a plan, with the decoded rules. It is a walk of the\n * trampoline, so a deep plan does not fill the stack.\n */\nconst build = (\n\tbudget: Json.Budget,\n\trules: ReadonlyArray<Rule.Rule>,\n): ((plan: Plan) => Node) =>\n\tTrampoline.adaptive<Plan, Node>((R, child) => (plan, depth) => {\n\t\t// A node past a limit is a problem at its place. The constructor checks\n\t\t// the ceilings, and the decoder checks the limits of the domain, with\n\t\t// the nodes and values of the conditions.\n\t\tconst make = (made: () => Node): Node => {\n\t\t\tconst node = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn made()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof Limits.LimitExceeded) {\n\t\t\t\t\t\tthrow Json.problemAt(plan.place, error.message)\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t})()\n\t\t\tconst most = budget.limits.policyNodes\n\t\t\tif (node.size > most) {\n\t\t\t\tthrow Json.problemAt(\n\t\t\t\t\tplan.place,\n\t\t\t\t\t`A policy tree has ${String(node.size)} nodes. The most is ${String(most)}.`,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn node\n\t\t}\n\t\tswitch (plan._tag) {\n\t\t\tcase \"Rules\":\n\t\t\t\treturn R.succeed(\n\t\t\t\t\tmake(\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tnew PolicyNodes.Rules({\n\t\t\t\t\t\t\t\talgorithm: plan.algorithm,\n\t\t\t\t\t\t\t\trules: Arr.map(plan.rules, (index) => rules[index]!),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\tcase \"All\":\n\t\t\tcase \"Any\":\n\t\t\tcase \"FirstOf\": {\n\t\t\t\tconst tag = plan._tag\n\t\t\t\treturn R.map(\n\t\t\t\t\tR.forEach(plan.policies, (inner) => child(inner, depth + 1)),\n\t\t\t\t\t(policies) => make(() => new PolicyNodes[tag](policies)),\n\t\t\t\t)\n\t\t\t}\n\t\t\tcase \"Ceiling\":\n\t\t\t\treturn R.flatMap(child(plan.cap, depth + 1), (cap) =>\n\t\t\t\t\tR.map(child(plan.policy, depth + 1), (policy) =>\n\t\t\t\t\t\tmake(() => new PolicyNodes.Ceiling({ cap, policy })),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t}\n\t})\n\n/** Runs a step that may throw a problem, and puts the problem in the error. */\nconst attempt = <A>(step: () => A): Effect.Effect<A, Json.Problem> =>\n\tEffect.try({\n\t\ttry: step,\n\t\tcatch: (error) => {\n\t\t\tif (error instanceof Json.Problem) {\n\t\t\t\treturn error\n\t\t\t}\n\t\t\tthrow error\n\t\t},\n\t})\n\n/**\n * Decodes the JSON form of a policy tree for a domain. It reads the tree\n * first, then the `meta` of each rule as an Effect, then the rules, and it\n * makes the nodes last.\n */\nexport const decode = (\n\tinput: unknown,\n\tdomain: Domain.Any,\n): Effect.Effect<Node, Json.Problem> =>\n\tEffect.gen(function* () {\n\t\tconst budget: Json.Budget = { used: 0, limits: domain.limits }\n\t\t// The rules of this decode. The list belongs to this decode.\n\t\tconst inputs: Array<RuleInput> = []\n\t\tconst plan = yield* attempt(() =>\n\t\t\treadTree(budget, inputs)({ input, at: undefined }),\n\t\t)\n\t\tconst metas = yield* Effect.forEach(inputs, (rule) =>\n\t\t\tdecodeMeta(rule, domain, budget),\n\t\t)\n\t\tconst rules = yield* attempt(() =>\n\t\t\tArr.map(inputs, (rule, index) =>\n\t\t\t\tdecodeRule(rule, metas[index], budget, domain),\n\t\t\t),\n\t\t)\n\t\treturn yield* attempt(() => build(budget, rules)(plan))\n\t})\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"policy-nodes.d.ts","names":["Rule.Rule"],"sources":["../../src/internal/policy-nodes.ts"],"mappings":";;;;qBAmBa,WAAS,OAAA;YAOV,mBAAmB,UAAU;UAS/B;WACA;WACA;WACA;;;uBAmCK,aAAa,KAAK;;EAGhC,YAAY,eAAe,OAAO;;MAM9B;;MAKA;;MAKA;;;qBAaQ,cAAc;WACjB;WACQ,WAAW;WACX,OAAO,cAAcA;EACtC,YAAY;aACF,WAAW;aACX,OAAO,cAAcA;;;;qBA0CnB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,KAAK;WACL,QAAQ;EACzB,YAAY;aAAkB,KAAK;aAAe,QAAQ;;;;YAO/C,OAAO,QAAQ,MAAM,MAAM,UAAU"}
1
+ {"version":3,"file":"policy-nodes.d.ts","names":["Rule.Rule"],"sources":["../../src/internal/policy-nodes.ts"],"mappings":";;;;qBAoBa,WAAS,OAAA;YAOV,mBAAmB,UAAU;UAS/B;WACA;WACA;WACA;;;uBA4BK,aAAa,KAAK;;EAGhC,YAAY,eAAe,OAAO;;MAM9B;;MAKA;;MAKA;;;qBAaQ,cAAc;WACjB;WACQ,WAAW;WACX,OAAO,cAAcA;EACtC,YAAY;aACF,WAAW;aACX,OAAO,cAAcA;;;;qBA0CnB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,YAAY;WACf;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,UAAU,cAAc;EACzC,YAAY,UAAU,cAAc;;;qBAOxB,gBAAgB;WACnB;WACQ,KAAK;WACL,QAAQ;EACzB,YAAY;aAAkB,KAAK;aAAe,QAAQ;;;;YAO/C,OAAO,QAAQ,MAAM,MAAM,UAAU"}
@@ -1,10 +1,11 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
- import { exceeded, policyNodes } from "./limits.js";
2
+ import { ceilings, exceeded } from "./limits.js";
3
+ import { run } from "./trampoline.js";
3
4
  import { isNode as isNode$1 } from "./nodes.js";
4
5
  import { isRule } from "./rule.js";
6
+ import * as Arr from "effect/Array";
5
7
  import * as Schema from "effect/Schema";
6
8
  import * as Data from "effect/Data";
7
- import * as Arr from "effect/Array";
8
9
  // src/internal/policy-nodes.ts
9
10
  /**
10
11
  * The nodes of a policy tree. As with the nodes of a condition, each node is
@@ -42,8 +43,7 @@ const Algorithm = Schema.Literals([
42
43
  const isAlgorithm = (u) => Algorithm.literals.includes(u);
43
44
  /** Checks the facts of a node against the limits. */
44
45
  const checked = (facts) => {
45
- if (facts.size > 1e5) throw exceeded("A policy tree", "policyNodes", "nodes", facts.size, policyNodes);
46
- if (facts.depth > 256) throw exceeded("A policy tree", "policyDepth", "levels", facts.depth, 256);
46
+ if (facts.size > ceilings.policyNodes) throw exceeded("A policy tree", "policyNodes", "nodes", facts.size, ceilings.policyNodes);
47
47
  return facts;
48
48
  };
49
49
  const factsOf = (children) => checked({
@@ -141,15 +141,18 @@ const isNode = (u) => u instanceof Base;
141
141
  * into the condition of each rule takes the rules from this list first, so it
142
142
  * does not go down the levels of the tree and of a condition at once.
143
143
  */
144
- const rulesOf = (node) => {
145
- switch (node._tag) {
146
- case "Rules": return node.rules;
147
- case "All":
148
- case "Any":
149
- case "FirstOf": return Arr.flatMap(node.policies, rulesOf);
150
- case "Ceiling": return [...rulesOf(node.cap), ...rulesOf(node.policy)];
151
- }
152
- };
144
+ const rulesOf = (root) => run(root.depth, (R) => {
145
+ const go = (node) => {
146
+ switch (node._tag) {
147
+ case "Rules": return R.succeed(node.rules);
148
+ case "All":
149
+ case "Any":
150
+ case "FirstOf": return R.map(R.forEach(node.policies, (child) => R.suspend(() => go(child))), Arr.flatten);
151
+ case "Ceiling": return R.map(R.forEach([node.cap, node.policy], (child) => R.suspend(() => go(child))), Arr.flatten);
152
+ }
153
+ };
154
+ return go(root);
155
+ });
153
156
  export { Algorithm, All, Any, Ceiling, FirstOf, Rules, isAlgorithm, isNode, policy_nodes_exports, rulesOf };
154
157
 
155
158
  //# sourceMappingURL=policy-nodes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Limits.policyNodes","Nodes.isNode","Rule.isRule"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\tif (facts.size > Limits.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.policyNodes,\n\t\t)\n\t}\n\tif (facts.depth > Limits.policyDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyDepth\",\n\t\t\t\"levels\",\n\t\t\tfacts.depth,\n\t\t\tLimits.policyDepth,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t// A condition counts its nodes and the values of its tests.\n\t\t\t\t\t(total, when) => total + 1 + (when?.weight ?? 0),\n\t\t\t\t),\n\t\t\t\tdepth: 1,\n\t\t\t\topaque: Arr.some(conditions, (when) => when?.opaque ?? false),\n\t\t\t}),\n\t\t)\n\t}\n}\n\nconst checkChildren = (\n\tpolicies: ReadonlyArray<unknown>,\n): ReadonlyArray<Node> => {\n\tconst children = Array.from(policies)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow new Error(\"A child of a policy tree is not a node of a policy\")\n\t}\n\treturn Object.freeze(children)\n}\n\n/** Every policy must allow, and one deny is enough to deny. */\nexport class All extends Base {\n\treadonly _tag = \"All\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** One allow is enough. Without an allow, one deny gives a deny. */\nexport class Any extends Base {\n\treadonly _tag = \"Any\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** The first policy that allows or denies gives the answer. */\nexport class FirstOf extends Base {\n\treadonly _tag = \"FirstOf\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** A deny from `cap` always wins. An allow from `cap` does nothing. */\nexport class Ceiling extends Base {\n\treadonly _tag = \"Ceiling\"\n\tdeclare readonly cap: Node\n\tdeclare readonly policy: Node\n\tconstructor(props: { readonly cap: Node; readonly policy: Node }) {\n\t\tconst [cap, policy] = checkChildren([props.cap, props.policy])\n\t\tsuper({ cap, policy }, factsOf([cap!, policy!]))\n\t}\n}\n\n/** A node of a policy tree. */\nexport type Node = Rules | All | Any | FirstOf | Ceiling\n\n/** Tests if a value is a node of a policy tree. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\n/**\n * The rules of a tree, in order, once for each place. A walk that also goes\n * into the condition of each rule takes the rules from this list first, so it\n * does not go down the levels of the tree and of a condition at once.\n */\nexport const rulesOf = (node: Node): ReadonlyArray<Rule.Rule> => {\n\tswitch (node._tag) {\n\t\tcase \"Rules\":\n\t\t\treturn node.rules\n\t\tcase \"All\":\n\t\tcase \"Any\":\n\t\tcase \"FirstOf\":\n\t\t\treturn Arr.flatMap(node.policies, rulesOf)\n\t\tcase \"Ceiling\":\n\t\t\treturn [...rulesOf(node.cap), ...rulesOf(node.policy)]\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"policy-nodes.js","names":["Limits.exceeded","Nodes.isNode","Rule.isRule","Trampoline.run"],"sources":["../../src/internal/policy-nodes.ts"],"sourcesContent":["/**\n * The nodes of a policy tree. As with the nodes of a condition, each node is\n * a class, only its constructor makes one, and it works out three facts once:\n * - `size`: the number of nodes, with each rule and each node of its condition,\n * and a shared child counted for each place;\n * - `depth`: the number of levels of the tree;\n * - `opaque`: a rule of the tree has a predicate.\n *\n * The constructor stops a tree past `policyNodes` or `policyDepth`. So a walk\n * over any tree, such as a decision, needs no memo.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport * as Schema from \"effect/Schema\"\nimport * as Rule from \"./rule.ts\"\nimport * as Limits from \"./limits.ts\"\nimport * as Nodes from \"./nodes.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** The combining algorithms of a list of rules. The decoder reads this list too. */\nexport const Algorithm = Schema.Literals([\n\t\"denyOverrides\",\n\t\"permitOverrides\",\n\t\"firstApplicable\",\n\t\"lastApplicable\",\n])\n\nexport type Algorithm = typeof Algorithm.Type\n\n/**\n * Tests if a value is a combining algorithm. It reads the list of the Schema\n * with `includes`, as `Nodes.isCompareOperator` does.\n */\nexport const isAlgorithm = (u: unknown): u is Algorithm =>\n\t(Algorithm.literals as ReadonlyArray<unknown>).includes(u)\n\ninterface Facts {\n\treadonly size: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\n/** Checks the facts of a node against the limits. */\nconst checked = (facts: Facts): Facts => {\n\t// A node does not know its domain, so it checks the ceiling. A policy\n\t// checks the limit of its domain.\n\tif (facts.size > Limits.ceilings.policyNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A policy tree\",\n\t\t\t\"policyNodes\",\n\t\t\t\"nodes\",\n\t\t\tfacts.size,\n\t\t\tLimits.ceilings.policyNodes,\n\t\t)\n\t}\n\treturn facts\n}\n\nconst factsOf = (children: ReadonlyArray<Node>): Facts =>\n\tchecked({\n\t\tsize: Arr.reduce(children, 1, (total, child) => total + child.size),\n\t\tdepth:\n\t\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t})\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\treadonly #facts: Facts\n\n\tconstructor(props: object, facts: Facts) {\n\t\tsuper(props as never)\n\t\tthis.#facts = facts\n\t}\n\n\t/** The number of nodes, with the rules and their conditions. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/** The number of levels of the tree. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** A rule of the tree has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/**\n * The condition of a rule. A value that is not a node counts as one node\n * here: the binding of the rule gives the issue for it, before any walk.\n */\nconst whenOf = (rule: Rule.Rule): Nodes.Node | undefined =>\n\tNodes.isNode(rule.when) ? rule.when : undefined\n\n/** A list of rules with a combining algorithm. */\nexport class Rules extends Base {\n\treadonly _tag = \"Rules\"\n\tdeclare readonly algorithm: Algorithm\n\tdeclare readonly rules: ReadonlyArray<Rule.Rule>\n\tconstructor(props: {\n\t\treadonly algorithm: Algorithm\n\t\treadonly rules: ReadonlyArray<Rule.Rule>\n\t}) {\n\t\tif (!isAlgorithm(props.algorithm)) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${String(props.algorithm)}\" is not a combining algorithm`,\n\t\t\t)\n\t\t}\n\t\tconst rules = Object.freeze(Array.from(props.rules))\n\t\t// A value that is not a rule, such as a copy of a rule made with a\n\t\t// spread, may keep the type fields of another rule, and it does not\n\t\t// work in a generator. It is a mistake in the code, so it is a defect.\n\t\tif (!Arr.every(rules, Rule.isRule)) {\n\t\t\tthrow new Error(\"A value in the list is not a rule\")\n\t\t}\n\t\tconst conditions = Arr.map(rules, whenOf)\n\t\tsuper(\n\t\t\t{ algorithm: props.algorithm, rules },\n\t\t\tchecked({\n\t\t\t\tsize: Arr.reduce(\n\t\t\t\t\tconditions,\n\t\t\t\t\t1,\n\t\t\t\t\t// A condition counts its nodes and the values of its tests.\n\t\t\t\t\t(total, when) => total + 1 + (when?.weight ?? 0),\n\t\t\t\t),\n\t\t\t\tdepth: 1,\n\t\t\t\topaque: Arr.some(conditions, (when) => when?.opaque ?? false),\n\t\t\t}),\n\t\t)\n\t}\n}\n\nconst checkChildren = (\n\tpolicies: ReadonlyArray<unknown>,\n): ReadonlyArray<Node> => {\n\tconst children = Array.from(policies)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow new Error(\"A child of a policy tree is not a node of a policy\")\n\t}\n\treturn Object.freeze(children)\n}\n\n/** Every policy must allow, and one deny is enough to deny. */\nexport class All extends Base {\n\treadonly _tag = \"All\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** One allow is enough. Without an allow, one deny gives a deny. */\nexport class Any extends Base {\n\treadonly _tag = \"Any\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** The first policy that allows or denies gives the answer. */\nexport class FirstOf extends Base {\n\treadonly _tag = \"FirstOf\"\n\tdeclare readonly policies: ReadonlyArray<Node>\n\tconstructor(policies: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(policies)\n\t\tsuper({ policies: children }, factsOf(children))\n\t}\n}\n\n/** A deny from `cap` always wins. An allow from `cap` does nothing. */\nexport class Ceiling extends Base {\n\treadonly _tag = \"Ceiling\"\n\tdeclare readonly cap: Node\n\tdeclare readonly policy: Node\n\tconstructor(props: { readonly cap: Node; readonly policy: Node }) {\n\t\tconst [cap, policy] = checkChildren([props.cap, props.policy])\n\t\tsuper({ cap, policy }, factsOf([cap!, policy!]))\n\t}\n}\n\n/** A node of a policy tree. */\nexport type Node = Rules | All | Any | FirstOf | Ceiling\n\n/** Tests if a value is a node of a policy tree. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\n/**\n * The rules of a tree, in order, once for each place. A walk that also goes\n * into the condition of each rule takes the rules from this list first, so it\n * does not go down the levels of the tree and of a condition at once.\n */\nexport const rulesOf = (root: Node): ReadonlyArray<Rule.Rule> =>\n\t// The walk takes the steps of a trampoline, so a deep tree does not fill\n\t// the stack.\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<ReadonlyArray<Rule.Rule>> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"Rules\":\n\t\t\t\t\treturn R.succeed(node.rules)\n\t\t\t\tcase \"All\":\n\t\t\t\tcase \"Any\":\n\t\t\t\tcase \"FirstOf\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.policies, (child) => R.suspend(() => go(child))),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t\tcase \"Ceiling\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach([node.cap, node.policy], (child) =>\n\t\t\t\t\t\t\tR.suspend(() => go(child)),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tArr.flatten,\n\t\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,YAAY,OAAO,SAAS;CACxC;CACA;CACA;CACA;AACD,CAAC;;;;;AAQD,MAAa,eAAe,MAC1B,UAAU,SAAoC,SAAS,CAAC;;AAS1D,MAAM,WAAW,UAAwB;CAGxC,IAAI,MAAM,OAAA,SAAuB,aAChC,MAAMA,SACL,iBACA,eACA,SACA,MAAM,MAAA,SACU,WACjB;CAED,OAAO;AACR;AAEA,MAAM,WAAW,aAChB,QAAQ;CACP,MAAM,IAAI,OAAO,UAAU,IAAI,OAAO,UAAU,QAAQ,MAAM,IAAI;CAClE,OACC,IAAI,IAAI,OAAO,UAAU,IAAI,KAAK,UAAU,KAAK,IAAI,KAAK,MAAM,KAAK,CAAC;CACvE,QAAQ,IAAI,KAAK,WAAW,UAAU,MAAM,MAAM;AACnD,CAAC;;AAGF,IAAe,OAAf,cAA4B,KAAK,MAAM;CACtC;CAEA,YAAY,OAAe,OAAc;EACxC,MAAM,KAAc;EACpB,KAAK,SAAS;CACf;;CAGA,IAAI,OAAe;EAClB,OAAO,KAAK,OAAO;CACpB;;CAGA,IAAI,QAAgB;EACnB,OAAO,KAAK,OAAO;CACpB;;CAGA,IAAI,SAAkB;EACrB,OAAO,KAAK,OAAO;CACpB;AACD;;;;;AAMA,MAAM,UAAU,SACfC,SAAa,KAAK,IAAI,IAAI,KAAK,OAAO,KAAA;;AAGvC,IAAa,QAAb,cAA2B,KAAK;CAC/B,OAAgB;CAGhB,YAAY,OAGT;EACF,IAAI,CAAC,YAAY,MAAM,SAAS,GAC/B,MAAM,IAAI,MACT,IAAI,OAAO,MAAM,SAAS,EAAE,+BAC7B;EAED,MAAM,QAAQ,OAAO,OAAO,MAAM,KAAK,MAAM,KAAK,CAAC;EAInD,IAAI,CAAC,IAAI,MAAM,OAAOC,MAAW,GAChC,MAAM,IAAI,MAAM,mCAAmC;EAEpD,MAAM,aAAa,IAAI,IAAI,OAAO,MAAM;EACxC,MACC;GAAE,WAAW,MAAM;GAAW;EAAM,GACpC,QAAQ;GACP,MAAM,IAAI,OACT,YACA,IAEC,OAAO,SAAS,QAAQ,KAAK,MAAM,UAAU,EAC/C;GACA,OAAO;GACP,QAAQ,IAAI,KAAK,aAAa,SAAS,MAAM,UAAU,KAAK;EAC7D,CAAC,CACF;CACD;AACD;AAEA,MAAM,iBACL,aACyB;CACzB,MAAM,WAAW,MAAM,KAAK,QAAQ;CACpC,IAAI,CAAC,IAAI,MAAM,UAAU,MAAM,GAC9B,MAAM,IAAI,MAAM,oDAAoD;CAErE,OAAO,OAAO,OAAO,QAAQ;AAC9B;;AAGA,IAAa,MAAb,cAAyB,KAAK;CAC7B,OAAgB;CAEhB,YAAY,UAA+B;EAC1C,MAAM,WAAW,cAAc,QAAQ;EACvC,MAAM,EAAE,UAAU,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAChD;AACD;;AAGA,IAAa,MAAb,cAAyB,KAAK;CAC7B,OAAgB;CAEhB,YAAY,UAA+B;EAC1C,MAAM,WAAW,cAAc,QAAQ;EACvC,MAAM,EAAE,UAAU,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAChD;AACD;;AAGA,IAAa,UAAb,cAA6B,KAAK;CACjC,OAAgB;CAEhB,YAAY,UAA+B;EAC1C,MAAM,WAAW,cAAc,QAAQ;EACvC,MAAM,EAAE,UAAU,SAAS,GAAG,QAAQ,QAAQ,CAAC;CAChD;AACD;;AAGA,IAAa,UAAb,cAA6B,KAAK;CACjC,OAAgB;CAGhB,YAAY,OAAsD;EACjE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAM,KAAK,MAAM,MAAM,CAAC;EAC7D,MAAM;GAAE;GAAK;EAAO,GAAG,QAAQ,CAAC,KAAM,MAAO,CAAC,CAAC;CAChD;AACD;;AAMA,MAAa,UAAU,MAA0B,aAAa;;;;;;AAO9D,MAAa,WAAW,SAGvBC,IAAe,KAAK,QAAQ,MAAM;CACjC,MAAM,MAAM,SAA0D;EACrE,QAAQ,KAAK,MAAb;GACC,KAAK,SACJ,OAAO,EAAE,QAAQ,KAAK,KAAK;GAC5B,KAAK;GACL,KAAK;GACL,KAAK,WACJ,OAAO,EAAE,IACR,EAAE,QAAQ,KAAK,WAAW,UAAU,EAAE,cAAc,GAAG,KAAK,CAAC,CAAC,GAC9D,IAAI,OACL;GACD,KAAK,WACJ,OAAO,EAAE,IACR,EAAE,QAAQ,CAAC,KAAK,KAAK,KAAK,MAAM,IAAI,UACnC,EAAE,cAAc,GAAG,KAAK,CAAC,CAC1B,GACA,IAAI,OACL;EACF;CACD;CACA,OAAO,GAAG,IAAI;AACf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"policy.d.ts","names":["Policy.Any","Policy.Node","Domain.Any"],"sources":["../../src/internal/policy.ts"],"mappings":";;;;;;KAsBK,MAAMA;KACN,OAAOC;;;;;qBAOC,WACZ,UAAUC,aACV,mBACS,KAAK;WACL,QAAQ;WACR,MAAM;;;;4BAOmB;;;;;uBAKL,QAAQ,MAAM;EAE3C;;SAKO,GAAG,aAAa,KAAK;;;;;;;;;;;;;qBAehB,oBAAoB"}
1
+ {"version":3,"file":"policy.d.ts","names":["Policy.Any","Policy.Node","Domain.Any"],"sources":["../../src/internal/policy.ts"],"mappings":";;;;;;KAwBK,MAAMA;KACN,OAAOC;;;;;qBAOC,WACZ,UAAUC,aACV,mBACS,KAAK;WACL,QAAQ;WACR,MAAM;;;;4BAOmB;;;;;uBAKL,QAAQ,MAAM;EAE3C;;SAKO,GAAG,aAAa,KAAK;;;;;;;;;;;;;qBA+BhB,oBAAoB"}