@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,23 +1,82 @@
1
1
  import * as Schema from "effect/Schema";
2
+ // src/internal/limits.ts
2
3
  /**
3
- * The most nodes of one condition. A child that a condition uses in many
4
- * places is counted for each place.
4
+ * The one table of limits. Each limit keeps a walk, a decoder or a search
5
+ * bounded, so no input can take a very long time or very much memory. A
6
+ * domain may set its own limits, up to the ceilings.
5
7
  */
6
- const conditionNodes = 1e5;
7
- /** The most nodes of one residual condition. */
8
- const residualNodes = 1e4;
9
8
  /**
10
- * The most nodes of a policy tree, with its rules and the nodes of their
11
- * conditions. A part that the tree uses in many places is counted for each
12
- * place.
9
+ * The limits of a domain that sets none. A walk does not fill the stack at
10
+ * any depth (see `Trampoline`), and the levels of a condition or of a policy
11
+ * tree are at most its nodes, so the limits of the nodes keep the time and
12
+ * the memory of a deep input small too. There is no limit of levels.
13
13
  */
14
- const policyNodes = 1e5;
15
- /** The most objects and arrays in one input to a decoder. */
16
- const jsonNodes = 1e6;
17
- /** The most fields in the catalog of one Schema. */
18
- const catalogFields = 1e4;
19
- /** The most steps of one search for a value. */
20
- const searchSteps = 1e6;
14
+ const defaults = {
15
+ fieldPathParts: 1e3,
16
+ patternParts: 1e3,
17
+ conditionNodes: 1e6,
18
+ residualNodes: 1e5,
19
+ policyNodes: 1e6,
20
+ jsonNodes: 1e7,
21
+ catalogFields: 1e5,
22
+ searchSteps: 1e6,
23
+ searchFields: 1e3
24
+ };
25
+ /**
26
+ * The most that a domain may set for each limit. The constructors of the
27
+ * nodes check these, since a node does not know its domain. The binding to a
28
+ * domain checks the limits of the domain.
29
+ */
30
+ const ceilings = {
31
+ fieldPathParts: 1e4,
32
+ patternParts: 1e4,
33
+ conditionNodes: 1e8,
34
+ residualNodes: 1e8,
35
+ policyNodes: 1e8,
36
+ jsonNodes: 1e9,
37
+ catalogFields: 1e7,
38
+ searchSteps: 1e9,
39
+ searchFields: 1e5
40
+ };
41
+ /** The Schema of one limit: a whole number from 1 up to its ceiling. */
42
+ const limit = (name) => Schema.optionalKey(Schema.Int.check(Schema.isBetween({
43
+ minimum: 1,
44
+ maximum: ceilings[name]
45
+ })));
46
+ /**
47
+ * The Schema of the limits that a domain sets. Each one is a whole number
48
+ * from 1 up to its ceiling, and a limit that the domain leaves out has its
49
+ * default.
50
+ */
51
+ const Options = Schema.Struct({
52
+ fieldPathParts: limit("fieldPathParts"),
53
+ patternParts: limit("patternParts"),
54
+ conditionNodes: limit("conditionNodes"),
55
+ residualNodes: limit("residualNodes"),
56
+ policyNodes: limit("policyNodes"),
57
+ jsonNodes: limit("jsonNodes"),
58
+ catalogFields: limit("catalogFields"),
59
+ searchSteps: limit("searchSteps"),
60
+ searchFields: limit("searchFields")
61
+ });
62
+ /**
63
+ * Makes the limits of a domain from the ones that it sets. A value that is
64
+ * not a whole number from 1 up to its ceiling is a mistake in the code, so
65
+ * it is a defect.
66
+ */
67
+ const make = (options = {}) => ({
68
+ ...defaults,
69
+ ...Schema.decodeUnknownSync(Options)(options)
70
+ });
71
+ const fieldPathParts = defaults.fieldPathParts;
72
+ const patternParts = defaults.patternParts;
73
+ const conditionNodes = defaults.conditionNodes;
74
+ const residualNodes = defaults.residualNodes;
75
+ const policyNodes = defaults.policyNodes;
76
+ const jsonNodes = defaults.jsonNodes;
77
+ const catalogFields = defaults.catalogFields;
78
+ const searchSteps = defaults.searchSteps;
79
+ const searchFields = defaults.searchFields;
21
80
  /**
22
81
  * The error when an input goes past a limit. It names the limit, the size of
23
82
  * the input, and the most that the limit takes.
@@ -38,6 +97,6 @@ const exceeded = (thing, limit, unit, size, max) => LimitExceeded.make({
38
97
  max,
39
98
  message: `${thing} has ${String(size)} ${unit}. The most is ${String(max)}.`
40
99
  });
41
- export { LimitExceeded, catalogFields, conditionNodes, exceeded, jsonNodes, policyNodes, residualNodes, searchSteps };
100
+ export { LimitExceeded, Options, catalogFields, ceilings, conditionNodes, defaults, exceeded, fieldPathParts, jsonNodes, make, patternParts, policyNodes, residualNodes, searchFields, searchSteps };
42
101
 
43
102
  //# sourceMappingURL=limits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","names":[],"sources":["../../src/internal/limits.ts"],"sourcesContent":["/**\n * The one table of limits. Each limit keeps a walk, a decoder or a search\n * bounded, so no input can fill the stack or take a very long time. The\n * values are the same in the types and at run time where both have one, as\n * for the parts of a field path.\n */\nimport * as Schema from \"effect/Schema\"\n\n/** The most parts of a field path, as in `settings.model`. */\nexport const fieldPathParts = 5\n\n/** The most parts of a field pattern, as in `**.secret`. */\nexport const patternParts = 16\n\n/**\n * The most nodes of one condition. A child that a condition uses in many\n * places is counted for each place.\n */\nexport const conditionNodes = 100_000\n\n/** The most levels of one condition. */\nexport const conditionDepth = 500\n\n/** The most nodes of one residual condition. */\nexport const residualNodes = 10_000\n\n/** The most levels of a policy tree. */\nexport const policyDepth = 256\n\n/**\n * The most nodes of a policy tree, with its rules and the nodes of their\n * conditions. A part that the tree uses in many places is counted for each\n * place.\n */\nexport const policyNodes = 100_000\n\n/** The most objects and arrays in one input to a decoder. */\nexport const jsonNodes = 1_000_000\n\n/** The most fields in the catalog of one Schema. */\nexport const catalogFields = 10_000\n\n/** The most steps of one search for a value. */\nexport const searchSteps = 1_000_000\n\n/** The most fields that one search for a value may test. */\nexport const searchFields = 256\n\n/**\n * The error when an input goes past a limit. It names the limit, the size of\n * the input, and the most that the limit takes.\n */\nexport class LimitExceeded extends Schema.TaggedError<LimitExceeded>(\n\t\"@nmnmcc/ability/Limits/LimitExceeded\",\n)(\"LimitExceeded\", {\n\tlimit: Schema.String,\n\tsize: Schema.Finite,\n\tmax: Schema.Finite,\n\tmessage: Schema.String,\n}) {}\n\n/**\n * Makes the error for a limit, with one form of message for every limit, as\n * in \"A condition has 100001 nodes. The most is 100000.\"\n */\nexport const exceeded = (\n\tthing: string,\n\tlimit: string,\n\tunit: string,\n\tsize: number,\n\tmax: number,\n): LimitExceeded =>\n\tLimitExceeded.make({\n\t\tlimit,\n\t\tsize,\n\t\tmax,\n\t\tmessage: `${thing} has ${String(size)} ${unit}. The most is ${String(max)}.`,\n\t})\n"],"mappings":";;;;;AAkBA,MAAa,iBAAiB;;AAM9B,MAAa,gBAAgB;;;;;;AAU7B,MAAa,cAAc;;AAG3B,MAAa,YAAY;;AAGzB,MAAa,gBAAgB;;AAG7B,MAAa,cAAc;;;;;AAS3B,IAAa,gBAAb,cAAmC,OAAO,YACzC,sCACD,CAAC,CAAC,iBAAiB;CAClB,OAAO,OAAO;CACd,MAAM,OAAO;CACb,KAAK,OAAO;CACZ,SAAS,OAAO;AACjB,CAAC,CAAC,CAAC,CAAC;;;;;AAMJ,MAAa,YACZ,OACA,OACA,MACA,MACA,QAEA,cAAc,KAAK;CAClB;CACA;CACA;CACA,SAAS,GAAG,MAAM,OAAO,OAAO,IAAI,EAAE,GAAG,KAAK,gBAAgB,OAAO,GAAG,EAAE;AAC3E,CAAC"}
1
+ {"version":3,"file":"limits.js","names":[],"sources":["../../src/internal/limits.ts"],"sourcesContent":["/**\n * The one table of limits. Each limit keeps a walk, a decoder or a search\n * bounded, so no input can take a very long time or very much memory. A\n * domain may set its own limits, up to the ceilings.\n */\nimport * as Schema from \"effect/Schema\"\n\n/** The values of all the limits. */\nexport interface Limits {\n\t/** The most parts of a field path, as in `settings.model`. */\n\treadonly fieldPathParts: number\n\t/** The most parts of a field pattern, as in `**.secret`. */\n\treadonly patternParts: number\n\t/**\n\t * The most nodes of one condition. A child that a condition uses in many\n\t * places is counted for each place.\n\t */\n\treadonly conditionNodes: number\n\t/** The most nodes of one residual condition. */\n\treadonly residualNodes: number\n\t/**\n\t * The most nodes of a policy tree, with its rules and the nodes of their\n\t * conditions. A part that the tree uses in many places is counted for\n\t * each place.\n\t */\n\treadonly policyNodes: number\n\t/** The most objects and arrays in one input to a decoder. */\n\treadonly jsonNodes: number\n\t/** The most fields in the list of the fields of one Schema. */\n\treadonly catalogFields: number\n\t/** The most steps of one search for a value. */\n\treadonly searchSteps: number\n\t/** The most fields that one search for a value may test. */\n\treadonly searchFields: number\n}\n\n/**\n * The limits of a domain that sets none. A walk does not fill the stack at\n * any depth (see `Trampoline`), and the levels of a condition or of a policy\n * tree are at most its nodes, so the limits of the nodes keep the time and\n * the memory of a deep input small too. There is no limit of levels.\n */\nexport const defaults: Limits = {\n\tfieldPathParts: 1000,\n\tpatternParts: 1000,\n\tconditionNodes: 1_000_000,\n\tresidualNodes: 100_000,\n\tpolicyNodes: 1_000_000,\n\tjsonNodes: 10_000_000,\n\tcatalogFields: 100_000,\n\t// A search is the work of one check of a kind, as in the Authorizer, so\n\t// its steps are a limit of the time of one request.\n\tsearchSteps: 1_000_000,\n\tsearchFields: 1000,\n}\n\n/**\n * The most that a domain may set for each limit. The constructors of the\n * nodes check these, since a node does not know its domain. The binding to a\n * domain checks the limits of the domain.\n */\nexport const ceilings: Limits = {\n\tfieldPathParts: 10_000,\n\tpatternParts: 10_000,\n\tconditionNodes: 100_000_000,\n\tresidualNodes: 100_000_000,\n\tpolicyNodes: 100_000_000,\n\tjsonNodes: 1_000_000_000,\n\tcatalogFields: 10_000_000,\n\tsearchSteps: 1_000_000_000,\n\tsearchFields: 100_000,\n}\n\n/** The Schema of one limit: a whole number from 1 up to its ceiling. */\nconst limit = (name: keyof Limits) =>\n\tSchema.optionalKey(\n\t\tSchema.Int.check(Schema.isBetween({ minimum: 1, maximum: ceilings[name] })),\n\t)\n\n/**\n * The Schema of the limits that a domain sets. Each one is a whole number\n * from 1 up to its ceiling, and a limit that the domain leaves out has its\n * default.\n */\nexport const Options = Schema.Struct({\n\tfieldPathParts: limit(\"fieldPathParts\"),\n\tpatternParts: limit(\"patternParts\"),\n\tconditionNodes: limit(\"conditionNodes\"),\n\tresidualNodes: limit(\"residualNodes\"),\n\tpolicyNodes: limit(\"policyNodes\"),\n\tjsonNodes: limit(\"jsonNodes\"),\n\tcatalogFields: limit(\"catalogFields\"),\n\tsearchSteps: limit(\"searchSteps\"),\n\tsearchFields: limit(\"searchFields\"),\n})\n\n/**\n * Makes the limits of a domain from the ones that it sets. A value that is\n * not a whole number from 1 up to its ceiling is a mistake in the code, so\n * it is a defect.\n */\nexport const make = (options: unknown = {}): Limits => ({\n\t...defaults,\n\t...Schema.decodeUnknownSync(Options)(options),\n})\n\n// The default of each limit, by its name.\n\nexport const fieldPathParts = defaults.fieldPathParts\nexport const patternParts = defaults.patternParts\nexport const conditionNodes = defaults.conditionNodes\nexport const residualNodes = defaults.residualNodes\nexport const policyNodes = defaults.policyNodes\nexport const jsonNodes = defaults.jsonNodes\nexport const catalogFields = defaults.catalogFields\nexport const searchSteps = defaults.searchSteps\nexport const searchFields = defaults.searchFields\n\n/**\n * The error when an input goes past a limit. It names the limit, the size of\n * the input, and the most that the limit takes.\n */\nexport class LimitExceeded extends Schema.TaggedError<LimitExceeded>(\n\t\"@nmnmcc/ability/Limits/LimitExceeded\",\n)(\"LimitExceeded\", {\n\tlimit: Schema.String,\n\tsize: Schema.Finite,\n\tmax: Schema.Finite,\n\tmessage: Schema.String,\n}) {}\n\n/**\n * Makes the error for a limit, with one form of message for every limit, as\n * in \"A condition has 100001 nodes. The most is 100000.\"\n */\nexport const exceeded = (\n\tthing: string,\n\tlimit: string,\n\tunit: string,\n\tsize: number,\n\tmax: number,\n): LimitExceeded =>\n\tLimitExceeded.make({\n\t\tlimit,\n\t\tsize,\n\t\tmax,\n\t\tmessage: `${thing} has ${String(size)} ${unit}. The most is ${String(max)}.`,\n\t})\n"],"mappings":";;;;;;;;;;;;;AA0CA,MAAa,WAAmB;CAC/B,gBAAgB;CAChB,cAAc;CACd,gBAAgB;CAChB,eAAe;CACf,aAAa;CACb,WAAW;CACX,eAAe;CAGf,aAAa;CACb,cAAc;AACf;;;;;;AAOA,MAAa,WAAmB;CAC/B,gBAAgB;CAChB,cAAc;CACd,gBAAgB;CAChB,eAAe;CACf,aAAa;CACb,WAAW;CACX,eAAe;CACf,aAAa;CACb,cAAc;AACf;;AAGA,MAAM,SAAS,SACd,OAAO,YACN,OAAO,IAAI,MAAM,OAAO,UAAU;CAAE,SAAS;CAAG,SAAS,SAAS;AAAM,CAAC,CAAC,CAC3E;;;;;;AAOD,MAAa,UAAU,OAAO,OAAO;CACpC,gBAAgB,MAAM,gBAAgB;CACtC,cAAc,MAAM,cAAc;CAClC,gBAAgB,MAAM,gBAAgB;CACtC,eAAe,MAAM,eAAe;CACpC,aAAa,MAAM,aAAa;CAChC,WAAW,MAAM,WAAW;CAC5B,eAAe,MAAM,eAAe;CACpC,aAAa,MAAM,aAAa;CAChC,cAAc,MAAM,cAAc;AACnC,CAAC;;;;;;AAOD,MAAa,QAAQ,UAAmB,CAAC,OAAe;CACvD,GAAG;CACH,GAAG,OAAO,kBAAkB,OAAO,CAAC,CAAC,OAAO;AAC7C;AAIA,MAAa,iBAAiB,SAAS;AACvC,MAAa,eAAe,SAAS;AACrC,MAAa,iBAAiB,SAAS;AACvC,MAAa,gBAAgB,SAAS;AACtC,MAAa,cAAc,SAAS;AACpC,MAAa,YAAY,SAAS;AAClC,MAAa,gBAAgB,SAAS;AACtC,MAAa,cAAc,SAAS;AACpC,MAAa,eAAe,SAAS;;;;;AAMrC,IAAa,gBAAb,cAAmC,OAAO,YACzC,sCACD,CAAC,CAAC,iBAAiB;CAClB,OAAO,OAAO;CACd,MAAM,OAAO;CACb,KAAK,OAAO;CACZ,SAAS,OAAO;AACjB,CAAC,CAAC,CAAC,CAAC;;;;;AAMJ,MAAa,YACZ,OACA,OACA,MACA,MACA,QAEA,cAAc,KAAK;CAClB;CACA;CACA;CACA,SAAS,GAAG,MAAM,OAAO,OAAO,IAAI,EAAE,GAAG,KAAK,gBAAgB,OAAO,GAAG,EAAE;AAC3E,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.d.ts","names":[],"sources":["../../src/internal/nodes.ts"],"mappings":";;;;YA2BY;;qBAGC,iBAAe,OAAA;YAShB,yBAAyB,gBAAgB;;iBAWpC;WACP;WACA,OAAO;;;iBAIA;WACP;WACA;;YAGE,UAAU,UAAU;UA+DtB;WACA;WACA;WACA;WACA;;;uBAmDK,aAAa,KAAK;;;;;;;EAMhC;EAMA,YAAY,eAAe,OAAO;;MAM9B;;;;;;MASA;;MAKA;;MAKA;;;qBAMQ,aAAa;WAChB;EACT;;;qBAMY,cAAc;WACjB;EACT;;;qBAMY,gBAAgB;WACnB;WACQ,IAAI;WACJ;WACA,OAAO;EACxB,YAAY;aACF,IAAI;aACJ;aACA,OAAO;;;;qBAiBL,WAAW;WACd;WACQ;WACA,QAAQ,cAAc;EACvC,YAAY;aACF;aACA,QAAQ,cAAc;;;;qBAQpB,eAAe;WAClB;WACQ;EACjB,YAAY;aAAkB;;;;qBAMlB,cAAc;WACjB;WACQ,QAAQ,cAAc;EACvC,YAAY;aAAkB,QAAQ,cAAc;;;qBAiBxC,YAAY;WACf;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,WAAW;WACd;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,YAAY;WACf;WACQ,WAAW;EAC5B,YAAY,WAAW;;;YASZ,OACX,gBACA,uBACI,OAAO;;;;;;qBAOC,kBAAkB;WACrB;WACQ;WACA,MAChB,cACA,qBACI,OAAO;EACZ,YAAY;aAAkB;aAAuB,KAAK;;;;YAe/C,OACX,OAAO,QAAQ,UAAU,KAAK,SAAS,QAAQ,MAAM,KAAK,MAAM"}
1
+ {"version":3,"file":"nodes.d.ts","names":[],"sources":["../../src/internal/nodes.ts"],"mappings":";;;;YA4BY;;qBAGC,iBAAe,OAAA;YAShB,yBAAyB,gBAAgB;;iBAWpC;WACP;WACA,OAAO;;;iBAIA;WACP;WACA;;YAGE,UAAU,UAAU;UAkEtB;WACA;WACA;WACA;WACA;;;uBA4CK,aAAa,KAAK;;;;;;;EAMhC;EAMA,YAAY,eAAe,OAAO;;MAM9B;;;;;;MASA;;MAKA;;MAKA;;;qBAMQ,aAAa;WAChB;EACT;;;qBAMY,cAAc;WACjB;EACT;;;qBAMY,gBAAgB;WACnB;WACQ,IAAI;WACJ;WACA,OAAO;EACxB,YAAY;aACF,IAAI;aACJ;aACA,OAAO;;;;qBAiBL,WAAW;WACd;WACQ;WACA,QAAQ,cAAc;EACvC,YAAY;aACF;aACA,QAAQ,cAAc;;;;qBAQpB,eAAe;WAClB;WACQ;EACjB,YAAY;aAAkB;;;;qBAMlB,cAAc;WACjB;WACQ,QAAQ,cAAc;EACvC,YAAY;aAAkB,QAAQ,cAAc;;;qBAiBxC,YAAY;WACf;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,WAAW;WACd;WACQ,YAAY,cAAc;EAC3C,YAAY,YAAY,cAAc;;qBAM1B,YAAY;WACf;WACQ,WAAW;EAC5B,YAAY,WAAW;;;YASZ,OACX,gBACA,uBACI,OAAO;;;;;;qBAOC,kBAAkB;WACrB;WACQ;WACA,MAChB,cACA,qBACI,OAAO;EACZ,YAAY;aAAkB;aAAuB,KAAK;;;;YAe/C,OACX,OAAO,QAAQ,UAAU,KAAK,SAAS,QAAQ,MAAM,KAAK,MAAM"}
@@ -1,8 +1,9 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
- import { conditionNodes, exceeded } from "./limits.js";
2
+ import { ceilings, exceeded } from "./limits.js";
3
+ import { run } from "./trampoline.js";
4
+ import * as Arr from "effect/Array";
3
5
  import * as Schema from "effect/Schema";
4
6
  import * as Data from "effect/Data";
5
- import * as Arr from "effect/Array";
6
7
  import { hasProperty } from "effect/Predicate";
7
8
  // src/internal/nodes.ts
8
9
  /**
@@ -70,8 +71,9 @@ const isScalar = (value) => value === null || typeof value === "string" || typeo
70
71
  */
71
72
  const checkPath = (path) => {
72
73
  if (typeof path !== "string") throw new Error("A field path must be a string");
73
- const parts = path.split(".", 6).length;
74
- if (parts > 5) throw exceeded("A field path", "fieldPathParts", "parts", parts, 5);
74
+ const most = ceilings.fieldPathParts;
75
+ const parts = path.split(".", most + 1).length;
76
+ if (parts > most) throw exceeded("A field path", "fieldPathParts", "parts", parts, most);
75
77
  return path;
76
78
  };
77
79
  /** Checks an operand, and gives a new one with only its data. */
@@ -102,7 +104,7 @@ const valuesFacts = (values) => checkWeight({
102
104
  weight: 1 + values.length
103
105
  });
104
106
  const checkWeight = (facts) => {
105
- if (facts.weight > 1e5) throw exceeded("A condition", "conditionNodes", "nodes and values", facts.weight, conditionNodes);
107
+ if (facts.weight > ceilings.conditionNodes) throw exceeded("A condition", "conditionNodes", "nodes and values", facts.weight, ceilings.conditionNodes);
106
108
  return facts;
107
109
  };
108
110
  /** The facts of a node with children. It stops a node past the limits. */
@@ -113,12 +115,10 @@ const factsOf = (children) => {
113
115
  ...leaf,
114
116
  weight
115
117
  });
116
- const depth = 1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth));
117
- if (depth > 500) throw exceeded("A condition", "conditionDepth", "levels", depth, 500);
118
118
  return {
119
119
  size,
120
120
  weight,
121
- depth,
121
+ depth: 1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth)),
122
122
  opaque: Arr.some(children, (child) => child.opaque)
123
123
  };
124
124
  };
@@ -266,42 +266,51 @@ const operandJson = (operand) => operand._tag === "Literal" ? {
266
266
  _tag: "Principal",
267
267
  path: operand.path
268
268
  };
269
- /** Gives the JSON form of a condition with no predicate. */
270
- const toJson = (node) => {
271
- switch (node._tag) {
272
- case "True":
273
- case "False": return { _tag: node._tag };
274
- case "Compare": return {
275
- _tag: "Compare",
276
- op: node.op,
277
- field: node.field,
278
- value: operandJson(node.value)
279
- };
280
- case "In": return {
281
- _tag: "In",
282
- field: node.field,
283
- values: Arr.map(node.values, operandJson)
284
- };
285
- case "Exists": return {
286
- _tag: "Exists",
287
- field: node.field
288
- };
289
- case "Under": return {
290
- _tag: "Under",
291
- prefix: Arr.map(node.prefix, operandJson)
292
- };
293
- case "And":
294
- case "Or": return {
295
- _tag: node._tag,
296
- conditions: Arr.map(node.conditions, toJson)
297
- };
298
- case "Not": return {
299
- _tag: "Not",
300
- condition: toJson(node.condition)
301
- };
302
- case "Predicate": throw new Error(`The predicate "${node.name}" can not become JSON. A condition with a predicate has no JSON form.`);
303
- }
304
- };
269
+ /**
270
+ * Gives the JSON form of a condition with no predicate. The walk takes the
271
+ * steps of a trampoline, so a deep condition does not fill the stack.
272
+ */
273
+ const toJson = (root) => run(root.depth, (R) => {
274
+ const go = (node) => {
275
+ switch (node._tag) {
276
+ case "True":
277
+ case "False": return R.succeed({ _tag: node._tag });
278
+ case "Compare": return R.succeed({
279
+ _tag: "Compare",
280
+ op: node.op,
281
+ field: node.field,
282
+ value: operandJson(node.value)
283
+ });
284
+ case "In": return R.succeed({
285
+ _tag: "In",
286
+ field: node.field,
287
+ values: Arr.map(node.values, operandJson)
288
+ });
289
+ case "Exists": return R.succeed({
290
+ _tag: "Exists",
291
+ field: node.field
292
+ });
293
+ case "Under": return R.succeed({
294
+ _tag: "Under",
295
+ prefix: Arr.map(node.prefix, operandJson)
296
+ });
297
+ case "And":
298
+ case "Or": {
299
+ const tag = node._tag;
300
+ return R.map(R.forEach(node.conditions, (child) => R.suspend(() => go(child))), (conditions) => ({
301
+ _tag: tag,
302
+ conditions
303
+ }));
304
+ }
305
+ case "Not": return R.map(R.suspend(() => go(node.condition)), (condition) => ({
306
+ _tag: "Not",
307
+ condition
308
+ }));
309
+ case "Predicate": throw new Error(`The predicate "${node.name}" can not become JSON. A condition with a predicate has no JSON form.`);
310
+ }
311
+ };
312
+ return go(root);
313
+ });
305
314
  /** Tests if a value is a node. Only a constructor of a node makes one. */
306
315
  const isNode = (u) => u instanceof Base;
307
316
  const notNode = () => /* @__PURE__ */ new Error("A condition must be made with a builder or a function of Condition. A plain object is not a condition");
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.js","names":["Limits.exceeded","Limits.conditionNodes"],"sources":["../../src/internal/nodes.ts"],"sourcesContent":["/**\n * The nodes of a condition. Each node is a class, and only its constructor\n * makes one, also when a condition comes from JSON. The constructor checks the\n * data of the node and works out three facts once:\n * - `size`: the number of nodes, with a shared child counted for each place;\n * - `depth`: the number of levels;\n * - `opaque`: the node has a predicate.\n *\n * The constructor stops a node that goes past `conditionNodes` or\n * `conditionDepth`. So a walk over any condition visits at most\n * `conditionNodes` nodes and goes at most `conditionDepth` levels down, and it\n * needs no memo.\n *\n * The facts are private fields, so `Equal` and `Hash` do not read them: two\n * nodes with the same data are equal.\n */\n// A predicate gives an Effect with an error type and a service type that the\n// nodes do not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Limits from \"./limits.ts\"\n\n/** A value that a condition can compare. */\nexport type Scalar = string | number | boolean | null\n\n/** The operators of a `Compare` test. The decoders read this list too. */\nexport const CompareOperator = Schema.Literals([\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n])\n\nexport type CompareOperator = typeof CompareOperator.Type\n\n/**\n * Tests if a value is an operator. It reads the list of the Schema with\n * `includes`: for a short list, this is about twice as fast as\n * `Schema.is`, and a decoder tests each node of a large input.\n */\nexport const isCompareOperator = (u: unknown): u is CompareOperator =>\n\t(CompareOperator.literals as ReadonlyArray<unknown>).includes(u)\n\n/** A fixed value. */\nexport interface Literal {\n\treadonly _tag: \"Literal\"\n\treadonly value: Scalar\n}\n\n/** A field of the principal. */\nexport interface PrincipalRef {\n\treadonly _tag: \"Principal\"\n\treadonly path: string\n}\n\nexport type Operand = Literal | PrincipalRef\n\nconst operands = Data.taggedEnum<Operand>()\n\nexport const literal = (value: Scalar): Literal => operands.Literal({ value })\n\nexport const principalRef = (path: string): PrincipalRef =>\n\toperands.Principal({ path })\n\n/** Tests if a value is a scalar. Any number is one, also `NaN`. */\nexport const isScalar = (value: unknown): value is Scalar =>\n\tvalue === null ||\n\ttypeof value === \"string\" ||\n\ttypeof value === \"number\" ||\n\ttypeof value === \"boolean\"\n\n/**\n * Checks a field path: a string with at most `fieldPathParts` parts. A longer\n * path is never a field of a resource or of the principal.\n */\nconst checkPath = (path: unknown): string => {\n\tif (typeof path !== \"string\") {\n\t\tthrow new Error(\"A field path must be a string\")\n\t}\n\tconst parts = path.split(\".\", Limits.fieldPathParts + 1).length\n\tif (parts > Limits.fieldPathParts) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A field path\",\n\t\t\t\"fieldPathParts\",\n\t\t\t\"parts\",\n\t\t\tparts,\n\t\t\tLimits.fieldPathParts,\n\t\t)\n\t}\n\treturn path\n}\n\n/** Checks an operand, and gives a new one with only its data. */\nconst checkOperand = (operand: unknown): Operand => {\n\tif (hasProperty(operand, \"_tag\")) {\n\t\tif (\n\t\t\toperand._tag === \"Literal\" &&\n\t\t\thasProperty(operand, \"value\") &&\n\t\t\tisScalar(operand.value)\n\t\t) {\n\t\t\treturn literal(operand.value)\n\t\t}\n\t\tif (operand._tag === \"Principal\" && hasProperty(operand, \"path\")) {\n\t\t\treturn principalRef(checkPath(operand.path))\n\t\t}\n\t}\n\tthrow new Error(\n\t\t\"An operand must be a fixed scalar value or a field of the principal\",\n\t)\n}\n\nconst checkOperands = (values: unknown): ReadonlyArray<Operand> => {\n\tif (!Array.isArray(values)) {\n\t\tthrow new Error(\"A list of operands must be an array\")\n\t}\n\treturn Object.freeze(Arr.map(Array.from(values), checkOperand))\n}\n\ninterface Facts {\n\treadonly size: number\n\treadonly weight: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\nconst leaf: Facts = { size: 1, weight: 1, depth: 1, opaque: false }\n\n/**\n * The facts of a test with values, as `In` and `Under`. Each value counts in\n * the weight, since a walk that reads the test reads its values too. It stops\n * a test past the limits.\n */\nconst valuesFacts = (values: ReadonlyArray<Operand>): Facts =>\n\tcheckWeight({ ...leaf, weight: 1 + values.length })\n\nconst checkWeight = (facts: Facts): Facts => {\n\tif (facts.weight > Limits.conditionNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionNodes\",\n\t\t\t\"nodes and values\",\n\t\t\tfacts.weight,\n\t\t\tLimits.conditionNodes,\n\t\t)\n\t}\n\treturn facts\n}\n\n/** The facts of a node with children. It stops a node past the limits. */\nconst factsOf = (children: ReadonlyArray<Node>): Facts => {\n\tconst size = Arr.reduce(children, 1, (total, child) => total + child.size)\n\tconst weight = Arr.reduce(children, 1, (total, child) => total + child.weight)\n\tcheckWeight({ ...leaf, weight })\n\tconst depth =\n\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth))\n\tif (depth > Limits.conditionDepth) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionDepth\",\n\t\t\t\"levels\",\n\t\t\tdepth,\n\t\t\tLimits.conditionDepth,\n\t\t)\n\t}\n\treturn {\n\t\tsize,\n\t\tweight,\n\t\tdepth,\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t}\n}\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\t/**\n\t * The JSON form of the node, for `JSON.stringify`. A number that JSON can\n\t * not hold, as `Infinity`, becomes an object that no decoder takes, in\n\t * place of `null`, which would change the test.\n\t */\n\ttoJSON(): unknown {\n\t\treturn toJson(this as unknown as Node)\n\t}\n\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 a shared child counted for each place. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/**\n\t * The number of nodes and of the values of their `In` and `Under` tests,\n\t * with a shared child counted for each place. A walk that reads the tests\n\t * does this much work, so the limits of the nodes count it.\n\t */\n\tget weight(): number {\n\t\treturn this.#facts.weight\n\t}\n\n\t/** The number of levels. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** The node has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/** Always true. */\nexport class True extends Base {\n\treadonly _tag = \"True\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Always false. */\nexport class False extends Base {\n\treadonly _tag = \"False\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Compares a field with a value. */\nexport class Compare extends Base {\n\treadonly _tag = \"Compare\"\n\tdeclare readonly op: CompareOperator\n\tdeclare readonly field: string\n\tdeclare readonly value: Operand\n\tconstructor(props: {\n\t\treadonly op: CompareOperator\n\t\treadonly field: string\n\t\treadonly value: Operand\n\t}) {\n\t\tif (!isCompareOperator(props.op)) {\n\t\t\tthrow new Error(`\"${String(props.op)}\" is not a compare operator`)\n\t\t}\n\t\tsuper(\n\t\t\t{\n\t\t\t\top: props.op,\n\t\t\t\tfield: checkPath(props.field),\n\t\t\t\tvalue: checkOperand(props.value),\n\t\t\t},\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is equal to one of the values. */\nexport class In extends Base {\n\treadonly _tag = \"In\"\n\tdeclare readonly field: string\n\tdeclare readonly values: ReadonlyArray<Operand>\n\tconstructor(props: {\n\t\treadonly field: string\n\t\treadonly values: ReadonlyArray<Operand>\n\t}) {\n\t\tconst values = checkOperands(props.values)\n\t\tsuper({ field: checkPath(props.field), values }, valuesFacts(values))\n\t}\n}\n\n/** True when a field is not `null` and not missing. */\nexport class Exists extends Base {\n\treadonly _tag = \"Exists\"\n\tdeclare readonly field: string\n\tconstructor(props: { readonly field: string }) {\n\t\tsuper({ field: checkPath(props.field) }, leaf)\n\t}\n}\n\n/** True when the path of the resource starts with the prefix. */\nexport class Under extends Base {\n\treadonly _tag = \"Under\"\n\tdeclare readonly prefix: ReadonlyArray<Operand>\n\tconstructor(props: { readonly prefix: ReadonlyArray<Operand> }) {\n\t\tconst prefix = checkOperands(props.prefix)\n\t\tsuper({ prefix }, valuesFacts(prefix))\n\t}\n}\n\nconst checkChildren = (conditions: unknown): ReadonlyArray<Node> => {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"The children of a node must be an array\")\n\t}\n\tconst children = Array.from(conditions as ReadonlyArray<unknown>)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow notNode()\n\t}\n\treturn Object.freeze(children)\n}\n\nexport class And extends Base {\n\treadonly _tag = \"And\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Or extends Base {\n\treadonly _tag = \"Or\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Not extends Base {\n\treadonly _tag = \"Not\"\n\tdeclare readonly condition: Node\n\tconstructor(condition: Node) {\n\t\tif (!isNode(condition)) {\n\t\t\tthrow notNode()\n\t\t}\n\t\tsuper({ condition }, factsOf([condition]))\n\t}\n}\n\n/** The test of a predicate, with no types. */\nexport type Run = (\n\tvalue: unknown,\n\tprincipal: unknown,\n) => Effect.Effect<boolean, unknown, unknown>\n\n/**\n * A test that runs an `Effect`. Its test takes inputs of the type `never`, so\n * no code can call it with a value of the wrong type: `Condition.evaluate`\n * checks the input. Use {@link runOf} inside the library.\n */\nexport class Predicate extends Base {\n\treadonly _tag = \"Predicate\"\n\tdeclare readonly name: string\n\tdeclare readonly run: (\n\t\tvalue: never,\n\t\tprincipal: never,\n\t) => Effect.Effect<boolean, unknown, unknown>\n\tconstructor(props: { readonly name: string; readonly run: Run }) {\n\t\tif (typeof props.name !== \"string\" || typeof props.run !== \"function\") {\n\t\t\tthrow new Error(\"A predicate needs a name and a test\")\n\t\t}\n\t\tsuper(\n\t\t\t{ name: props.name, run: props.run },\n\t\t\t{ size: 1, weight: 1, depth: 1, opaque: true },\n\t\t)\n\t}\n}\n\n/** Gives the test of a predicate, with no types. */\nexport const runOf = (node: Predicate): Run => node.run as Run\n\n/** A node of a condition. */\nexport type Node =\n\tTrue | False | Compare | In | Exists | Under | And | Or | Not | Predicate\n\nconst operandJson = (operand: Operand): unknown =>\n\toperand._tag === \"Literal\"\n\t\t? {\n\t\t\t\t_tag: \"Literal\",\n\t\t\t\t// JSON has no `Infinity`, `-Infinity` or `NaN`. Such a number\n\t\t\t\t// becomes an object that the decoder does not take.\n\t\t\t\tvalue:\n\t\t\t\t\ttypeof operand.value === \"number\" && !Number.isFinite(operand.value)\n\t\t\t\t\t\t? { nonFinite: String(operand.value) }\n\t\t\t\t\t\t: operand.value,\n\t\t\t}\n\t\t: { _tag: \"Principal\", path: operand.path }\n\n/** Gives the JSON form of a condition with no predicate. */\nexport const toJson = (node: Node): unknown => {\n\tswitch (node._tag) {\n\t\tcase \"True\":\n\t\tcase \"False\":\n\t\t\treturn { _tag: node._tag }\n\t\tcase \"Compare\":\n\t\t\treturn {\n\t\t\t\t_tag: \"Compare\",\n\t\t\t\top: node.op,\n\t\t\t\tfield: node.field,\n\t\t\t\tvalue: operandJson(node.value),\n\t\t\t}\n\t\tcase \"In\":\n\t\t\treturn {\n\t\t\t\t_tag: \"In\",\n\t\t\t\tfield: node.field,\n\t\t\t\tvalues: Arr.map(node.values, operandJson),\n\t\t\t}\n\t\tcase \"Exists\":\n\t\t\treturn { _tag: \"Exists\", field: node.field }\n\t\tcase \"Under\":\n\t\t\treturn { _tag: \"Under\", prefix: Arr.map(node.prefix, operandJson) }\n\t\tcase \"And\":\n\t\tcase \"Or\":\n\t\t\treturn { _tag: node._tag, conditions: Arr.map(node.conditions, toJson) }\n\t\tcase \"Not\":\n\t\t\treturn { _tag: \"Not\", condition: toJson(node.condition) }\n\t\tcase \"Predicate\":\n\t\t\tthrow new Error(\n\t\t\t\t`The predicate \"${node.name}\" can not become JSON. A condition with a predicate has no JSON form.`,\n\t\t\t)\n\t}\n}\n\n/** Tests if a value is a node. Only a constructor of a node makes one. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\nconst notNode = (): Error =>\n\tnew Error(\n\t\t\"A condition must be made with a builder or a function of Condition. A plain object is not a condition\",\n\t)\n\n/** Gives a value as a node, or throws when it is not a node. */\nexport const toNode = (u: unknown): Node => {\n\tif (!isNode(u)) {\n\t\tthrow notNode()\n\t}\n\treturn u\n}\n\nexport const always: Node = new True()\n\nexport const never: Node = new False()\n"],"mappings":"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"}
1
+ {"version":3,"file":"nodes.js","names":["Limits.exceeded","Trampoline.run"],"sources":["../../src/internal/nodes.ts"],"sourcesContent":["/**\n * The nodes of a condition. Each node is a class, and only its constructor\n * makes one, also when a condition comes from JSON. The constructor checks the\n * data of the node and works out three facts once:\n * - `size`: the number of nodes, with a shared child counted for each place;\n * - `depth`: the number of levels;\n * - `opaque`: the node has a predicate.\n *\n * The constructor stops a node that goes past `conditionNodes` or\n * `conditionDepth`. So a walk over any condition visits at most\n * `conditionNodes` nodes and goes at most `conditionDepth` levels down, and it\n * needs no memo.\n *\n * The facts are private fields, so `Equal` and `Hash` do not read them: two\n * nodes with the same data are equal.\n */\n// A predicate gives an Effect with an error type and a service type that the\n// nodes do not know, so they are `unknown` here.\n// @effect-diagnostics anyUnknownInErrorContext:off\nimport * as Arr from \"effect/Array\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport { hasProperty } from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Limits from \"./limits.ts\"\nimport * as Trampoline from \"./trampoline.ts\"\n\n/** A value that a condition can compare. */\nexport type Scalar = string | number | boolean | null\n\n/** The operators of a `Compare` test. The decoders read this list too. */\nexport const CompareOperator = Schema.Literals([\n\t\"eq\",\n\t\"ne\",\n\t\"lt\",\n\t\"lte\",\n\t\"gt\",\n\t\"gte\",\n])\n\nexport type CompareOperator = typeof CompareOperator.Type\n\n/**\n * Tests if a value is an operator. It reads the list of the Schema with\n * `includes`: for a short list, this is about twice as fast as\n * `Schema.is`, and a decoder tests each node of a large input.\n */\nexport const isCompareOperator = (u: unknown): u is CompareOperator =>\n\t(CompareOperator.literals as ReadonlyArray<unknown>).includes(u)\n\n/** A fixed value. */\nexport interface Literal {\n\treadonly _tag: \"Literal\"\n\treadonly value: Scalar\n}\n\n/** A field of the principal. */\nexport interface PrincipalRef {\n\treadonly _tag: \"Principal\"\n\treadonly path: string\n}\n\nexport type Operand = Literal | PrincipalRef\n\nconst operands = Data.taggedEnum<Operand>()\n\nexport const literal = (value: Scalar): Literal => operands.Literal({ value })\n\nexport const principalRef = (path: string): PrincipalRef =>\n\toperands.Principal({ path })\n\n/** Tests if a value is a scalar. Any number is one, also `NaN`. */\nexport const isScalar = (value: unknown): value is Scalar =>\n\tvalue === null ||\n\ttypeof value === \"string\" ||\n\ttypeof value === \"number\" ||\n\ttypeof value === \"boolean\"\n\n/**\n * Checks a field path: a string with at most `fieldPathParts` parts. A longer\n * path is never a field of a resource or of the principal.\n */\nconst checkPath = (path: unknown): string => {\n\tif (typeof path !== \"string\") {\n\t\tthrow new Error(\"A field path must be a string\")\n\t}\n\t// A node does not know its domain, so it checks the ceiling. The binding\n\t// to a domain checks the limit of the domain.\n\tconst most = Limits.ceilings.fieldPathParts\n\tconst parts = path.split(\".\", most + 1).length\n\tif (parts > most) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A field path\",\n\t\t\t\"fieldPathParts\",\n\t\t\t\"parts\",\n\t\t\tparts,\n\t\t\tmost,\n\t\t)\n\t}\n\treturn path\n}\n\n/** Checks an operand, and gives a new one with only its data. */\nconst checkOperand = (operand: unknown): Operand => {\n\tif (hasProperty(operand, \"_tag\")) {\n\t\tif (\n\t\t\toperand._tag === \"Literal\" &&\n\t\t\thasProperty(operand, \"value\") &&\n\t\t\tisScalar(operand.value)\n\t\t) {\n\t\t\treturn literal(operand.value)\n\t\t}\n\t\tif (operand._tag === \"Principal\" && hasProperty(operand, \"path\")) {\n\t\t\treturn principalRef(checkPath(operand.path))\n\t\t}\n\t}\n\tthrow new Error(\n\t\t\"An operand must be a fixed scalar value or a field of the principal\",\n\t)\n}\n\nconst checkOperands = (values: unknown): ReadonlyArray<Operand> => {\n\tif (!Array.isArray(values)) {\n\t\tthrow new Error(\"A list of operands must be an array\")\n\t}\n\treturn Object.freeze(Arr.map(Array.from(values), checkOperand))\n}\n\ninterface Facts {\n\treadonly size: number\n\treadonly weight: number\n\treadonly depth: number\n\treadonly opaque: boolean\n}\n\nconst leaf: Facts = { size: 1, weight: 1, depth: 1, opaque: false }\n\n/**\n * The facts of a test with values, as `In` and `Under`. Each value counts in\n * the weight, since a walk that reads the test reads its values too. It stops\n * a test past the limits.\n */\nconst valuesFacts = (values: ReadonlyArray<Operand>): Facts =>\n\tcheckWeight({ ...leaf, weight: 1 + values.length })\n\nconst checkWeight = (facts: Facts): Facts => {\n\t// A node does not know its domain, so it checks the ceiling. The binding\n\t// to a domain, and a decoder, check their limits.\n\tif (facts.weight > Limits.ceilings.conditionNodes) {\n\t\tthrow Limits.exceeded(\n\t\t\t\"A condition\",\n\t\t\t\"conditionNodes\",\n\t\t\t\"nodes and values\",\n\t\t\tfacts.weight,\n\t\t\tLimits.ceilings.conditionNodes,\n\t\t)\n\t}\n\treturn facts\n}\n\n/** The facts of a node with children. It stops a node past the limits. */\nconst factsOf = (children: ReadonlyArray<Node>): Facts => {\n\tconst size = Arr.reduce(children, 1, (total, child) => total + child.size)\n\tconst weight = Arr.reduce(children, 1, (total, child) => total + child.weight)\n\tcheckWeight({ ...leaf, weight })\n\tconst depth =\n\t\t1 + Arr.reduce(children, 0, (top, child) => Math.max(top, child.depth))\n\treturn {\n\t\tsize,\n\t\tweight,\n\t\tdepth,\n\t\topaque: Arr.some(children, (child) => child.opaque),\n\t}\n}\n\n/** The base of every node. It keeps the facts. */\nabstract class Base extends Data.Class {\n\t/**\n\t * The JSON form of the node, for `JSON.stringify`. A number that JSON can\n\t * not hold, as `Infinity`, becomes an object that no decoder takes, in\n\t * place of `null`, which would change the test.\n\t */\n\ttoJSON(): unknown {\n\t\treturn toJson(this as unknown as Node)\n\t}\n\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 a shared child counted for each place. */\n\tget size(): number {\n\t\treturn this.#facts.size\n\t}\n\n\t/**\n\t * The number of nodes and of the values of their `In` and `Under` tests,\n\t * with a shared child counted for each place. A walk that reads the tests\n\t * does this much work, so the limits of the nodes count it.\n\t */\n\tget weight(): number {\n\t\treturn this.#facts.weight\n\t}\n\n\t/** The number of levels. */\n\tget depth(): number {\n\t\treturn this.#facts.depth\n\t}\n\n\t/** The node has a predicate. */\n\tget opaque(): boolean {\n\t\treturn this.#facts.opaque\n\t}\n}\n\n/** Always true. */\nexport class True extends Base {\n\treadonly _tag = \"True\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Always false. */\nexport class False extends Base {\n\treadonly _tag = \"False\"\n\tconstructor() {\n\t\tsuper({}, leaf)\n\t}\n}\n\n/** Compares a field with a value. */\nexport class Compare extends Base {\n\treadonly _tag = \"Compare\"\n\tdeclare readonly op: CompareOperator\n\tdeclare readonly field: string\n\tdeclare readonly value: Operand\n\tconstructor(props: {\n\t\treadonly op: CompareOperator\n\t\treadonly field: string\n\t\treadonly value: Operand\n\t}) {\n\t\tif (!isCompareOperator(props.op)) {\n\t\t\tthrow new Error(`\"${String(props.op)}\" is not a compare operator`)\n\t\t}\n\t\tsuper(\n\t\t\t{\n\t\t\t\top: props.op,\n\t\t\t\tfield: checkPath(props.field),\n\t\t\t\tvalue: checkOperand(props.value),\n\t\t\t},\n\t\t\tleaf,\n\t\t)\n\t}\n}\n\n/** True when a field is equal to one of the values. */\nexport class In extends Base {\n\treadonly _tag = \"In\"\n\tdeclare readonly field: string\n\tdeclare readonly values: ReadonlyArray<Operand>\n\tconstructor(props: {\n\t\treadonly field: string\n\t\treadonly values: ReadonlyArray<Operand>\n\t}) {\n\t\tconst values = checkOperands(props.values)\n\t\tsuper({ field: checkPath(props.field), values }, valuesFacts(values))\n\t}\n}\n\n/** True when a field is not `null` and not missing. */\nexport class Exists extends Base {\n\treadonly _tag = \"Exists\"\n\tdeclare readonly field: string\n\tconstructor(props: { readonly field: string }) {\n\t\tsuper({ field: checkPath(props.field) }, leaf)\n\t}\n}\n\n/** True when the path of the resource starts with the prefix. */\nexport class Under extends Base {\n\treadonly _tag = \"Under\"\n\tdeclare readonly prefix: ReadonlyArray<Operand>\n\tconstructor(props: { readonly prefix: ReadonlyArray<Operand> }) {\n\t\tconst prefix = checkOperands(props.prefix)\n\t\tsuper({ prefix }, valuesFacts(prefix))\n\t}\n}\n\nconst checkChildren = (conditions: unknown): ReadonlyArray<Node> => {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"The children of a node must be an array\")\n\t}\n\tconst children = Array.from(conditions as ReadonlyArray<unknown>)\n\tif (!Arr.every(children, isNode)) {\n\t\tthrow notNode()\n\t}\n\treturn Object.freeze(children)\n}\n\nexport class And extends Base {\n\treadonly _tag = \"And\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Or extends Base {\n\treadonly _tag = \"Or\"\n\tdeclare readonly conditions: ReadonlyArray<Node>\n\tconstructor(conditions: ReadonlyArray<Node>) {\n\t\tconst children = checkChildren(conditions)\n\t\tsuper({ conditions: children }, factsOf(children))\n\t}\n}\n\nexport class Not extends Base {\n\treadonly _tag = \"Not\"\n\tdeclare readonly condition: Node\n\tconstructor(condition: Node) {\n\t\tif (!isNode(condition)) {\n\t\t\tthrow notNode()\n\t\t}\n\t\tsuper({ condition }, factsOf([condition]))\n\t}\n}\n\n/** The test of a predicate, with no types. */\nexport type Run = (\n\tvalue: unknown,\n\tprincipal: unknown,\n) => Effect.Effect<boolean, unknown, unknown>\n\n/**\n * A test that runs an `Effect`. Its test takes inputs of the type `never`, so\n * no code can call it with a value of the wrong type: `Condition.evaluate`\n * checks the input. Use {@link runOf} inside the library.\n */\nexport class Predicate extends Base {\n\treadonly _tag = \"Predicate\"\n\tdeclare readonly name: string\n\tdeclare readonly run: (\n\t\tvalue: never,\n\t\tprincipal: never,\n\t) => Effect.Effect<boolean, unknown, unknown>\n\tconstructor(props: { readonly name: string; readonly run: Run }) {\n\t\tif (typeof props.name !== \"string\" || typeof props.run !== \"function\") {\n\t\t\tthrow new Error(\"A predicate needs a name and a test\")\n\t\t}\n\t\tsuper(\n\t\t\t{ name: props.name, run: props.run },\n\t\t\t{ size: 1, weight: 1, depth: 1, opaque: true },\n\t\t)\n\t}\n}\n\n/** Gives the test of a predicate, with no types. */\nexport const runOf = (node: Predicate): Run => node.run as Run\n\n/** A node of a condition. */\nexport type Node =\n\tTrue | False | Compare | In | Exists | Under | And | Or | Not | Predicate\n\nconst operandJson = (operand: Operand): unknown =>\n\toperand._tag === \"Literal\"\n\t\t? {\n\t\t\t\t_tag: \"Literal\",\n\t\t\t\t// JSON has no `Infinity`, `-Infinity` or `NaN`. Such a number\n\t\t\t\t// becomes an object that the decoder does not take.\n\t\t\t\tvalue:\n\t\t\t\t\ttypeof operand.value === \"number\" && !Number.isFinite(operand.value)\n\t\t\t\t\t\t? { nonFinite: String(operand.value) }\n\t\t\t\t\t\t: operand.value,\n\t\t\t}\n\t\t: { _tag: \"Principal\", path: operand.path }\n\n/**\n * Gives the JSON form of a condition with no predicate. The walk takes the\n * steps of a trampoline, so a deep condition does not fill the stack.\n */\nexport const toJson = (root: Node): unknown =>\n\tTrampoline.run(root.depth, (R) => {\n\t\tconst go = (node: Node): Trampoline.Step<unknown> => {\n\t\t\tswitch (node._tag) {\n\t\t\t\tcase \"True\":\n\t\t\t\tcase \"False\":\n\t\t\t\t\treturn R.succeed({ _tag: node._tag })\n\t\t\t\tcase \"Compare\":\n\t\t\t\t\treturn R.succeed({\n\t\t\t\t\t\t_tag: \"Compare\",\n\t\t\t\t\t\top: node.op,\n\t\t\t\t\t\tfield: node.field,\n\t\t\t\t\t\tvalue: operandJson(node.value),\n\t\t\t\t\t})\n\t\t\t\tcase \"In\":\n\t\t\t\t\treturn R.succeed({\n\t\t\t\t\t\t_tag: \"In\",\n\t\t\t\t\t\tfield: node.field,\n\t\t\t\t\t\tvalues: Arr.map(node.values, operandJson),\n\t\t\t\t\t})\n\t\t\t\tcase \"Exists\":\n\t\t\t\t\treturn R.succeed({ _tag: \"Exists\", field: node.field })\n\t\t\t\tcase \"Under\":\n\t\t\t\t\treturn R.succeed({\n\t\t\t\t\t\t_tag: \"Under\",\n\t\t\t\t\t\tprefix: Arr.map(node.prefix, operandJson),\n\t\t\t\t\t})\n\t\t\t\tcase \"And\":\n\t\t\t\tcase \"Or\": {\n\t\t\t\t\tconst tag = node._tag\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.forEach(node.conditions, (child) => R.suspend(() => go(child))),\n\t\t\t\t\t\t(conditions) => ({ _tag: tag, conditions }),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tcase \"Not\":\n\t\t\t\t\treturn R.map(\n\t\t\t\t\t\tR.suspend(() => go(node.condition)),\n\t\t\t\t\t\t(condition) => ({\n\t\t\t\t\t\t\t_tag: \"Not\",\n\t\t\t\t\t\t\tcondition,\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\tcase \"Predicate\":\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`The predicate \"${node.name}\" can not become JSON. A condition with a predicate has no JSON form.`,\n\t\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn go(root)\n\t})\n\n/** Tests if a value is a node. Only a constructor of a node makes one. */\nexport const isNode = (u: unknown): u is Node => u instanceof Base\n\nconst notNode = (): Error =>\n\tnew Error(\n\t\t\"A condition must be made with a builder or a function of Condition. A plain object is not a condition\",\n\t)\n\n/** Gives a value as a node, or throws when it is not a node. */\nexport const toNode = (u: unknown): Node => {\n\tif (!isNode(u)) {\n\t\tthrow notNode()\n\t}\n\treturn u\n}\n\nexport const always: Node = new True()\n\nexport const never: Node = new False()\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
+ import { ceilings, fieldPathParts, patternParts } from "./limits.js";
1
2
  import { keyFits } from "./catalog.js";
2
- import * as Result from "effect/Result";
3
3
  import * as Arr from "effect/Array";
4
+ import * as Result from "effect/Result";
5
+ import * as Option from "effect/Option";
4
6
  // src/internal/patterns.ts
5
7
  /**
6
8
  * Field patterns, as in `settings.*`, `secret*` or `**.secret`. A pattern is
@@ -27,19 +29,21 @@ const partOf = (text) => text === "**" ? globstar : text.includes("*") ? {
27
29
  };
28
30
  /**
29
31
  * Reads a pattern. It fails with the problem when the pattern has `**`
30
- * inside a part, or has more parts than `patternParts`.
32
+ * inside a part, or has more parts than `parts`, the `patternParts` of a
33
+ * domain.
31
34
  */
32
- const parse = (text) => {
33
- const texts = text.split(".", 17);
34
- if (texts.length > 16) return Result.fail(`The field pattern "${text}" has more than ${String(16)} parts. The most is ${String(16)}.`);
35
+ const parse = (text, parts = patternParts) => {
36
+ const texts = text.split(".", parts + 1);
37
+ if (texts.length > parts) return Result.fail(`The field pattern "${text}" has more than ${String(parts)} parts. The most is ${String(parts)}.`);
35
38
  if (Arr.some(texts, (part) => part !== "**" && part.includes("**"))) return Result.fail(`The field pattern "${text}" has "**" inside a part. "**" must be a whole part`);
36
39
  return Result.succeed(Arr.dedupeAdjacentWith(Arr.map(texts, partOf), (left, right) => left._tag === "Globstar" && right._tag === "Globstar"));
37
40
  };
38
41
  /**
39
- * Reads a pattern of a bound rule. The binding of the rule took the pattern,
40
- * so a problem here is a defect.
42
+ * Reads a pattern of a bound rule. The binding of the rule took the pattern
43
+ * with the limits of its domain, so this reads it up to the ceiling, and a
44
+ * problem here is a defect.
41
45
  */
42
- const parseBound = (text) => Result.getOrThrowWith(parse(text), (problem) => new Error(problem));
46
+ const parseBound = (text) => Result.getOrThrowWith(parse(text, ceilings.patternParts), (problem) => new Error(problem));
43
47
  /** Tests if a name matches the pieces of a part with stars. */
44
48
  const globMatches = (pieces, name) => {
45
49
  const first = pieces[0];
@@ -96,14 +100,45 @@ const matches = (pattern, names) => {
96
100
  * the names of the field.
97
101
  */
98
102
  const matchesBelow = (pattern, names) => Arr.some(Arr.reduce(names, start(pattern), (states, name) => step(pattern, states, name)), (state) => state < pattern.length);
99
- /** Tests if a pattern matches some field of a catalog. */
100
- const fitsCatalog = (pattern, catalog) => {
101
- const below = (branch, states) => states.length > 0 && (Arr.some(branch.children, ([name, child]) => visit(child, name, states)) || branch.key !== void 0 && branch.keyBranch !== void 0 && visit(branch.keyBranch, branch.key, states));
102
- const visit = (branch, segment, before) => {
103
- const states = step(pattern, before, segment);
104
- return branch.field !== void 0 && isDone(pattern, states) || below(branch, states);
103
+ /**
104
+ * Tests if a pattern matches some field of a catalog, with at most `parts`
105
+ * parts. A recursive Schema has no end of fields, so the search goes over the
106
+ * pairs of a place of the catalog and one place of the match. There are at
107
+ * most the places of the catalog times the parts of the pattern of them, so
108
+ * the work is the steps of the catalog times the parts of the pattern, as the
109
+ * binding counts it. Each pair comes one time, a level after a level, so the
110
+ * search finds the shortest field first, and it keeps a queue, so a deep
111
+ * Schema does not fill the stack. The set of the pairs is native, and the set
112
+ * and the queue belong to this call.
113
+ */
114
+ const fitsCatalog = (pattern, catalog, parts = fieldPathParts) => {
115
+ const seen = /* @__PURE__ */ new Set();
116
+ const visits = (id, states, depth) => Arr.flatMap(states, (state) => {
117
+ const key = `${String(id)}|${String(state)}`;
118
+ if (seen.has(key)) return [];
119
+ seen.add(key);
120
+ return [{
121
+ branch: catalog.branches[id],
122
+ state,
123
+ depth
124
+ }];
125
+ });
126
+ const next = (visit) => {
127
+ if (visit.depth === parts) return [];
128
+ const branch = visit.branch;
129
+ const steps = branch.key === void 0 || branch.keyBranch === void 0 ? branch.children : [...branch.children, [branch.key, branch.keyBranch]];
130
+ return Arr.flatMap(steps, ([segment, id]) => visits(id, step(pattern, [visit.state], segment), visit.depth + 1));
105
131
  };
106
- return below(catalog.root, start(pattern));
132
+ const queue = [...visits(catalog.root.id, start(pattern), 0)];
133
+ return Arr.some(Arr.unfold(0, (index) => {
134
+ const visit = queue[index];
135
+ if (visit === void 0) return Option.none();
136
+ if (visit.depth > 0 && visit.state === pattern.length) return Option.some([true, queue.length]);
137
+ Arr.forEach(next(visit), (found) => {
138
+ queue.push(found);
139
+ });
140
+ return Option.some([false, index + 1]);
141
+ }), (match) => match);
107
142
  };
108
143
  export { fitsCatalog, matches, matchesBelow, parse, parseBound };
109
144
 
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.js","names":["Catalog.keyFits"],"sources":["../../src/internal/patterns.ts"],"sourcesContent":["/**\n * Field patterns, as in `settings.*`, `secret*` or `**.secret`. A pattern is\n * a list of parts with dots between them:\n * - A name with no star matches that name.\n * - A part with stars matches a name, and each star matches any text with no\n * dot, as `secret*` matches `secretKey`.\n * - `**` matches any number of names, also none. It must be a whole part.\n *\n * A pattern that matches a field matches each field below it too. So `pay`\n * matches `pay.amount`, as `pay.**` does.\n *\n * The match goes one name at a time, and it keeps the set of places in the\n * pattern that it may be at. It does not go back, so the time is at most the\n * number of names times the number of parts.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Result from \"effect/Result\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\n\n/** One part of a pattern. */\nexport type Part =\n\t| { readonly _tag: \"Name\"; readonly name: string }\n\t/** The texts between the stars of the part. */\n\t| { readonly _tag: \"Glob\"; readonly pieces: ReadonlyArray<string> }\n\t| { readonly _tag: \"Globstar\" }\n\n/** A pattern, as a list of parts. Two `**` parts are never next to each other. */\nexport type Pattern = ReadonlyArray<Part>\n\nconst globstar: Part = { _tag: \"Globstar\" }\n\nconst partOf = (text: string): Part =>\n\ttext === \"**\"\n\t\t? globstar\n\t\t: text.includes(\"*\")\n\t\t\t? { _tag: \"Glob\", pieces: text.split(\"*\") }\n\t\t\t: { _tag: \"Name\", name: text }\n\n/**\n * Reads a pattern. It fails with the problem when the pattern has `**`\n * inside a part, or has more parts than `patternParts`.\n */\nexport const parse = (text: string): Result.Result<Pattern, string> => {\n\tconst texts = text.split(\".\", Limits.patternParts + 1)\n\tif (texts.length > Limits.patternParts) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has more than ${String(Limits.patternParts)} parts. The most is ${String(Limits.patternParts)}.`,\n\t\t)\n\t}\n\tif (Arr.some(texts, (part) => part !== \"**\" && part.includes(\"**\"))) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has \"**\" inside a part. \"**\" must be a whole part`,\n\t\t)\n\t}\n\t// `a.**.**` matches what `a.**` matches.\n\treturn Result.succeed(\n\t\tArr.dedupeAdjacentWith(\n\t\t\tArr.map(texts, partOf),\n\t\t\t(left, right) => left._tag === \"Globstar\" && right._tag === \"Globstar\",\n\t\t),\n\t)\n}\n\n/**\n * Reads a pattern of a bound rule. The binding of the rule took the pattern,\n * so a problem here is a defect.\n */\nexport const parseBound = (text: string): Pattern =>\n\tResult.getOrThrowWith(parse(text), (problem) => new Error(problem))\n\n/** Tests if a name matches the pieces of a part with stars. */\nconst globMatches = (pieces: ReadonlyArray<string>, name: string): boolean => {\n\tconst first = pieces[0]!\n\tconst last = pieces[pieces.length - 1]!\n\tconst middle = pieces.slice(1, -1)\n\tif (\n\t\tname.length < first.length + last.length ||\n\t\t!name.startsWith(first) ||\n\t\t!name.endsWith(last)\n\t) {\n\t\treturn false\n\t}\n\t// Each piece in the middle is found as early as it can be. An earlier\n\t// place never leaves less room for the pieces after it.\n\tconst end = name.length - last.length\n\tconst place = Arr.reduce(middle, first.length as number, (at, piece) => {\n\t\tif (at < 0) {\n\t\t\treturn at\n\t\t}\n\t\tconst found = name.indexOf(piece, at)\n\t\treturn found < 0 || found + piece.length > end ? -1 : found + piece.length\n\t})\n\treturn place >= 0\n}\n\n/**\n * Tests if a part matches one segment of a path. A part with stars matches\n * any key of a record. A name matches a key of a record when the Schema of\n * the keys takes it.\n */\nconst partFits = (part: Part, segment: Catalog.Segment): boolean => {\n\tswitch (part._tag) {\n\t\tcase \"Globstar\":\n\t\t\treturn true\n\t\tcase \"Glob\":\n\t\t\treturn typeof segment !== \"string\" || globMatches(part.pieces, segment)\n\t\tcase \"Name\":\n\t\t\treturn typeof segment === \"string\"\n\t\t\t\t? part.name === segment\n\t\t\t\t: Catalog.keyFits(segment, part.name)\n\t}\n}\n\n/** The places of a match: the parts that it may take next. */\ntype States = ReadonlyArray<number>\n\n/** Adds the place after each `**`, since a `**` may match no names. */\nconst close = (pattern: Pattern, states: States): States =>\n\t// A native `Set` keeps the first copy of each place, in order. It is\n\t// faster than `Arr.dedupe`, and this is on the path of each match.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(states, (state) =>\n\t\t\t\tpattern[state]?._tag === \"Globstar\" ? [state, state + 1] : [state],\n\t\t\t),\n\t\t),\n\t)\n\nconst start = (pattern: Pattern): States => close(pattern, [0])\n\n/** Gives the places after one more segment. A `**` that takes it stays. */\nconst step = (\n\tpattern: Pattern,\n\tstates: States,\n\tsegment: Catalog.Segment,\n): States =>\n\tclose(\n\t\tpattern,\n\t\tArr.flatMap(states, (state) => {\n\t\t\tconst part = pattern[state]\n\t\t\treturn part === undefined\n\t\t\t\t? []\n\t\t\t\t: part._tag === \"Globstar\"\n\t\t\t\t\t? [state]\n\t\t\t\t\t: partFits(part, segment)\n\t\t\t\t\t\t? [state + 1]\n\t\t\t\t\t\t: []\n\t\t}),\n\t)\n\nconst isDone = (pattern: Pattern, states: States): boolean =>\n\tstates.includes(pattern.length)\n\n/**\n * Tests if a pattern matches a field, or a field above it. The field is a\n * list of names.\n */\nexport const matches = (\n\tpattern: Pattern,\n\tnames: ReadonlyArray<string>,\n): boolean => {\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ states: start(pattern), done: isDone(pattern, start(pattern)) },\n\t\t(before, name) => {\n\t\t\tif (before.done || before.states.length === 0) {\n\t\t\t\treturn before\n\t\t\t}\n\t\t\tconst states = step(pattern, before.states, name)\n\t\t\treturn { states, done: isDone(pattern, states) }\n\t\t},\n\t)\n\treturn found.done\n}\n\n/**\n * Tests if a pattern may match a field below a field, with more names after\n * the names of the field.\n */\nexport const matchesBelow = (\n\tpattern: Pattern,\n\tnames: ReadonlyArray<string>,\n): boolean =>\n\tArr.some(\n\t\tArr.reduce(names, start(pattern), (states, name) =>\n\t\t\tstep(pattern, states, name),\n\t\t),\n\t\t(state) => state < pattern.length,\n\t)\n\n/** Tests if a pattern matches some field of a catalog. */\nexport const fitsCatalog = (\n\tpattern: Pattern,\n\tcatalog: Catalog.Catalog,\n): boolean => {\n\tconst below = (branch: Catalog.Branch, states: States): boolean =>\n\t\tstates.length > 0 &&\n\t\t(Arr.some(branch.children, ([name, child]) => visit(child, name, states)) ||\n\t\t\t(branch.key !== undefined &&\n\t\t\t\tbranch.keyBranch !== undefined &&\n\t\t\t\tvisit(branch.keyBranch, branch.key, states)))\n\tconst visit = (\n\t\tbranch: Catalog.Branch,\n\t\tsegment: Catalog.Segment,\n\t\tbefore: States,\n\t): boolean => {\n\t\tconst states = step(pattern, before, segment)\n\t\treturn (\n\t\t\t(branch.field !== undefined && isDone(pattern, states)) ||\n\t\t\tbelow(branch, states)\n\t\t)\n\t}\n\treturn below(catalog.root, start(pattern))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"patterns.js","names":["Limits.patternParts","Catalog.keyFits","Limits.fieldPathParts"],"sources":["../../src/internal/patterns.ts"],"sourcesContent":["/**\n * Field patterns, as in `settings.*`, `secret*` or `**.secret`. A pattern is\n * a list of parts with dots between them:\n * - A name with no star matches that name.\n * - A part with stars matches a name, and each star matches any text with no\n * dot, as `secret*` matches `secretKey`.\n * - `**` matches any number of names, also none. It must be a whole part.\n *\n * A pattern that matches a field matches each field below it too. So `pay`\n * matches `pay.amount`, as `pay.**` does.\n *\n * The match goes one name at a time, and it keeps the set of places in the\n * pattern that it may be at. It does not go back, so the time is at most the\n * number of names times the number of parts.\n */\nimport * as Arr from \"effect/Array\"\nimport * as Option from \"effect/Option\"\nimport * as Result from \"effect/Result\"\nimport * as Catalog from \"./catalog.ts\"\nimport * as Limits from \"./limits.ts\"\n\n/** One part of a pattern. */\nexport type Part =\n\t| { readonly _tag: \"Name\"; readonly name: string }\n\t/** The texts between the stars of the part. */\n\t| { readonly _tag: \"Glob\"; readonly pieces: ReadonlyArray<string> }\n\t| { readonly _tag: \"Globstar\" }\n\n/** A pattern, as a list of parts. Two `**` parts are never next to each other. */\nexport type Pattern = ReadonlyArray<Part>\n\nconst globstar: Part = { _tag: \"Globstar\" }\n\nconst partOf = (text: string): Part =>\n\ttext === \"**\"\n\t\t? globstar\n\t\t: text.includes(\"*\")\n\t\t\t? { _tag: \"Glob\", pieces: text.split(\"*\") }\n\t\t\t: { _tag: \"Name\", name: text }\n\n/**\n * Reads a pattern. It fails with the problem when the pattern has `**`\n * inside a part, or has more parts than `parts`, the `patternParts` of a\n * domain.\n */\nexport const parse = (\n\ttext: string,\n\tparts: number = Limits.patternParts,\n): Result.Result<Pattern, string> => {\n\tconst texts = text.split(\".\", parts + 1)\n\tif (texts.length > parts) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has more than ${String(parts)} parts. The most is ${String(parts)}.`,\n\t\t)\n\t}\n\tif (Arr.some(texts, (part) => part !== \"**\" && part.includes(\"**\"))) {\n\t\treturn Result.fail(\n\t\t\t`The field pattern \"${text}\" has \"**\" inside a part. \"**\" must be a whole part`,\n\t\t)\n\t}\n\t// `a.**.**` matches what `a.**` matches.\n\treturn Result.succeed(\n\t\tArr.dedupeAdjacentWith(\n\t\t\tArr.map(texts, partOf),\n\t\t\t(left, right) => left._tag === \"Globstar\" && right._tag === \"Globstar\",\n\t\t),\n\t)\n}\n\n/**\n * Reads a pattern of a bound rule. The binding of the rule took the pattern\n * with the limits of its domain, so this reads it up to the ceiling, and a\n * problem here is a defect.\n */\nexport const parseBound = (text: string): Pattern =>\n\tResult.getOrThrowWith(\n\t\tparse(text, Limits.ceilings.patternParts),\n\t\t(problem) => new Error(problem),\n\t)\n\n/** Tests if a name matches the pieces of a part with stars. */\nconst globMatches = (pieces: ReadonlyArray<string>, name: string): boolean => {\n\tconst first = pieces[0]!\n\tconst last = pieces[pieces.length - 1]!\n\tconst middle = pieces.slice(1, -1)\n\tif (\n\t\tname.length < first.length + last.length ||\n\t\t!name.startsWith(first) ||\n\t\t!name.endsWith(last)\n\t) {\n\t\treturn false\n\t}\n\t// Each piece in the middle is found as early as it can be. An earlier\n\t// place never leaves less room for the pieces after it.\n\tconst end = name.length - last.length\n\tconst place = Arr.reduce(middle, first.length as number, (at, piece) => {\n\t\tif (at < 0) {\n\t\t\treturn at\n\t\t}\n\t\tconst found = name.indexOf(piece, at)\n\t\treturn found < 0 || found + piece.length > end ? -1 : found + piece.length\n\t})\n\treturn place >= 0\n}\n\n/**\n * Tests if a part matches one segment of a path. A part with stars matches\n * any key of a record. A name matches a key of a record when the Schema of\n * the keys takes it.\n */\nconst partFits = (part: Part, segment: Catalog.Segment): boolean => {\n\tswitch (part._tag) {\n\t\tcase \"Globstar\":\n\t\t\treturn true\n\t\tcase \"Glob\":\n\t\t\treturn typeof segment !== \"string\" || globMatches(part.pieces, segment)\n\t\tcase \"Name\":\n\t\t\treturn typeof segment === \"string\"\n\t\t\t\t? part.name === segment\n\t\t\t\t: Catalog.keyFits(segment, part.name)\n\t}\n}\n\n/** The places of a match: the parts that it may take next. */\ntype States = ReadonlyArray<number>\n\n/** Adds the place after each `**`, since a `**` may match no names. */\nconst close = (pattern: Pattern, states: States): States =>\n\t// A native `Set` keeps the first copy of each place, in order. It is\n\t// faster than `Arr.dedupe`, and this is on the path of each match.\n\tArray.from(\n\t\tnew Set(\n\t\t\tArr.flatMap(states, (state) =>\n\t\t\t\tpattern[state]?._tag === \"Globstar\" ? [state, state + 1] : [state],\n\t\t\t),\n\t\t),\n\t)\n\nconst start = (pattern: Pattern): States => close(pattern, [0])\n\n/** Gives the places after one more segment. A `**` that takes it stays. */\nconst step = (\n\tpattern: Pattern,\n\tstates: States,\n\tsegment: Catalog.Segment,\n): States =>\n\tclose(\n\t\tpattern,\n\t\tArr.flatMap(states, (state) => {\n\t\t\tconst part = pattern[state]\n\t\t\treturn part === undefined\n\t\t\t\t? []\n\t\t\t\t: part._tag === \"Globstar\"\n\t\t\t\t\t? [state]\n\t\t\t\t\t: partFits(part, segment)\n\t\t\t\t\t\t? [state + 1]\n\t\t\t\t\t\t: []\n\t\t}),\n\t)\n\nconst isDone = (pattern: Pattern, states: States): boolean =>\n\tstates.includes(pattern.length)\n\n/**\n * Tests if a pattern matches a field, or a field above it. The field is a\n * list of names.\n */\nexport const matches = (\n\tpattern: Pattern,\n\tnames: ReadonlyArray<string>,\n): boolean => {\n\tconst found = Arr.reduce(\n\t\tnames,\n\t\t{ states: start(pattern), done: isDone(pattern, start(pattern)) },\n\t\t(before, name) => {\n\t\t\tif (before.done || before.states.length === 0) {\n\t\t\t\treturn before\n\t\t\t}\n\t\t\tconst states = step(pattern, before.states, name)\n\t\t\treturn { states, done: isDone(pattern, states) }\n\t\t},\n\t)\n\treturn found.done\n}\n\n/**\n * Tests if a pattern may match a field below a field, with more names after\n * the names of the field.\n */\nexport const matchesBelow = (\n\tpattern: Pattern,\n\tnames: ReadonlyArray<string>,\n): boolean =>\n\tArr.some(\n\t\tArr.reduce(names, start(pattern), (states, name) =>\n\t\t\tstep(pattern, states, name),\n\t\t),\n\t\t(state) => state < pattern.length,\n\t)\n\n/**\n * Tests if a pattern matches some field of a catalog, with at most `parts`\n * parts. A recursive Schema has no end of fields, so the search goes over the\n * pairs of a place of the catalog and one place of the match. There are at\n * most the places of the catalog times the parts of the pattern of them, so\n * the work is the steps of the catalog times the parts of the pattern, as the\n * binding counts it. Each pair comes one time, a level after a level, so the\n * search finds the shortest field first, and it keeps a queue, so a deep\n * Schema does not fill the stack. The set of the pairs is native, and the set\n * and the queue belong to this call.\n */\nexport const fitsCatalog = (\n\tpattern: Pattern,\n\tcatalog: Catalog.Catalog,\n\tparts: number = Limits.fieldPathParts,\n): boolean => {\n\tinterface Visit {\n\t\treadonly branch: Catalog.Branch\n\t\treadonly state: number\n\t\treadonly depth: number\n\t}\n\tconst seen = new Set<string>()\n\tconst visits = (\n\t\tid: number,\n\t\tstates: States,\n\t\tdepth: number,\n\t): ReadonlyArray<Visit> =>\n\t\tArr.flatMap(states, (state) => {\n\t\t\tconst key = `${String(id)}|${String(state)}`\n\t\t\tif (seen.has(key)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tseen.add(key)\n\t\t\treturn [{ branch: catalog.branches[id]!, state, depth }]\n\t\t})\n\tconst next = (visit: Visit): ReadonlyArray<Visit> => {\n\t\tif (visit.depth === parts) {\n\t\t\treturn []\n\t\t}\n\t\tconst branch = visit.branch\n\t\tconst steps: ReadonlyArray<readonly [Catalog.Segment, number]> =\n\t\t\tbranch.key === undefined || branch.keyBranch === undefined\n\t\t\t\t? branch.children\n\t\t\t\t: [...branch.children, [branch.key, branch.keyBranch] as const]\n\t\treturn Arr.flatMap(steps, ([segment, id]) =>\n\t\t\tvisits(id, step(pattern, [visit.state], segment), visit.depth + 1),\n\t\t)\n\t}\n\tconst queue: Array<Visit> = [...visits(catalog.root.id, start(pattern), 0)]\n\t// The walk stops at the first field that the pattern matches. The visits\n\t// go into the queue one at a time: a spread into a call would pass one\n\t// argument for each, past the most arguments of a call.\n\treturn Arr.some(\n\t\tArr.unfold(0, (index) => {\n\t\t\tconst visit = queue[index]\n\t\t\tif (visit === undefined) {\n\t\t\t\treturn Option.none()\n\t\t\t}\n\t\t\tif (visit.depth > 0 && visit.state === pattern.length) {\n\t\t\t\treturn Option.some([true, queue.length] as const)\n\t\t\t}\n\t\t\tArr.forEach(next(visit), (found) => {\n\t\t\t\tqueue.push(found)\n\t\t\t})\n\t\t\treturn Option.some([false, index + 1] as const)\n\t\t}),\n\t\t(match) => match,\n\t)\n}\n"],"mappings":"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"}