@nmnmcc/ability 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +143 -87
  2. package/dist/Authorizer.d.ts +43 -47
  3. package/dist/Authorizer.d.ts.map +1 -1
  4. package/dist/Authorizer.js +5 -290
  5. package/dist/Authorizer.js.map +1 -1
  6. package/dist/Condition.d.ts +164 -332
  7. package/dist/Condition.d.ts.map +1 -1
  8. package/dist/Condition.js +75 -890
  9. package/dist/Condition.js.map +1 -1
  10. package/dist/Decision.d.ts +13 -13
  11. package/dist/Decision.d.ts.map +1 -1
  12. package/dist/Decision.js.map +1 -1
  13. package/dist/Domain.d.ts +10 -21
  14. package/dist/Domain.d.ts.map +1 -1
  15. package/dist/Domain.js +4 -62
  16. package/dist/Domain.js.map +1 -1
  17. package/dist/HttpApi.d.ts +31 -7
  18. package/dist/HttpApi.d.ts.map +1 -1
  19. package/dist/HttpApi.js +28 -11
  20. package/dist/HttpApi.js.map +1 -1
  21. package/dist/Limits.d.ts +114 -0
  22. package/dist/Limits.d.ts.map +1 -0
  23. package/dist/Limits.js +132 -0
  24. package/dist/Limits.js.map +1 -0
  25. package/dist/Policy.d.ts +274 -272
  26. package/dist/Policy.d.ts.map +1 -1
  27. package/dist/Policy.js +71 -1180
  28. package/dist/Policy.js.map +1 -1
  29. package/dist/Resource.d.ts +8 -181
  30. package/dist/Resource.d.ts.map +1 -1
  31. package/dist/Resource.js +8 -807
  32. package/dist/Resource.js.map +1 -1
  33. package/dist/index.d.ts +3 -4
  34. package/dist/index.js +4 -5
  35. package/dist/internal/algebra.js +267 -0
  36. package/dist/internal/algebra.js.map +1 -0
  37. package/dist/internal/analysis.js +245 -0
  38. package/dist/internal/analysis.js.map +1 -0
  39. package/dist/internal/authorizer.d.ts +30 -0
  40. package/dist/internal/authorizer.d.ts.map +1 -0
  41. package/dist/internal/authorizer.js +221 -0
  42. package/dist/internal/authorizer.js.map +1 -0
  43. package/dist/internal/bind.js +286 -0
  44. package/dist/internal/bind.js.map +1 -0
  45. package/dist/internal/catalog.d.ts +56 -0
  46. package/dist/internal/catalog.d.ts.map +1 -0
  47. package/dist/internal/catalog.js +292 -0
  48. package/dist/internal/catalog.js.map +1 -0
  49. package/dist/internal/decide.js +166 -0
  50. package/dist/internal/decide.js.map +1 -0
  51. package/dist/internal/domain.js +32 -0
  52. package/dist/internal/domain.js.map +1 -0
  53. package/dist/internal/json.js +134 -0
  54. package/dist/internal/json.js.map +1 -0
  55. package/dist/internal/limits.d.ts +14 -0
  56. package/dist/internal/limits.d.ts.map +1 -0
  57. package/dist/internal/limits.js +43 -0
  58. package/dist/internal/limits.js.map +1 -0
  59. package/dist/internal/nodes.d.ts +121 -0
  60. package/dist/internal/nodes.d.ts.map +1 -0
  61. package/dist/internal/nodes.js +277 -0
  62. package/dist/internal/nodes.js.map +1 -0
  63. package/dist/internal/patterns.js +110 -0
  64. package/dist/internal/patterns.js.map +1 -0
  65. package/dist/internal/policy-json.js +255 -0
  66. package/dist/internal/policy-json.js.map +1 -0
  67. package/dist/internal/policy-nodes.d.ts +61 -0
  68. package/dist/internal/policy-nodes.d.ts.map +1 -0
  69. package/dist/internal/policy-nodes.js +146 -0
  70. package/dist/internal/policy-nodes.js.map +1 -0
  71. package/dist/internal/policy.d.ts +42 -0
  72. package/dist/internal/policy.d.ts.map +1 -0
  73. package/dist/internal/policy.js +124 -0
  74. package/dist/internal/policy.js.map +1 -0
  75. package/dist/internal/read.js +39 -0
  76. package/dist/internal/read.js.map +1 -0
  77. package/dist/internal/resource.js +115 -0
  78. package/dist/internal/resource.js.map +1 -0
  79. package/dist/internal/rule.d.ts +26 -0
  80. package/dist/internal/rule.d.ts.map +1 -0
  81. package/dist/internal/rule.js +93 -0
  82. package/dist/internal/rule.js.map +1 -0
  83. package/dist/internal/search.d.ts +22 -0
  84. package/dist/internal/search.d.ts.map +1 -0
  85. package/dist/internal/search.js +274 -0
  86. package/dist/internal/search.js.map +1 -0
  87. package/dist/internal/suggest.js +46 -0
  88. package/dist/internal/suggest.js.map +1 -0
  89. package/package.json +4 -5
  90. package/src/Authorizer.ts +69 -585
  91. package/src/Condition.ts +339 -2095
  92. package/src/Decision.ts +6 -5
  93. package/src/Domain.ts +13 -70
  94. package/src/HttpApi.ts +75 -13
  95. package/src/Limits.ts +132 -0
  96. package/src/Policy.ts +386 -2772
  97. package/src/Resource.ts +14 -1555
  98. package/src/index.ts +3 -9
  99. package/src/internal/algebra.ts +482 -0
  100. package/src/internal/analysis.ts +478 -0
  101. package/src/internal/authorizer.ts +418 -0
  102. package/src/internal/bind.ts +596 -0
  103. package/src/internal/catalog.ts +534 -0
  104. package/src/internal/decide.ts +402 -0
  105. package/src/internal/domain.ts +66 -0
  106. package/src/internal/json.ts +260 -0
  107. package/src/internal/limits.ts +78 -0
  108. package/src/internal/nodes.ts +404 -0
  109. package/src/internal/patterns.ts +211 -0
  110. package/src/internal/policy-json.ts +484 -0
  111. package/src/internal/policy-nodes.ts +207 -0
  112. package/src/internal/policy.ts +264 -0
  113. package/src/internal/read.ts +55 -0
  114. package/src/internal/resource.ts +180 -0
  115. package/src/internal/rule.ts +181 -0
  116. package/src/internal/search.ts +541 -0
  117. package/src/internal/suggest.ts +104 -0
  118. package/dist/Principal.d.ts +0 -35
  119. package/dist/Principal.d.ts.map +0 -1
  120. package/dist/Principal.js +0 -37
  121. package/dist/Principal.js.map +0 -1
  122. package/dist/Rule.d.ts +0 -275
  123. package/dist/Rule.d.ts.map +0 -1
  124. package/dist/Rule.js +0 -257
  125. package/dist/Rule.js.map +0 -1
  126. package/src/Principal.ts +0 -41
  127. package/src/Rule.ts +0 -683
package/dist/Resource.js CHANGED
@@ -1,760 +1,11 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
- import * as Arr from "effect/Array";
3
- import * as Option from "effect/Option";
4
- import * as Schema from "effect/Schema";
5
- import { Class as Class$1 } from "effect/Data";
6
- import * as HashMap from "effect/HashMap";
7
- import * as HashSet from "effect/HashSet";
8
- import { hasProperty, isObject } from "effect/Predicate";
9
- import * as SchemaAST from "effect/SchemaAST";
2
+ import { makeClass, makeStruct } from "./internal/resource.js";
10
3
  // src/Resource.ts
11
- /**
12
- * A resource is a kind of thing that a person may act on, such as a Bot or a
13
- * document. Each resource has a kind name, a list of actions, a Schema for its
14
- * data, and an optional path.
15
- *
16
- * Make a resource in one of two ways:
17
- * - {@link Class} makes a Schema class, in the same way as
18
- * `Schema.TaggedClass`. The class is a Schema, a constructor, and a resource
19
- * at the same time.
20
- * - {@link make} makes a resource from plain fields. Its values are plain
21
- * objects with a `_tag` field.
22
- *
23
- * Every value of a resource has a `_tag` field with the kind name. The library
24
- * uses this field to find the resource of a value. It does not guess from the
25
- * constructor name.
26
- *
27
- * A path puts a resource inside a tree, such as
28
- * `["org", organizationId, "user", ownerUserId, "bot", id]`. A condition can
29
- * then ask if a value is under a part of the tree. See `Condition.under`.
30
- *
31
- * @stability stable
32
- * @since 0.3.0
33
- */
34
4
  var Resource_exports = /* @__PURE__ */ __exportAll({
35
5
  Class: () => Class,
36
- anyDomain: () => anyDomain,
37
- descriptor: () => descriptor,
38
- domainOf: () => domainOf,
39
- fieldPaths: () => fieldPaths,
40
- hasGetter: () => hasGetter,
41
- hasMatchingGetter: () => hasMatchingGetter,
42
- hasMatchingPath: () => hasMatchingPath,
43
- hasNoValue: () => hasNoValue,
44
- hasPath: () => hasPath,
45
- hasValue: () => hasValue,
46
- isPlainField: () => isPlainField,
47
- isResource: () => isResource,
48
- kindOf: () => kindOf,
49
6
  make: () => make
50
7
  });
51
8
  /**
52
- * The class of the runtime data of a resource. {@link isResource} uses it to
53
- * know a resource.
54
- */
55
- var ResourceDescriptor = class extends Class$1 {};
56
- /** The prototypes from `prototype` up, below `Object.prototype`. */
57
- const prototypesFrom = (prototype) => Arr.unfold(prototype, (current) => typeof current === "object" && current !== null && current !== Object.prototype ? Option.some([current, Object.getPrototypeOf(current)]) : Option.none());
58
- /**
59
- * Gives the prototype of the class of a class node, or `undefined`. The node
60
- * does not keep the class: a class Schema with an annotation or a check, or
61
- * one inside `Schema.suspend`, is a new Schema with the same node. So the
62
- * walk asks the node to make a value with no check, and reads its prototype.
63
- *
64
- * This uses the `"~constructor"` annotation of Effect, which is not a public
65
- * API: it gives a link whose decode makes a value of the class from its
66
- * fields without a check. When that is not so, the walk gives `undefined`,
67
- * and the check of a policy says no to the getter.
68
- */
69
- const classPrototype = (ast) => {
70
- const make = ast.annotations?.["~constructor"];
71
- if (typeof make !== "function") return;
72
- try {
73
- const made = make(ast.typeParameters);
74
- const decode = hasProperty(made, "link") && hasProperty(made.link, "transformation") && hasProperty(made.link.transformation, "decode") ? made.link.transformation.decode : void 0;
75
- const value = hasProperty(decode, "transform") && typeof decode.transform === "function" ? decode.transform({}) : void 0;
76
- return isObject(value) ? Object.getPrototypeOf(value) : void 0;
77
- } catch {
78
- return;
79
- }
80
- };
81
- /**
82
- * Tests if a path names a getter of a class Schema, such as a getter of a
83
- * `Schema.Class` or a `Principal.Class`, at its root or in a nested field. A
84
- * condition reads such a field, and the types of a condition take it, so the
85
- * check of a policy takes it too. It goes into the fields of a struct, the
86
- * values of a record, the members of a union, `Schema.suspend`, and a class
87
- * with an annotation or a check.
88
- *
89
- * @stability stable
90
- * @category predicates
91
- * @since 1.0.0
92
- */
93
- const hasGetter = (schema, path) => !isTooLong(path) && Schema.isSchema(schema) && getterAt(schema.ast, path.split("."), 0, (part, name) => part === name, /* @__PURE__ */ new Map());
94
- /**
95
- * The most parts that a field path may have. The walks below call themselves
96
- * for each part, and a path comes from the input of a policy. So a very long
97
- * path, as in a recursive Schema, could fill the stack.
98
- */
99
- const maxPathParts = 256;
100
- /** Tests if a field path has more than {@link maxPathParts} parts. */
101
- const isTooLong = (path) => path.split(".", 257).length > maxPathParts;
102
- /**
103
- * Tests if a Schema has a getter of a class at a path whose parts fit
104
- * `parts`, as `hasGetter` does for one path. A part fits a name when `fits`
105
- * says so, as a part with `*` fits many names. A part `**` stands for any
106
- * number of parts, also none.
107
- *
108
- * @stability stable
109
- * @category predicates
110
- * @since 1.0.0
111
- */
112
- const hasMatchingGetter = (schema, parts, fits) => parts.length <= maxPathParts && Schema.isSchema(schema) && getterAt(schema.ast, parts, 0, fits, /* @__PURE__ */ new Map());
113
- /** The names of the getters of a prototype and the prototypes above it. */
114
- const getterNames = (prototype) => Arr.flatMap(prototypesFrom(prototype), (current) => Arr.filter(Object.getOwnPropertyNames(current), (name) => Object.getOwnPropertyDescriptor(current, name)?.get !== void 0));
115
- const getterAt = (ast, names, index, fits, memo) => rememberAt(memo, ast, index, false, () => {
116
- const name = names[index];
117
- if (name === void 0) return false;
118
- const last = index === names.length - 1;
119
- const next = (type, at) => getterAt(type, names, at, fits, memo);
120
- if (name === "**" && !last && next(ast, index + 1)) return true;
121
- switch (ast._tag) {
122
- case "Union": return Arr.some(ast.types, (type) => next(type, index));
123
- case "Suspend": return next(ast.thunk(), index);
124
- case "Declaration": {
125
- const inner = classStruct(ast);
126
- if (inner === void 0) return false;
127
- return last ? Arr.some(getterNames(classPrototype(ast)), (getter) => fits(name, getter)) : next(inner, index);
128
- }
129
- case "Objects": return !last && (Arr.some(ast.propertySignatures, (property) => typeof property.name === "string" && (name === "**" && next(property.type, index) || fits(name, property.name) && next(property.type, index + 1))) || Arr.some(ast.indexSignatures, (signature) => name === "**" ? hasSomeKey(signature.parameter) && (next(signature.type, index) || next(signature.type, index + 1)) : Arr.some([name.replaceAll("*", ""), name.replaceAll("*", "a")], (key) => fits(name, key) && keyFits(signature.parameter, key)) && next(signature.type, index + 1)));
130
- default: return false;
131
- }
132
- });
133
- /**
134
- * Tests if a record takes a key. A key of an object is a string, also a key
135
- * that a number Schema takes: `{ 1: "a" }` has the key `"1"`. So a key that
136
- * is the text of a number is tried as that number too.
137
- */
138
- const keyFits = (parameter, key) => {
139
- const is = Schema.is(Schema.make(parameter));
140
- return is(key) || key !== "" && String(Number(key)) === key && is(Number(key));
141
- };
142
- /**
143
- * Gives the struct of a `Schema.Class`. A class is a declaration with one
144
- * struct as its type parameter, and Effect marks it with a `"~constructor"`
145
- * annotation, also through more codec steps, as in
146
- * `Schema.fromJsonString(User)`. Other declarations, such as
147
- * `Schema.OptionFromNullOr` or `Schema.ReadonlyMap`, have no such mark, and
148
- * the fields of their type parameter are not fields of the value.
149
- */
150
- const classStruct = (ast) => {
151
- const [inner] = ast.typeParameters;
152
- return ast.annotations?.["~constructor"] !== void 0 && ast.typeParameters.length === 1 && inner?._tag === "Objects" ? inner : void 0;
153
- };
154
- /**
155
- * Gives the structs inside a Schema, with the `Schema.suspend` nodes that are
156
- * open on the way to each one. The open nodes are in a native `Set`, because
157
- * it must find a node by the node itself: `Equal` compares Schema nodes by
158
- * structure, and a recursive Schema has no end. It looks through optional keys, unions such as
159
- * `Schema.NullOr`, `Schema.suspend`, and `Schema.Class`. A suspend node that
160
- * is already open is not opened again, so a recursive Schema has an end.
161
- */
162
- const structsOf = (ast, open) => {
163
- switch (ast._tag) {
164
- case "Objects": return ast.propertySignatures.length === 0 ? [] : [[ast, open]];
165
- case "Union": return ast.types.flatMap((type) => structsOf(type, open));
166
- case "Suspend": return open.has(ast) ? [] : structsOf(ast.thunk(), /* @__PURE__ */ new Set([...open, ast]));
167
- case "Declaration": {
168
- const inner = classStruct(ast);
169
- return inner === void 0 ? [] : structsOf(inner, open);
170
- }
171
- default: return [];
172
- }
173
- };
174
- /** Gives the kind of value that a field holds. See {@link FieldShape}. */
175
- const shapeOf = (ast, open = /* @__PURE__ */ new Set()) => {
176
- switch (ast._tag) {
177
- case "Objects": return ast.indexSignatures.length > 0 ? "record" : "other";
178
- case "Union": {
179
- const shapes = ast.types.map((type) => shapeOf(type, open));
180
- return shapes.every((shape) => shape === "scalar") ? "scalar" : shapes.includes("record") ? "record" : "other";
181
- }
182
- case "Suspend": return open.has(ast) ? "other" : shapeOf(ast.thunk(), /* @__PURE__ */ new Set([...open, ast]));
183
- default: return isScalar(ast) ? "scalar" : "other";
184
- }
185
- };
186
- const isScalar = (ast) => {
187
- switch (ast._tag) {
188
- case "String":
189
- case "Number":
190
- case "Boolean":
191
- case "BigInt":
192
- case "Symbol":
193
- case "UniqueSymbol":
194
- case "Literal":
195
- case "Enum":
196
- case "TemplateLiteral":
197
- case "Null":
198
- case "Undefined": return true;
199
- default: return false;
200
- }
201
- };
202
- /**
203
- * Tests if a Schema holds, in some branch, a value that is not a struct: a
204
- * scalar, or a value such as a record or an array.
205
- */
206
- const holdsValue = (ast, open) => {
207
- switch (ast._tag) {
208
- case "Objects": return ast.propertySignatures.length === 0 || ast.indexSignatures.length > 0;
209
- case "Union": return ast.types.some((type) => holdsValue(type, open));
210
- case "Suspend": return !open.has(ast) && holdsValue(ast.thunk(), /* @__PURE__ */ new Set([...open, ast]));
211
- case "Declaration": return classStruct(ast) === void 0;
212
- default: return true;
213
- }
214
- };
215
- /**
216
- * Gives the path of every field of a Schema, with a dot between the names of
217
- * nested fields. A field of a union may come two times: one time as a field
218
- * that holds a value, and one time as a field that holds a struct.
219
- */
220
- const walkFields = (ast, prefix = "", open = /* @__PURE__ */ new Set()) => Arr.flatMap(structsOf(ast, open), ([struct, inner]) => Arr.flatMap(struct.propertySignatures, (property) => {
221
- const name = property.name;
222
- if (typeof name !== "string") return [];
223
- if (name.includes(".")) throw new Error(`A field name may not have a dot: "${name}"`);
224
- if (name === "") throw new Error("A field name may not be empty");
225
- const path = prefix === "" ? name : `${prefix}.${name}`;
226
- const type = property.type;
227
- return [...holdsValue(type, inner) ? [{
228
- path,
229
- leaf: true,
230
- type
231
- }] : [], ...structsOf(type, inner).length > 0 ? [{
232
- path,
233
- leaf: false,
234
- type
235
- }, ...walkFields(type, path, inner)] : []];
236
- }));
237
- /**
238
- * Gives the paths of all the fields of a Schema, with dots for nested fields.
239
- * A nested struct gives its own path and the paths of its fields. Arrays are
240
- * left out. It throws when a field name has a dot or is empty, because such a
241
- * name would give two fields one path, or a path with an empty part.
242
- *
243
- * @stability stable
244
- * @category getters
245
- * @since 1.0.0
246
- */
247
- const fieldPaths = (schema) => Arr.dedupe(Arr.map(walkFields(schema.ast), (visit) => visit.path));
248
- /** Tests if a Schema holds only plain scalars: see {@link isPlainField}. */
249
- /**
250
- * The ids of the checks that the search knows. `domainOf` reads them, so a
251
- * field with only these checks has an exact domain.
252
- */
253
- const knownChecks = HashSet.make("effect/schema/isInt", "effect/schema/isFinite");
254
- /**
255
- * Tests if each check of a Schema node is a check that the search knows. A
256
- * check such as `isGreaterThan(0)` stops some values, and the search does not
257
- * see it, so it could find a value that the Schema does not take.
258
- */
259
- const hasOnlyKnownChecks = (checks) => Arr.every(checks ?? [], (check) => check._tag === "FilterGroup" ? hasOnlyKnownChecks(check.checks) : HashSet.has(knownChecks, check.annotations?.representation?.id));
260
- const isPlainScalar = (ast) => {
261
- if (ast._tag === "Number" ? !hasOnlyKnownChecks(ast.checks) : (ast.checks ?? []).length > 0) return false;
262
- switch (ast._tag) {
263
- case "String":
264
- case "Number":
265
- case "Boolean":
266
- case "Literal":
267
- case "Null":
268
- case "Undefined": return true;
269
- case "Union": return ast.options?.mode !== "oneOf" && Arr.every(ast.types, isPlainScalar);
270
- default: return false;
271
- }
272
- };
273
- /**
274
- * Tests if a path names a plain scalar field of a Schema: a string, a number,
275
- * a boolean, a literal, `null`, or an optional field or a union of these. Its
276
- * only checks may be integer and finite checks: another check, such as
277
- * `isGreaterThan(0)`, stops values that the search does not know about. On
278
- * the way, each part must be a required field of a plain struct with no
279
- * checks, with a name that is not a name of `Object.prototype`.
280
- *
281
- * `Policy.coverage` and `Condition.satisfiable` answer `All`, `None` or
282
- * `Some` only when each field of the condition is such a field. Their model of
283
- * the values of a field is exact for these Schemas. For other Schemas, such as
284
- * classes, records, unions of structs or declarations, a value may have data
285
- * that the Schema does not show, and the answer is `Unknown`.
286
- *
287
- * @stability stable
288
- * @category predicates
289
- * @since 1.0.0
290
- */
291
- const isPlainField = (schema, path) => !isTooLong(path) && plainAt(schema.ast, path.split("."));
292
- const plainAt = (ast, names) => {
293
- const [name, ...rest] = names;
294
- if (name === void 0 || ast._tag !== "Objects" || ast.indexSignatures.length > 0 || (ast.checks ?? []).length > 0 || name in Object.prototype) return false;
295
- const property = ast.propertySignatures.find((signature) => signature.name === name);
296
- if (property === void 0) return false;
297
- return rest.length === 0 ? isPlainScalar(property.type) : !SchemaAST.isOptional(property.type) && plainAt(property.type, rest);
298
- };
299
- /**
300
- * Tests if a path names a field of a Schema that holds a value with no named
301
- * fields: a scalar, or a value such as a record or an array. It goes into
302
- * records by their keys, and into recursive Schemas as far as the path goes.
303
- * So it finds paths that {@link fieldPaths} does not list.
304
- *
305
- * @stability stable
306
- * @category predicates
307
- * @since 1.0.0
308
- */
309
- const hasPath = (schema, path) => !isTooLong(path) && resolves(schema.ast, path.split("."), 0, /* @__PURE__ */ new Map());
310
- /**
311
- * Gives the answer for a node at a place from the memo, or works it out and
312
- * keeps it. While the work runs, the memo holds `pending`, so a walk that
313
- * comes back to the node at the same place, with no step on the path, gets
314
- * `pending` and stops.
315
- */
316
- const rememberAt = (memo, ast, index, pending, work) => {
317
- const places = memo.get(ast) ?? /* @__PURE__ */ new Map();
318
- memo.set(ast, places);
319
- if (places.has(index)) return places.get(index);
320
- places.set(index, pending);
321
- const result = work();
322
- places.set(index, result);
323
- return result;
324
- };
325
- const resolves = (ast, names, index, memo) => rememberAt(memo, ast, index, false, () => {
326
- if (index === names.length) return index > 0 && holdsValue(ast, /* @__PURE__ */ new Set());
327
- const name = names[index];
328
- switch (ast._tag) {
329
- case "Objects": {
330
- const property = ast.propertySignatures.find((signature) => signature.name === name);
331
- if (property !== void 0) return resolves(property.type, names, index + 1, memo);
332
- return ast.indexSignatures.some((signature) => keyFits(signature.parameter, name) && resolves(signature.type, names, index + 1, memo));
333
- }
334
- case "Union": return ast.types.some((type) => resolves(type, names, index, memo));
335
- case "Suspend": return resolves(ast.thunk(), names, index, memo);
336
- case "Declaration": {
337
- const inner = classStruct(ast);
338
- return inner !== void 0 && resolves(inner, names, index, memo);
339
- }
340
- default: return false;
341
- }
342
- });
343
- /**
344
- * Tests if a Schema has a field whose path fits a pattern. `parts` are the
345
- * parts of the pattern, split at the dots. A part `**` stands for any number
346
- * of parts, also none. Another part fits a name when `fits` says so, as a
347
- * part with `*` fits many names. For a record, the walk asks if the key
348
- * Schema takes a key that fits the part. For a template literal key Schema,
349
- * with parts that are literals and strings, the answer is exact. For another key Schema, the walk tries sample keys, so it may miss a
350
- * key, and then the answer is `false`.
351
- *
352
- * @stability stable
353
- * @category predicates
354
- * @since 1.0.0
355
- */
356
- const hasMatchingPath = (schema, parts, fits) => parts.length <= maxPathParts && matchesFrom(schema.ast, parts, 0, false, fits, /* @__PURE__ */ new Map());
357
- const matchesFrom = (ast, parts, index, stepped, fits, memo) => rememberAt(memo, ast, index * 2 + (stepped ? 1 : 0), false, () => {
358
- const next = (type, at, step) => matchesFrom(type, parts, at, step, fits, memo);
359
- switch (ast._tag) {
360
- case "Union": return Arr.some(ast.types, (type) => next(type, index, stepped));
361
- case "Suspend": return next(ast.thunk(), index, stepped);
362
- case "Declaration": {
363
- const inner = classStruct(ast);
364
- if (inner !== void 0) return next(inner, index, stepped);
365
- break;
366
- }
367
- }
368
- if (index === parts.length) return stepped && holdsValue(ast, /* @__PURE__ */ new Set());
369
- const part = parts[index];
370
- if (part === "**" && next(ast, index + 1, stepped)) return true;
371
- if (ast._tag !== "Objects") return false;
372
- const fromPart = [part.replaceAll("*", ""), part.replaceAll("*", "a")];
373
- return Arr.some(ast.propertySignatures, (property) => typeof property.name === "string" && (part === "**" ? next(property.type, index, true) : fits(part, property.name) && next(property.type, index + 1, true))) || Arr.some(ast.indexSignatures, (signature) => part === "**" ? hasSomeKey(signature.parameter) && next(signature.type, index, true) : (globMeetsKeys(part, signature.parameter) ?? Arr.some(Arr.dedupe([...fromPart, ...sampleKeys(signature.parameter)]), (key) => keyFits(signature.parameter, key) && fits(part, key))) && next(signature.type, index + 1, true));
374
- });
375
- /**
376
- * Tests if a key Schema takes some key: the exact search for a template
377
- * literal, or a sample key.
378
- */
379
- const hasSomeKey = (parameter) => globMeetsKeys("*", parameter) ?? Arr.some(Arr.dedupe([
380
- "",
381
- "a",
382
- ...sampleKeys(parameter)
383
- ]), (key) => keyFits(parameter, key));
384
- /** The longest key pattern that the exact search of keys takes. */
385
- const maxExactKeyLength = 256;
386
- const charsOf = (text) => Arr.map(Array.from(text), (char) => ({
387
- _tag: "Char",
388
- char
389
- }));
390
- /**
391
- * Gives the token lists of a template literal, one for each choice of its
392
- * unions of literals, or `undefined` when the lists can not be exact: a part
393
- * that this walk does not know, such as a number, a part with checks, or
394
- * more than 64 lists.
395
- */
396
- const templateTokens = (ast) => {
397
- const partTokens = (part) => {
398
- if ((part.checks ?? []).length > 0) return;
399
- switch (part._tag) {
400
- case "Literal": {
401
- const text = String(part.literal);
402
- return /[\uD800-\uDFFF]/.test(text) ? void 0 : [charsOf(text)];
403
- }
404
- case "String": return [[{ _tag: "Any" }]];
405
- case "Number": return;
406
- case "Union": {
407
- if (part.options?.mode === "oneOf") return;
408
- const choices = Arr.map(part.types, partTokens);
409
- return Arr.every(choices, (choice) => choice !== void 0) ? Arr.flatten(choices) : void 0;
410
- }
411
- default: return;
412
- }
413
- };
414
- return Arr.reduce(ast.parts, [[]], (lists, part) => {
415
- const choices = partTokens(part);
416
- if (lists === void 0 || choices === void 0) return;
417
- const all = Arr.flatMap(lists, (list) => Arr.map(choices, (choice) => [...list, ...choice]));
418
- return all.length > 64 || Arr.some(all, (list) => list.length > maxExactKeyLength) ? void 0 : all;
419
- });
420
- };
421
- /**
422
- * Tests if a key pattern and a list of template tokens share a key. It walks
423
- * the pairs of places in the two lists, with a few characters that stand for
424
- * all others, so the answer is exact for these tokens.
425
- */
426
- const tokensMeet = (glob, template) => {
427
- const key = (t, g) => t * (glob.length + 1) + g;
428
- const closeOf = (t, g) => [
429
- [t, g],
430
- ...template[t]?._tag === "Any" ? closeOf(t + 1, g) : [],
431
- ...glob[g]?._tag === "Star" ? closeOf(t, g + 1) : []
432
- ];
433
- const alphabet = Arr.dedupe([
434
- "a",
435
- "0",
436
- ...Arr.flatMap(glob, (token) => token._tag === "Char" ? [token.char] : []),
437
- ...Arr.flatMap(template, (token) => token._tag === "Char" ? [token.char] : [])
438
- ]);
439
- const stepTemplate = (t, char) => {
440
- const token = template[t];
441
- return token === void 0 ? [] : token._tag === "Any" ? [t] : token.char === char ? [t + 1] : [];
442
- };
443
- const stepGlob = (g, char) => {
444
- const token = glob[g];
445
- return token === void 0 ? [] : token._tag === "Char" ? token.char === char ? [g + 1] : [] : char === "." ? [] : [g];
446
- };
447
- const search = (frontier, seen) => {
448
- const fresh = Array.from(HashMap.values(HashMap.fromIterable(Arr.map(Arr.filter(Arr.flatMap(frontier, ([t, g]) => closeOf(t, g)), ([t, g]) => !HashSet.has(seen, key(t, g))), (pair) => [key(pair[0], pair[1]), pair]))));
449
- if (fresh.length === 0) return false;
450
- if (Arr.some(fresh, ([t, g]) => t === template.length && g === glob.length)) return true;
451
- const next = HashSet.union(seen, HashSet.fromIterable(Arr.map(fresh, ([t, g]) => key(t, g))));
452
- return search(Arr.flatMap(fresh, ([t, g]) => Arr.flatMap(alphabet, (char) => Arr.flatMap(stepTemplate(t, char), (t2) => Arr.map(stepGlob(g, char), (g2) => [t2, g2])))), next);
453
- };
454
- return search([[0, 0]], HashSet.empty());
455
- };
456
- /**
457
- * Tests if a key pattern fits a key of a template literal key Schema, or
458
- * `undefined` when the search is not exact for the Schema. The search calls
459
- * itself one time for each character of a key, so a very long part gives
460
- * `undefined` too.
461
- */
462
- const globMeetsKeys = (part, ast) => {
463
- if (ast._tag !== "TemplateLiteral" || (ast.checks ?? []).length > 0 || part.length > maxExactKeyLength) return;
464
- const lists = templateTokens(ast);
465
- if (lists === void 0) return;
466
- const glob = Arr.map(part.match(/\*+|[^*]/gu) ?? [], (token) => token.startsWith("*") ? { _tag: "Star" } : {
467
- _tag: "Char",
468
- char: token
469
- });
470
- return Arr.some(lists, (list) => tokensMeet(glob, list));
471
- };
472
- /**
473
- * A few keys that a key Schema of a record takes: each part of a template
474
- * literal gives its literal, or a short text for a string or a number. These
475
- * are samples. A key that is not a sample may fit too, so a "no" from a test
476
- * with them is only "not found".
477
- */
478
- const sampleKeys = (ast) => {
479
- switch (ast._tag) {
480
- case "String": return ["", "a"];
481
- case "Number": return ["0", "1"];
482
- case "Literal": return [String(ast.literal)];
483
- case "Union": return Arr.flatMap(ast.types, sampleKeys);
484
- case "TemplateLiteral": return Arr.reduce(ast.parts, [""], (keys, part) => Arr.take(Arr.flatMap(keys, (key) => Arr.map(Arr.take(sampleKeys(part), 2), (end) => key + end)), 64));
485
- default: return [];
486
- }
487
- };
488
- /**
489
- * Tests if a Schema surely has a value: each required field, at each depth,
490
- * has a type that has values, with no checks other than integer and finite
491
- * checks on numbers. A check such as a minimum length may stop every value,
492
- * and the library can not see that, so such a Schema gives `false`. So does a
493
- * required field that holds the struct itself again.
494
- *
495
- * @stability stable
496
- * @category predicates
497
- * @since 1.0.0
498
- */
499
- const hasValue = (schema) => isSurelyInhabited(schema.ast, /* @__PURE__ */ new Set());
500
- const isSurelyInhabited = (ast, open) => {
501
- if (ast._tag === "Number" ? !hasOnlyKnownChecks(ast.checks) : (ast.checks ?? []).length > 0) return false;
502
- switch (ast._tag) {
503
- case "Never": return false;
504
- case "Objects": return (ast.propertySignatures.length === 0 || ast.indexSignatures.length === 0) && Arr.every(ast.propertySignatures, (property) => SchemaAST.isOptional(property.type) || isSurelyInhabited(property.type, open));
505
- case "Union": return ast.options?.mode !== "oneOf" && Arr.some(ast.types, (type) => isSurelyInhabited(type, open));
506
- case "Suspend": return !open.has(ast) && isSurelyInhabited(ast.thunk(), /* @__PURE__ */ new Set([...open, ast]));
507
- case "Arrays": return Arr.every([...Arr.filter(ast.elements, (element) => !SchemaAST.isOptional(element)), ...ast.rest.slice(1)], (element) => isSurelyInhabited(element, open));
508
- case "Declaration": {
509
- const inner = classStruct(ast);
510
- if (inner !== void 0) return isSurelyInhabited(inner, open);
511
- const id = declarationId(ast);
512
- if (HashSet.has(wrapperDeclarations, id)) return Arr.some(ast.typeParameters, (type) => isSurelyInhabited(type, open));
513
- return HashSet.has(inhabitedDeclarations, id);
514
- }
515
- case "Enum": return ast.enums.length > 0;
516
- case "TemplateLiteral": return Arr.every(ast.parts, (part) => isSurelyInhabited(part, open));
517
- case "String":
518
- case "Number":
519
- case "Boolean":
520
- case "BigInt":
521
- case "Symbol":
522
- case "UniqueSymbol":
523
- case "Literal":
524
- case "Null":
525
- case "Undefined":
526
- case "Void":
527
- case "Unknown":
528
- case "Any":
529
- case "ObjectKeyword": return true;
530
- default: return false;
531
- }
532
- };
533
- /** Declarations of Effect that hold a value of a type parameter. */
534
- const wrapperDeclarations = HashSet.make("effect/schema/Redacted", "effect/schema/Result");
535
- /** Declarations of Effect that have a value for any type parameters. */
536
- const inhabitedDeclarations = HashSet.make("effect/schema/Date", "effect/schema/URL", "effect/schema/Uint8Array", "effect/schema/Duration", "effect/schema/DateTimeUtc", "effect/schema/BigDecimal", "effect/schema/Cause", "effect/schema/Chunk", "effect/schema/HashMap", "effect/schema/HashSet", "effect/schema/ReadonlyMap", "effect/schema/ReadonlySet", "effect/schema/Option", "effect/schema/File", "effect/schema/FormData", "effect/schema/RegExp", "effect/schema/Json", "effect/schema/ByteSize", "effect/schema/DateTimeZoned", "effect/schema/Error", "effect/schema/Exit", "effect/schema/CauseReason", "effect/schema/Graph", "effect/schema/SemVer", "effect/schema/SemVerRange", "effect/schema/TimeZone", "effect/schema/TimeZoneNamed", "effect/schema/TimeZoneOffset", "effect/schema/URLSearchParams");
537
- /**
538
- * Tests if a Schema has no value at all: a required field of it, at some
539
- * depth, has no value, as `Schema.Never` has none. A required field that
540
- * holds the struct itself again, with no way to stop, has no finite value
541
- * either. Other checks, such as a minimum and a maximum that do not meet, are
542
- * not seen, so a "no" does not prove that the Schema has a value.
543
- *
544
- * @stability stable
545
- * @category predicates
546
- * @since 1.0.0
547
- */
548
- const hasNoValue = (schema) => isEmptyType(schema.ast, /* @__PURE__ */ new Set());
549
- const isEmptyType = (ast, open) => {
550
- switch (ast._tag) {
551
- case "Never": return true;
552
- case "Objects": return Arr.some(ast.propertySignatures, (property) => !SchemaAST.isOptional(property.type) && isEmptyType(property.type, open));
553
- case "Union": return Arr.every(ast.types, (type) => isEmptyType(type, open));
554
- case "Suspend": return open.has(ast) || isEmptyType(ast.thunk(), /* @__PURE__ */ new Set([...open, ast]));
555
- case "Declaration": {
556
- const inner = classStruct(ast);
557
- return inner !== void 0 && isEmptyType(inner, open);
558
- }
559
- default: return false;
560
- }
561
- };
562
- const numberOrder = [
563
- "none",
564
- "integer",
565
- "finite",
566
- "any"
567
- ];
568
- const emptyDomain = {
569
- null: false,
570
- object: false,
571
- boolean: false,
572
- string: false,
573
- number: "none",
574
- literals: []
575
- };
576
- const nullDomain = {
577
- ...emptyDomain,
578
- null: true
579
- };
580
- const objectDomain = {
581
- ...emptyDomain,
582
- object: true
583
- };
584
- /**
585
- * The domain of a field with no Schema: any value.
586
- *
587
- * @stability stable
588
- * @category constants
589
- * @since 1.0.0
590
- */
591
- const anyDomain = {
592
- null: true,
593
- object: true,
594
- boolean: true,
595
- string: true,
596
- number: "any",
597
- literals: []
598
- };
599
- const joinDomains = (domains) => domains.reduce((left, right) => ({
600
- null: left.null || right.null,
601
- object: left.object || right.object,
602
- boolean: left.boolean || right.boolean,
603
- string: left.string || right.string,
604
- number: numberOrder.indexOf(left.number) >= numberOrder.indexOf(right.number) ? left.number : right.number,
605
- literals: [...left.literals, ...right.literals]
606
- }), emptyDomain);
607
- const hasCheck = (checks, id) => checks.some((check) => check.annotations?.representation?.id === id || check._tag === "FilterGroup" && hasCheck(check.checks, id));
608
- /**
609
- * The ids of the declarations of Effect that hold only values with parts.
610
- * Effect puts the id in the `representation` annotation. A declaration that
611
- * is not in this list may hold a scalar, as `Schema.Json` may.
612
- */
613
- const objectDeclarations = HashSet.make("effect/schema/Date", "effect/schema/URL", "effect/schema/Uint8Array", "effect/schema/Duration", "effect/schema/DateTimeUtc", "effect/schema/BigDecimal", "effect/schema/Redacted", "effect/schema/Cause", "effect/schema/Chunk", "effect/schema/HashMap", "effect/schema/HashSet", "effect/schema/ReadonlyMap", "effect/schema/ReadonlySet", "effect/schema/Option", "effect/schema/Result", "effect/schema/File", "effect/schema/FormData", "effect/schema/RegExp");
614
- const declarationId = (ast) => {
615
- const representation = ast.annotations?.["representation"];
616
- return hasProperty(representation, "id") && typeof representation.id === "string" ? representation.id : void 0;
617
- };
618
- /** The domain of the values of a Schema node. */
619
- const domainOfType = (ast, open) => {
620
- switch (ast._tag) {
621
- case "Null":
622
- case "Undefined": return nullDomain;
623
- case "Never": return emptyDomain;
624
- case "Void":
625
- case "Unknown":
626
- case "Any": return anyDomain;
627
- case "String":
628
- case "TemplateLiteral": return {
629
- ...emptyDomain,
630
- string: true
631
- };
632
- case "Number": {
633
- const checks = ast.checks ?? [];
634
- return {
635
- ...emptyDomain,
636
- number: hasCheck(checks, "effect/schema/isInt") ? "integer" : hasCheck(checks, "effect/schema/isFinite") ? "finite" : "any"
637
- };
638
- }
639
- case "Boolean": return {
640
- ...emptyDomain,
641
- boolean: true
642
- };
643
- case "Literal": return typeof ast.literal === "bigint" ? objectDomain : {
644
- ...emptyDomain,
645
- literals: [ast.literal]
646
- };
647
- case "Enum": return {
648
- ...emptyDomain,
649
- literals: ast.enums.map(([, value]) => value)
650
- };
651
- case "Union": return joinDomains(ast.types.map((type) => domainOfType(type, open)));
652
- case "Suspend": return open.has(ast) ? anyDomain : domainOfType(ast.thunk(), /* @__PURE__ */ new Set([...open, ast]));
653
- case "Objects": return ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0 ? anyDomain : objectDomain;
654
- case "Declaration": return classStruct(ast) !== void 0 || HashSet.has(objectDeclarations, declarationId(ast)) ? objectDomain : anyDomain;
655
- default: return objectDomain;
656
- }
657
- };
658
- const domainAt = (ast, names, index, memo) => rememberAt(memo, ast, index, anyDomain, () => {
659
- if (index === names.length) return domainOfType(ast, /* @__PURE__ */ new Set());
660
- const name = names[index];
661
- switch (ast._tag) {
662
- case "Objects": {
663
- const property = ast.propertySignatures.find((signature) => signature.name === name);
664
- if (property !== void 0) {
665
- const inner = domainAt(property.type, names, index + 1, memo);
666
- return SchemaAST.isOptional(property.type) || name in Object.prototype ? joinDomains([nullDomain, inner]) : inner;
667
- }
668
- return anyDomain;
669
- }
670
- case "Union": return joinDomains(ast.types.map((type) => domainAt(type, names, index, memo)));
671
- case "Suspend": return domainAt(ast.thunk(), names, index, memo);
672
- case "Declaration": {
673
- const inner = classStruct(ast);
674
- return inner === void 0 ? anyDomain : domainAt(inner, names, index, memo);
675
- }
676
- case "Arrays":
677
- case "ObjectKeyword":
678
- case "Unknown":
679
- case "Any": return anyDomain;
680
- default: return nullDomain;
681
- }
682
- });
683
- /**
684
- * Gives the domain of a field of a Schema, by its path with dots. A path that
685
- * the Schema does not name gives `null`, as a condition reads such a field.
686
- *
687
- * @stability stable
688
- * @category getters
689
- * @since 1.0.0
690
- */
691
- const domainOf = (schema, path) => isTooLong(path) ? anyDomain : domainAt(schema.ast, path.split("."), 0, /* @__PURE__ */ new Map());
692
- /**
693
- * Checks the names of the top fields of a resource at once, when it is made.
694
- * The names of nested fields are checked when the descriptor is made.
695
- */
696
- const checkFields = (kind, options) => {
697
- if (Object.hasOwn(options.fields, "_tag")) throw new Error(`The resource "${kind}" may not have a field "_tag"`);
698
- Arr.map(Object.keys(options.fields), (name) => {
699
- if (name.includes(".")) throw new Error(`A field name may not have a dot: "${name}"`);
700
- if (name === "") throw new Error("A field name may not be empty");
701
- });
702
- };
703
- /**
704
- * Gives a value that is made on first use, and then kept. The descriptor of a
705
- * resource reads its Schema, and a recursive Schema refers to the resource
706
- * class, so it can not be read while the class is made. The cell belongs to
707
- * one resource.
708
- */
709
- const lazy = (make) => {
710
- const cell = {};
711
- return () => {
712
- if (!("value" in cell)) cell.value = make();
713
- return cell.value;
714
- };
715
- };
716
- /**
717
- * Checks each part of the path of a resource: a string, or a reference to a
718
- * top-level field of the resource. A reference to another field would read
719
- * nothing, and a rule on the path could then match a value by mistake.
720
- */
721
- const checkPath = (kind, fields, path) => Arr.map(Array.from(path), (segment) => {
722
- if (typeof segment === "string") return segment;
723
- if (hasProperty(segment, "_tag") && segment._tag === "Field" && hasProperty(segment, "field") && typeof segment.field === "string" && Object.hasOwn(fields, segment.field)) return {
724
- _tag: "Field",
725
- field: segment.field
726
- };
727
- throw new globalThis.Error(`A part of the path of "${kind}" is not a string or a field of the resource`);
728
- });
729
- /**
730
- * Copies the options of a resource. The descriptor is made later, when it is
731
- * first read, so a change to the objects of the caller before then must not
732
- * change it: the Schema of the values and the descriptor must agree.
733
- */
734
- const snapshot = (options) => ({
735
- ...options,
736
- actions: [...options.actions],
737
- fields: { ...options.fields }
738
- });
739
- const makeDescriptor = (kind, options) => {
740
- const refs = Object.fromEntries(Arr.map(Object.keys(options.fields), (key) => [key, {
741
- _tag: "Field",
742
- field: key
743
- }]));
744
- const visits = walkFields(Schema.Struct(options.fields).ast);
745
- const leaves = Arr.filter(visits, (visit) => visit.leaf);
746
- const structs = HashSet.fromIterable(Arr.map(Arr.filter(visits, (visit) => !visit.leaf), (visit) => visit.path));
747
- const open = Arr.filter(leaves, (visit) => shapeOf(visit.type) !== "scalar" || HashSet.has(structs, visit.path));
748
- return new ResourceDescriptor({
749
- kind,
750
- actions: [...options.actions],
751
- path: options.path === void 0 ? void 0 : checkPath(kind, options.fields, options.path(refs)),
752
- fieldPaths: Arr.dedupe(Arr.map(leaves, (visit) => visit.path)),
753
- openPaths: Arr.dedupe(Arr.map(open, (visit) => visit.path)),
754
- schema: Schema.TaggedStruct(kind, options.fields)
755
- });
756
- };
757
- /**
758
9
  * Makes a resource as a Schema class. Use it in the same way as
759
10
  * `Schema.TaggedClass`.
760
11
  *
@@ -763,35 +14,23 @@ const makeDescriptor = (kind, options) => {
763
14
  * import { Schema } from "effect"
764
15
  * import { Resource } from "@nmnmcc/ability"
765
16
  *
766
- * class Bot extends Resource.Class<Bot>()("Bot", {
17
+ * class Document extends Resource.Class<Document>()("Document", {
767
18
  * actions: ["read", "update"],
768
19
  * fields: {
769
20
  * id: Schema.String,
770
- * ownerUserId: Schema.NullOr(Schema.String),
21
+ * teamId: Schema.NullOr(Schema.String),
771
22
  * },
772
- * path: (f) => ["user", f.ownerUserId, "bot", f.id],
23
+ * path: (f) => ["team", f.teamId, "doc", f.id],
773
24
  * }) {}
774
25
  *
775
- * const bot = new Bot({ id: "b1", ownerUserId: "u1" })
26
+ * const document = new Document({ id: "d1", teamId: "t1" })
776
27
  * ```
777
28
  *
778
29
  * @stability stable
779
30
  * @category constructors
780
31
  * @since 0.3.0
781
32
  */
782
- const Class = ((identifier) => (kind, input) => {
783
- const options = snapshot(input);
784
- checkFields(kind, options);
785
- const Base = Schema.TaggedClass(identifier)(kind, options.fields);
786
- const descriptor = lazy(() => makeDescriptor(kind, options));
787
- return class extends Base {
788
- static get descriptor() {
789
- return descriptor();
790
- }
791
- static kind = kind;
792
- static actions = [...options.actions];
793
- };
794
- });
33
+ const Class = makeClass;
795
34
  /**
796
35
  * Makes a resource from plain fields. Its values are plain objects with a
797
36
  * `_tag` field.
@@ -800,45 +39,7 @@ const Class = ((identifier) => (kind, input) => {
800
39
  * @category constructors
801
40
  * @since 0.3.0
802
41
  */
803
- const make = ((kind, input) => {
804
- const options = snapshot(input);
805
- checkFields(kind, options);
806
- const schema = Schema.TaggedStruct(kind, options.fields);
807
- const descriptor = lazy(() => makeDescriptor(kind, options));
808
- return {
809
- get descriptor() {
810
- return descriptor();
811
- },
812
- kind,
813
- actions: [...options.actions],
814
- schema,
815
- make: (input) => schema.make(input)
816
- };
817
- });
818
- /**
819
- * Tests if a value is a resource.
820
- *
821
- * @stability stable
822
- * @category guards
823
- * @since 0.3.0
824
- */
825
- const isResource = (u) => hasProperty(u, "descriptor") && u.descriptor instanceof ResourceDescriptor;
826
- /**
827
- * Gets the runtime data of a resource.
828
- *
829
- * @stability stable
830
- * @category getters
831
- * @since 0.3.0
832
- */
833
- const descriptor = (resource) => resource.descriptor;
834
- /**
835
- * Gives the kind name of a resource value, from its `_tag` field.
836
- *
837
- * @stability stable
838
- * @category getters
839
- * @since 1.0.0
840
- */
841
- const kindOf = (value) => hasProperty(value, "_tag") && typeof value._tag === "string" ? value._tag : void 0;
842
- export { Class, Resource_exports, anyDomain, descriptor, domainOf, fieldPaths, hasGetter, hasMatchingGetter, hasMatchingPath, hasNoValue, hasPath, hasValue, isPlainField, isResource, kindOf, make };
42
+ const make = makeStruct;
43
+ export { Class, Resource_exports, make };
843
44
 
844
45
  //# sourceMappingURL=Resource.js.map