@intentius/chant-lexicon-cedar 0.44.8 → 0.44.10

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 (149) hide show
  1. package/README.md +106 -2
  2. package/dist/agentcore/embed.d.ts +189 -0
  3. package/dist/agentcore/embed.d.ts.map +1 -0
  4. package/dist/agentcore/enforcement.d.ts +76 -0
  5. package/dist/agentcore/enforcement.d.ts.map +1 -0
  6. package/dist/agentcore/scan.d.ts +46 -0
  7. package/dist/agentcore/scan.d.ts.map +1 -0
  8. package/dist/codegen/docs-dogwood.d.ts +21 -0
  9. package/dist/codegen/docs-dogwood.d.ts.map +1 -0
  10. package/dist/codegen/docs.d.ts.map +1 -1
  11. package/dist/codegen/package.d.ts.map +1 -1
  12. package/dist/config.d.ts +25 -0
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/dogwood/cli.d.ts +247 -0
  15. package/dist/dogwood/cli.d.ts.map +1 -0
  16. package/dist/dogwood/event-schema.d.ts +161 -0
  17. package/dist/dogwood/event-schema.d.ts.map +1 -0
  18. package/dist/dogwood/index.d.ts +39 -0
  19. package/dist/dogwood/index.d.ts.map +1 -0
  20. package/dist/dogwood/macros.d.ts +96 -0
  21. package/dist/dogwood/macros.d.ts.map +1 -0
  22. package/dist/dogwood/policy.d.ts +120 -0
  23. package/dist/dogwood/policy.d.ts.map +1 -0
  24. package/dist/dogwood/replay-activity.d.ts +196 -0
  25. package/dist/dogwood/replay-activity.d.ts.map +1 -0
  26. package/dist/dogwood/replay-op.d.ts +165 -0
  27. package/dist/dogwood/replay-op.d.ts.map +1 -0
  28. package/dist/dogwood/scan.d.ts +109 -0
  29. package/dist/dogwood/scan.d.ts.map +1 -0
  30. package/dist/dogwood/serialize.d.ts +66 -0
  31. package/dist/dogwood/serialize.d.ts.map +1 -0
  32. package/dist/dogwood/temporal.d.ts +259 -0
  33. package/dist/dogwood/temporal.d.ts.map +1 -0
  34. package/dist/dogwood/trace.d.ts +215 -0
  35. package/dist/dogwood/trace.d.ts.map +1 -0
  36. package/dist/dogwood/upstream.d.ts +41 -0
  37. package/dist/dogwood/upstream.d.ts.map +1 -0
  38. package/dist/dogwood/window.d.ts +73 -0
  39. package/dist/dogwood/window.d.ts.map +1 -0
  40. package/dist/index.d.ts +12 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/integrity.json +15 -3
  43. package/dist/lint/audit-catalog.d.ts.map +1 -1
  44. package/dist/lint/post-synth/dogwood-helpers.d.ts +63 -0
  45. package/dist/lint/post-synth/dogwood-helpers.d.ts.map +1 -0
  46. package/dist/lint/post-synth/dwdc010.d.ts +25 -0
  47. package/dist/lint/post-synth/dwdc010.d.ts.map +1 -0
  48. package/dist/lint/post-synth/dwdc011.d.ts +19 -0
  49. package/dist/lint/post-synth/dwdc011.d.ts.map +1 -0
  50. package/dist/lint/post-synth/dwdc012.d.ts +21 -0
  51. package/dist/lint/post-synth/dwdc012.d.ts.map +1 -0
  52. package/dist/lint/post-synth/dwdc013.d.ts +33 -0
  53. package/dist/lint/post-synth/dwdc013.d.ts.map +1 -0
  54. package/dist/lint/post-synth/dwde010.d.ts +32 -0
  55. package/dist/lint/post-synth/dwde010.d.ts.map +1 -0
  56. package/dist/lint/post-synth/dwde011.d.ts +33 -0
  57. package/dist/lint/post-synth/dwde011.d.ts.map +1 -0
  58. package/dist/lint/post-synth/dwds010.d.ts +24 -0
  59. package/dist/lint/post-synth/dwds010.d.ts.map +1 -0
  60. package/dist/lint/post-synth/index.d.ts.map +1 -1
  61. package/dist/manifest.json +1 -1
  62. package/dist/okf/index.md +7 -0
  63. package/dist/okf/rules/DWDC010.md +11 -0
  64. package/dist/okf/rules/DWDC011.md +11 -0
  65. package/dist/okf/rules/DWDC012.md +11 -0
  66. package/dist/okf/rules/DWDC013.md +15 -0
  67. package/dist/okf/rules/DWDE010.md +11 -0
  68. package/dist/okf/rules/DWDE011.md +11 -0
  69. package/dist/okf/rules/DWDS010.md +11 -0
  70. package/dist/okf/types/Policy.md +1 -0
  71. package/dist/op/activities/index.d.ts +18 -0
  72. package/dist/op/activities/index.d.ts.map +1 -0
  73. package/dist/plugin.d.ts.map +1 -1
  74. package/dist/policy-text.d.ts +53 -0
  75. package/dist/policy-text.d.ts.map +1 -0
  76. package/dist/rules/dogwood-helpers.ts +139 -0
  77. package/dist/rules/dwdc010.ts +62 -0
  78. package/dist/rules/dwdc011.ts +61 -0
  79. package/dist/rules/dwdc012.ts +46 -0
  80. package/dist/rules/dwdc013.ts +64 -0
  81. package/dist/rules/dwde010.ts +130 -0
  82. package/dist/rules/dwde011.ts +108 -0
  83. package/dist/rules/dwds010.ts +46 -0
  84. package/dist/serializer.d.ts +10 -18
  85. package/dist/serializer.d.ts.map +1 -1
  86. package/dist/skills/chant-cedar-authoring.md +180 -0
  87. package/dist/skills/chant-cedar-avp-embedding.md +125 -0
  88. package/dist/skills/chant-cedar-dogwood.md +327 -0
  89. package/dist/skills/chant-cedar-meta-policy.md +119 -0
  90. package/package.json +7 -2
  91. package/src/agentcore/embed.test.ts +254 -0
  92. package/src/agentcore/embed.ts +399 -0
  93. package/src/agentcore/enforcement.test.ts +43 -0
  94. package/src/agentcore/enforcement.ts +92 -0
  95. package/src/agentcore/scan.ts +119 -0
  96. package/src/codegen/docs-dogwood.ts +1119 -0
  97. package/src/codegen/docs.ts +66 -1
  98. package/src/codegen/package.ts +3 -2
  99. package/src/config.test.ts +12 -0
  100. package/src/config.ts +28 -0
  101. package/src/dogwood/cli.test.ts +513 -0
  102. package/src/dogwood/cli.ts +666 -0
  103. package/src/dogwood/event-schema.test.ts +218 -0
  104. package/src/dogwood/event-schema.ts +318 -0
  105. package/src/dogwood/index.ts +271 -0
  106. package/src/dogwood/macros.test.ts +104 -0
  107. package/src/dogwood/macros.ts +229 -0
  108. package/src/dogwood/policy.test.ts +94 -0
  109. package/src/dogwood/policy.ts +141 -0
  110. package/src/dogwood/replay-activity.test.ts +481 -0
  111. package/src/dogwood/replay-activity.ts +506 -0
  112. package/src/dogwood/replay-op.ts +242 -0
  113. package/src/dogwood/scan.ts +287 -0
  114. package/src/dogwood/serialize.test.ts +331 -0
  115. package/src/dogwood/serialize.ts +246 -0
  116. package/src/dogwood/temporal.test.ts +272 -0
  117. package/src/dogwood/temporal.ts +592 -0
  118. package/src/dogwood/testdata/custom-kinds.dwschema +17 -0
  119. package/src/dogwood/testdata/default-macros.dw +23 -0
  120. package/src/dogwood/testdata/lowered-read-after-login.json +13 -0
  121. package/src/dogwood/testdata/max-window-raised.dwschema +25 -0
  122. package/src/dogwood/testdata/pinned.dwschema +31 -0
  123. package/src/dogwood/testdata/read-after-login.cedarschema +20 -0
  124. package/src/dogwood/testdata/read-after-login.dw +17 -0
  125. package/src/dogwood/testdata/temporal-policies.dw +53 -0
  126. package/src/dogwood/trace.test.ts +231 -0
  127. package/src/dogwood/trace.ts +471 -0
  128. package/src/dogwood/upstream.ts +41 -0
  129. package/src/dogwood/window.ts +124 -0
  130. package/src/index.ts +76 -0
  131. package/src/lint/audit-catalog.ts +64 -0
  132. package/src/lint/post-synth/dogwood-helpers.ts +139 -0
  133. package/src/lint/post-synth/dwd-post-synth.test.ts +374 -0
  134. package/src/lint/post-synth/dwdc010.ts +62 -0
  135. package/src/lint/post-synth/dwdc011.ts +61 -0
  136. package/src/lint/post-synth/dwdc012.ts +46 -0
  137. package/src/lint/post-synth/dwdc013.ts +64 -0
  138. package/src/lint/post-synth/dwde-post-synth.test.ts +368 -0
  139. package/src/lint/post-synth/dwde010.ts +130 -0
  140. package/src/lint/post-synth/dwde011.ts +108 -0
  141. package/src/lint/post-synth/dwds010.ts +46 -0
  142. package/src/lint/post-synth/index.ts +14 -0
  143. package/src/lint/post-synth/post-synth.test.ts +7 -3
  144. package/src/op/activities/index.ts +27 -0
  145. package/src/plugin.test.ts +3 -2
  146. package/src/plugin.ts +30 -0
  147. package/src/policy-text.ts +128 -0
  148. package/src/serializer.ts +71 -109
  149. package/src/skills/chant-cedar-dogwood.md +327 -0
@@ -0,0 +1,272 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import {
3
+ and,
4
+ arrayOf,
5
+ bool,
6
+ call,
7
+ compare,
8
+ count,
9
+ ctx,
10
+ decimalOf,
11
+ entityUid,
12
+ exists,
13
+ formerly,
14
+ int,
15
+ interval,
16
+ not,
17
+ predicate,
18
+ previous,
19
+ raw,
20
+ renderCondition,
21
+ renderTerm,
22
+ scopeRef,
23
+ since,
24
+ str,
25
+ sum,
26
+ temporalMarker,
27
+ term,
28
+ tp,
29
+ typedBinder,
30
+ varRef,
31
+ wildcard,
32
+ } from "./temporal";
33
+ import { countDistinctWithin, countWithin, macroCondition, macroWindow, sumWithin } from "./macros";
34
+ import { renderWindow, window, windowSeconds } from "./window";
35
+
36
+ // ── Windows ────────────────────────────────────────────────────────
37
+
38
+ describe("temporal windows", () => {
39
+ test("a window is an integer and one of the four units", () => {
40
+ expect(window("90m")).toEqual({ value: 90, unit: "m" });
41
+ expect(renderWindow({ value: 7, unit: "d" })).toBe("7d");
42
+ });
43
+
44
+ test("a unit outside s/m/h/d is refused", () => {
45
+ // @ts-expect-error — "1w" is not assignable to WindowLike
46
+ expect(() => window("1w")).toThrow(/not a temporal window/);
47
+ });
48
+
49
+ test("windows are comparable across units", () => {
50
+ expect(windowSeconds("90m")).toBe(windowSeconds({ value: 5400, unit: "s" }));
51
+ expect(windowSeconds("2d")).toBeGreaterThan(windowSeconds("24h"));
52
+ });
53
+ });
54
+
55
+ // ── Terms ──────────────────────────────────────────────────────────
56
+
57
+ describe("terms", () => {
58
+ test("literals render in Cedar's surface forms", () => {
59
+ expect(renderTerm(str('he said "hi"'))).toBe('"he said \\"hi\\""');
60
+ expect(renderTerm(int(-3))).toBe("-3");
61
+ expect(renderTerm(bool(true))).toBe("true");
62
+ expect(renderTerm(decimalOf("1.50"))).toBe('decimal("1.50")');
63
+ expect(renderTerm(wildcard())).toBe("*");
64
+ expect(renderTerm(arrayOf(1, 2, str("x")))).toBe('[1, 2, "x"]');
65
+ });
66
+
67
+ test("a non-integer is refused rather than silently truncated", () => {
68
+ expect(() => int(1.5)).toThrow(/whole number/);
69
+ });
70
+
71
+ test("paths and UIDs are distinct constructions, not guessed from a string", () => {
72
+ expect(renderTerm(ctx("input.user"))).toBe("context.input.user");
73
+ expect(renderTerm(scopeRef("principal", "dept"))).toBe("principal.dept");
74
+ expect(renderTerm(entityUid('Drupe::OAuthUser::"alice"'))).toBe('Drupe::OAuthUser::"alice"');
75
+ expect(renderTerm(varRef("total"))).toBe("total");
76
+ });
77
+
78
+ test("a malformed entity UID is refused at construction", () => {
79
+ expect(() => entityUid("Drupe::OAuthUser")).toThrow(/entity UID/);
80
+ });
81
+ });
82
+
83
+ // ── The parser primitives ──────────────────────────────────────────
84
+
85
+ describe("predicates", () => {
86
+ test("action, kind and named arguments", () => {
87
+ const node = predicate('Drupe::Action::"Login"', "response", { "input.user": ctx("input.user") });
88
+ expect(renderCondition(node)).toBe(
89
+ 'Drupe::Action::"Login"::response{ input.user: context.input.user }',
90
+ );
91
+ });
92
+
93
+ test("no arguments still renders the mandatory braces", () => {
94
+ expect(renderCondition(predicate('Drupe::Action::"Login"', "request"))).toBe(
95
+ 'Drupe::Action::"Login"::request{}',
96
+ );
97
+ });
98
+
99
+ test("an unqualified action is refused", () => {
100
+ expect(() => predicate("Login", "request")).toThrow(/predicate action/);
101
+ });
102
+
103
+ test("a field path must be dotted identifiers", () => {
104
+ expect(() => predicate('Ns::Action::"A"', "request", { "input user": int(1) })).toThrow(/field path/);
105
+ });
106
+ });
107
+
108
+ describe("the past-only operators", () => {
109
+ const login = predicate('Drupe::Action::"Login"', "request");
110
+
111
+ test("formerly and previous take their window as an argument", () => {
112
+ expect(renderCondition(formerly("1h", login))).toBe('formerly within 1h Drupe::Action::"Login"::request{}');
113
+ expect(renderCondition(previous("30s", login))).toBe('previous within 30s Drupe::Action::"Login"::request{}');
114
+ });
115
+
116
+ test("since is infix and carries the window on the operator", () => {
117
+ const read = predicate('Drupe::Action::"Read"', "response");
118
+ expect(renderCondition(since(read, "30m", login))).toBe(
119
+ 'Drupe::Action::"Read"::response{} since within 30m Drupe::Action::"Login"::request{}',
120
+ );
121
+ });
122
+
123
+ test("a window is not optional — there is no arity that omits it", () => {
124
+ // @ts-expect-error — formerly() has no one-argument form
125
+ expect(() => formerly(login)).toThrow();
126
+ });
127
+
128
+ test("a macro body may defer the window to its call site", () => {
129
+ expect(renderCondition(formerly(macroWindow("?w"), macroCondition("?s")))).toBe("formerly within ?w ?s");
130
+ });
131
+ });
132
+
133
+ describe("exists, tp and the aggregates", () => {
134
+ test("exists binds one typed variable over a condition", () => {
135
+ const node = exists(typedBinder("total", "Long"), compare(varRef("total"), ">", 100));
136
+ expect(renderCondition(node)).toBe("exists (total: Long). total > 100");
137
+ });
138
+
139
+ test("count names its aggregation domain explicitly", () => {
140
+ const node = count(
141
+ [typedBinder("t", "Timepoint")],
142
+ formerly("1h", and(predicate('Ns::Action::"A"', "request"), tp("t"))),
143
+ );
144
+ expect(renderTerm(node)).toBe(
145
+ 'count for (t: Timepoint). where formerly within 1h (Ns::Action::"A"::request{} && tp(t))',
146
+ );
147
+ });
148
+
149
+ test("sum names both its bound variable and its domain", () => {
150
+ const node = sum(
151
+ "a",
152
+ [typedBinder("a", "Long"), typedBinder("t", "Timepoint")],
153
+ predicate('Ns::Action::"A"', "request", { "input.amount": varRef("a") }),
154
+ );
155
+ expect(renderTerm(node)).toBe(
156
+ 'sum a for (a: Long), (t: Timepoint). where Ns::Action::"A"::request{ input.amount: a }',
157
+ );
158
+ });
159
+
160
+ test("an aggregate with no for-list is refused", () => {
161
+ expect(() => count([], raw("x"))).toThrow(/at least one typed binder/);
162
+ });
163
+ });
164
+
165
+ // ── Precedence ─────────────────────────────────────────────────────
166
+ //
167
+ // The renderer parenthesises rather than relying on the reader knowing the
168
+ // grammar's binding rules. Each of these is a place the tree and the surface
169
+ // syntax would otherwise disagree.
170
+
171
+ describe("parenthesisation", () => {
172
+ const a = predicate('Ns::Action::"A"', "request");
173
+ const b = predicate('Ns::Action::"B"', "request");
174
+ const c = predicate('Ns::Action::"C"', "request");
175
+
176
+ test("an && chain under formerly is parenthesised (atom position)", () => {
177
+ expect(renderCondition(formerly("1h", and(a, b)))).toBe(
178
+ 'formerly within 1h (Ns::Action::"A"::request{} && Ns::Action::"B"::request{})',
179
+ );
180
+ });
181
+
182
+ test("a predicate under formerly is not", () => {
183
+ expect(renderCondition(formerly("1h", a))).toBe('formerly within 1h Ns::Action::"A"::request{}');
184
+ });
185
+
186
+ test("! negates only its operand, so an && chain under it is parenthesised", () => {
187
+ expect(renderCondition(not(and(a, b)))).toBe(
188
+ '!(Ns::Action::"A"::request{} && Ns::Action::"B"::request{})',
189
+ );
190
+ expect(renderCondition(not(a))).toBe('!Ns::Action::"A"::request{}');
191
+ });
192
+
193
+ test("since's left operand is a neg_conjunct, so an && chain there is parenthesised", () => {
194
+ expect(renderCondition(since(and(a, b), "1h", c))).toBe(
195
+ '(Ns::Action::"A"::request{} && Ns::Action::"B"::request{}) since within 1h Ns::Action::"C"::request{}',
196
+ );
197
+ });
198
+
199
+ test("since needs no parentheses as an && operand", () => {
200
+ expect(renderCondition(and(since(a, "1h", b), c))).toBe(
201
+ 'Ns::Action::"A"::request{} since within 1h Ns::Action::"B"::request{} && Ns::Action::"C"::request{}',
202
+ );
203
+ });
204
+
205
+ test("exists binds maximally to the right, so it is fenced inside an && chain", () => {
206
+ const node = and(exists(typedBinder("n", "Long"), compare(varRef("n"), ">", 1)), a);
207
+ expect(renderCondition(node)).toBe('(exists (n: Long). n > 1) && Ns::Action::"A"::request{}');
208
+ });
209
+
210
+ test("an aggregate in comparison position is fenced off from the operator", () => {
211
+ const node = compare(count([typedBinder("t", "Timepoint")], a), "<", 5);
212
+ expect(renderCondition(node)).toBe(
213
+ '(count for (t: Timepoint). where Ns::Action::"A"::request{}) < 5',
214
+ );
215
+ });
216
+
217
+ test("a single-operand and() collapses instead of emitting a bare conjunct", () => {
218
+ expect(renderCondition(and(a))).toBe(renderCondition(a));
219
+ });
220
+
221
+ test("and() with nothing to conjoin is a construction error, not empty text", () => {
222
+ expect(() => and()).toThrow(/at least one operand/);
223
+ });
224
+ });
225
+
226
+ // ── Macro calls ────────────────────────────────────────────────────
227
+
228
+ describe("macro calls", () => {
229
+ const a = predicate('Ns::Action::"A"', "request");
230
+
231
+ test("a call argument may be an interval, a condition or a term", () => {
232
+ expect(renderCondition(call("once", [interval("1h"), a]))).toBe(
233
+ 'once(1h, Ns::Action::"A"::request{})',
234
+ );
235
+ expect(renderCondition(call("is_small", [term("context.input.shares")]))).toBe(
236
+ "is_small(context.input.shares)",
237
+ );
238
+ });
239
+
240
+ test("the interval argument is bare — `within` belongs to the operator", () => {
241
+ expect(renderCondition(call("once", [interval("90m")]))).not.toContain("within");
242
+ });
243
+
244
+ test("the default library's aggregates are calls, not operators", () => {
245
+ expect(renderCondition(countWithin("1h", a))).toBe('count_within(1h, Ns::Action::"A"::request{})');
246
+ expect(renderCondition(sumWithin("amount", "1h", a))).toBe(
247
+ 'sum_within(amount, 1h, Ns::Action::"A"::request{})',
248
+ );
249
+ expect(renderCondition(countDistinctWithin("user", "24h", a))).toBe(
250
+ 'count_distinct_within(user, 24h, Ns::Action::"A"::request{})',
251
+ );
252
+ });
253
+ });
254
+
255
+ // ── The extension marker ───────────────────────────────────────────
256
+
257
+ describe("the temporal marker", () => {
258
+ test("wraps a condition for embedding mid-expression", () => {
259
+ const marker = temporalMarker(formerly("1h", predicate('Ns::Action::"A"', "request")));
260
+ expect(marker).toBe('temporal { formerly within 1h Ns::Action::"A"::request{} }');
261
+ });
262
+ });
263
+
264
+ // ── The escape hatch ───────────────────────────────────────────────
265
+
266
+ describe("raw()", () => {
267
+ test("passes text through, which is why DWDC012 reads the emitted file", () => {
268
+ expect(renderCondition(raw("formerly Ns::Action::\"A\"::request{}"))).toBe(
269
+ 'formerly Ns::Action::"A"::request{}',
270
+ );
271
+ });
272
+ });