@zapier/policy-schema 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,9 +4,10 @@ var zod = require('zod');
4
4
 
5
5
  // src/schema.ts
6
6
  var RUBBERDUCK_POLICY_VERSION = "rc-rubberduck";
7
- var VALID_VERSIONS = [1, 2, 3, RUBBERDUCK_POLICY_VERSION];
7
+ var VALID_VERSIONS = [1, 2, 3, 4, RUBBERDUCK_POLICY_VERSION];
8
8
  var ID_BASED_VERSIONS = /* @__PURE__ */ new Set([
9
9
  3,
10
+ 4,
10
11
  RUBBERDUCK_POLICY_VERSION
11
12
  ]);
12
13
  var VALID_EFFECTS = ["allow", "ask", "deny"];
@@ -191,6 +192,7 @@ var V2StatementSchema = zod.z.object({
191
192
  var StatementSchema = V2StatementSchema;
192
193
  var StatementIdSchema = zod.z.string().regex(/^s_\S+$/, { error: "Statement id must match ^s_\\S+$" }).max(512);
193
194
  var PolicyIdSchema = zod.z.string().regex(/^p_\S+$/, { error: "Policy id must match ^p_\\S+$" }).max(512);
195
+ var RuleIdSchema = zod.z.string().regex(/^r_\S+$/, { error: "Rule id must match ^r_\\S+$" }).max(512);
194
196
  var V1PolicySchema = zod.z.object({
195
197
  version: zod.z.literal(1),
196
198
  statements: zod.z.array(V1StatementSchema)
@@ -227,10 +229,25 @@ var V3PolicySchema = zod.z.object({
227
229
  // empty (e.g. `[[]]` denies everything).
228
230
  statements: zod.z.array(zod.z.array(V3StatementSchema)).min(1).max(V3_MAX_TIERS)
229
231
  }).strict();
232
+ var V4_MAX_TIERS = 3;
233
+ var V4RuleSchema = zod.z.object({
234
+ id: RuleIdSchema,
235
+ effect: zod.z.enum(["allow", "ask", "deny"]),
236
+ permissions: zod.z.array(zod.z.string()).min(1),
237
+ resources: zod.z.array(zod.z.string()).min(1),
238
+ conditions: zod.z.array(ConditionSchema).optional()
239
+ }).strict();
240
+ var V4PolicySchema = zod.z.object({
241
+ version: zod.z.literal(4),
242
+ // Ordered tiers, in priority order. 1..V4_MAX_TIERS tiers; a tier may be
243
+ // empty (e.g. `[[]]` denies everything). Renamed from v3's `statements`.
244
+ rules: zod.z.array(zod.z.array(V4RuleSchema)).min(1).max(V4_MAX_TIERS)
245
+ }).strict();
230
246
  var PolicySchema = zod.z.union([
231
247
  V1PolicySchema,
232
248
  V2PolicySchema,
233
249
  V3PolicySchema,
250
+ V4PolicySchema,
234
251
  RcRubberduckPolicySchema
235
252
  ]);
236
253
  var CanonicalHashSchema = zod.z.custom(
@@ -265,6 +282,12 @@ var OverridePolicyIdBaseShape = {
265
282
  policyId: PolicyIdSchema,
266
283
  effect: zod.z.literal("allow")
267
284
  };
285
+ var OverrideRuleIdBaseShape = {
286
+ id: zod.z.string().optional(),
287
+ /** Targets a v4 rule by id (r_ prefix, non-null). */
288
+ ruleId: RuleIdSchema,
289
+ effect: zod.z.literal("allow")
290
+ };
268
291
  var OverrideSchema = zod.z.union([
269
292
  // statementHash × match
270
293
  zod.z.object({ ...OverrideHashBaseShape, match: zod.z.literal("any") }).strict(),
@@ -292,6 +315,15 @@ var OverrideSchema = zod.z.union([
292
315
  zod.z.object({
293
316
  ...OverridePolicyIdBaseShape,
294
317
  conditionals: zod.z.array(ConditionSchema)
318
+ }).strict(),
319
+ // ruleId × match
320
+ zod.z.object({ ...OverrideRuleIdBaseShape, match: zod.z.literal("any") }).strict(),
321
+ // ruleId × contextHash
322
+ zod.z.object({ ...OverrideRuleIdBaseShape, contextHash: CanonicalHashSchema }).strict(),
323
+ // ruleId × conditionals
324
+ zod.z.object({
325
+ ...OverrideRuleIdBaseShape,
326
+ conditionals: zod.z.array(ConditionSchema)
295
327
  }).strict()
296
328
  ]);
297
329
  function getOpenApiSchema() {
@@ -306,7 +338,7 @@ function getOpenApiSchema() {
306
338
  effect: {
307
339
  type: "string",
308
340
  enum: [...VALID_EFFECTS].sort(),
309
- description: "Whether to allow, ask, or deny the specified permissions. The 'ask' effect is valid in version 2, version 3, and rc-rubberduck policies."
341
+ description: "Whether to allow, ask, or deny the specified permissions. The 'ask' effect is valid in version 2, version 3, version 4, and rc-rubberduck policies."
310
342
  },
311
343
  permissions: {
312
344
  type: "array",
@@ -385,42 +417,123 @@ function getOpenApiSchema() {
385
417
  }
386
418
  }
387
419
  };
388
- return {
389
- type: "object",
390
- required: ["version", "statements"],
420
+ const ruleItems = {
421
+ ...statementItems,
391
422
  properties: {
392
- version: {
393
- anyOf: [
394
- { type: "integer", enum: [1, 2, 3] },
395
- { type: "string", enum: [RUBBERDUCK_POLICY_VERSION] }
396
- ],
397
- description: "Policy schema version. Stable versions are 1, 2, and 3; supported release candidates are rc-rubberduck."
398
- },
423
+ ...statementItems.properties,
399
424
  id: {
400
425
  type: "string",
401
- description: "Policy id (required for rc-rubberduck policies; not permitted on any other version). Must use the p_ prefix and contain no whitespace."
426
+ description: "Rule identifier. Required for v4 policies; must use the r_ prefix with no whitespace."
402
427
  },
403
- defaultEffect: {
404
- type: "string",
405
- enum: [...DefaultEffectSchema.options],
406
- description: "Effect applied when no statement matches. Required on rc-rubberduck policies (deny equals implicit deny); not permitted on any other version."
428
+ resources: {
429
+ ...statementItems.properties.resources,
430
+ description: "List of resource identifiers the rule applies to."
407
431
  },
408
- statements: {
409
- type: "array",
410
- description: "Policy statements. For v1/v2/rc-rubberduck each entry is a statement. For v3 each entry is a tier (a list of statements); tiers are in priority order and evaluated first-to-last (the first tier with a match decides), so `statements` is a list of lists.",
411
- items: {
412
- anyOf: [
413
- statementItems,
414
- {
415
- type: "array",
416
- description: "A v3 tier: statements evaluated together with deny > ask > allow precedence.",
417
- items: statementItems
418
- }
419
- ]
420
- }
432
+ conditions: {
433
+ ...statementItems.properties.conditions,
434
+ description: "Optional conditions that must be met for the rule to apply."
421
435
  }
422
436
  }
423
437
  };
438
+ const versionDescription = "Policy schema version. Stable versions are 1, 2, 3, and 4; supported release candidates are rc-rubberduck.";
439
+ const flatStatements = {
440
+ type: "array",
441
+ description: "Policy statements; each entry is a statement.",
442
+ items: statementItems
443
+ };
444
+ const statementTiers = {
445
+ type: "array",
446
+ description: "v3 tiers in priority order; each entry is a tier (a list of statements) evaluated first-to-last (the first tier with a match decides), so `statements` is a list of lists.",
447
+ items: {
448
+ type: "array",
449
+ description: "A v3 tier: statements evaluated together with deny > ask > allow precedence.",
450
+ items: statementItems
451
+ }
452
+ };
453
+ const ruleTiers = {
454
+ type: "array",
455
+ description: "v4 rules in priority order; each entry is a tier (a list of rules) evaluated first-to-last (the first tier with a match decides), so `rules` is a list of lists.",
456
+ items: {
457
+ type: "array",
458
+ description: "A v4 tier: rules evaluated together with deny > ask > allow precedence.",
459
+ items: ruleItems
460
+ }
461
+ };
462
+ const idProperty = {
463
+ type: "string",
464
+ description: "Policy id (rc-rubberduck only). Must use the p_ prefix and contain no whitespace."
465
+ };
466
+ const defaultEffectProperty = {
467
+ type: "string",
468
+ enum: [...DefaultEffectSchema.options],
469
+ description: "Effect applied when no statement matches (rc-rubberduck only; deny equals implicit deny)."
470
+ };
471
+ return {
472
+ oneOf: [
473
+ {
474
+ type: "object",
475
+ required: ["version", "statements"],
476
+ properties: {
477
+ version: {
478
+ type: "integer",
479
+ const: 1,
480
+ description: versionDescription
481
+ },
482
+ statements: flatStatements
483
+ }
484
+ },
485
+ {
486
+ type: "object",
487
+ required: ["version", "statements"],
488
+ properties: {
489
+ version: {
490
+ type: "integer",
491
+ const: 2,
492
+ description: versionDescription
493
+ },
494
+ statements: flatStatements
495
+ }
496
+ },
497
+ {
498
+ type: "object",
499
+ required: ["version", "statements"],
500
+ properties: {
501
+ version: {
502
+ type: "integer",
503
+ const: 3,
504
+ description: versionDescription
505
+ },
506
+ statements: statementTiers
507
+ }
508
+ },
509
+ {
510
+ type: "object",
511
+ required: ["version", "rules"],
512
+ properties: {
513
+ version: {
514
+ type: "integer",
515
+ const: 4,
516
+ description: versionDescription
517
+ },
518
+ rules: ruleTiers
519
+ }
520
+ },
521
+ {
522
+ type: "object",
523
+ required: ["version", "id", "defaultEffect", "statements"],
524
+ properties: {
525
+ version: {
526
+ type: "string",
527
+ const: RUBBERDUCK_POLICY_VERSION,
528
+ description: versionDescription
529
+ },
530
+ id: idProperty,
531
+ defaultEffect: defaultEffectProperty,
532
+ statements: flatStatements
533
+ }
534
+ }
535
+ ]
536
+ };
424
537
  }
425
538
 
426
539
  exports.ArrayOperatorSchema = ArrayOperatorSchema;
@@ -436,12 +549,14 @@ exports.PolicyIdSchema = PolicyIdSchema;
436
549
  exports.PolicySchema = PolicySchema;
437
550
  exports.RUBBERDUCK_POLICY_VERSION = RUBBERDUCK_POLICY_VERSION;
438
551
  exports.RangeValueSchema = RangeValueSchema;
552
+ exports.RuleIdSchema = RuleIdSchema;
439
553
  exports.ScalarOperatorSchema = ScalarOperatorSchema;
440
554
  exports.SizeValueSchema = SizeValueSchema;
441
555
  exports.StatementIdSchema = StatementIdSchema;
442
556
  exports.StatementSchema = StatementSchema;
443
557
  exports.UrlOperatorSchema = UrlOperatorSchema;
444
558
  exports.V3_MAX_TIERS = V3_MAX_TIERS;
559
+ exports.V4_MAX_TIERS = V4_MAX_TIERS;
445
560
  exports.VALID_EFFECTS = VALID_EFFECTS;
446
561
  exports.VALID_OPERATOR_LIST = VALID_OPERATOR_LIST;
447
562
  exports.VALID_VERSIONS = VALID_VERSIONS;
package/dist/index.d.cts CHANGED
@@ -3,14 +3,14 @@ import { z } from 'zod';
3
3
  /** Release-candidate policy document version for the current experimental shape. */
4
4
  declare const RUBBERDUCK_POLICY_VERSION: "rc-rubberduck";
5
5
  /** Supported policy document versions. */
6
- declare const VALID_VERSIONS: readonly [1, 2, 3, "rc-rubberduck"];
6
+ declare const VALID_VERSIONS: readonly [1, 2, 3, 4, "rc-rubberduck"];
7
7
  /**
8
8
  * Versions whose statements carry an id (s_/p_) and are therefore targeted by
9
9
  * that id rather than canonical hash. Single source of truth shared with the
10
10
  * engine's usesIds override-targeting predicate: "requires an id" and
11
11
  * "targeted by id" coincide for every supported version.
12
12
  */
13
- declare const ID_BASED_VERSIONS: Set<"rc-rubberduck" | 1 | 2 | 3>;
13
+ declare const ID_BASED_VERSIONS: Set<"rc-rubberduck" | 1 | 2 | 3 | 4>;
14
14
  /** Valid policy effects. */
15
15
  declare const VALID_EFFECTS: readonly ["allow", "ask", "deny"];
16
16
  /** Derived from VALID_EFFECTS — the constant is the single source of truth. */
@@ -359,6 +359,10 @@ type StatementId = z.infer<typeof StatementIdSchema>;
359
359
  declare const PolicyIdSchema: z.ZodString;
360
360
  /** Derived from PolicyIdSchema — the schema is the single source of truth. */
361
361
  type PolicyId = z.infer<typeof PolicyIdSchema>;
362
+ /** Rule identifier for v4 policies. Must match ^r_\S+$ and be ≤512 chars. */
363
+ declare const RuleIdSchema: z.ZodString;
364
+ /** Derived from RuleIdSchema — the schema is the single source of truth. */
365
+ type RuleId = z.infer<typeof RuleIdSchema>;
362
366
  /** Effect applied when no statement matches (rc-rubberduck, required). deny == implicit deny. */
363
367
  declare const DefaultEffectSchema: z.ZodEnum<{
364
368
  deny: "deny";
@@ -368,6 +372,8 @@ declare const DefaultEffectSchema: z.ZodEnum<{
368
372
  type DefaultEffect = z.infer<typeof DefaultEffectSchema>;
369
373
  /** Upper bound on v3 statement tiers. Raising this later is backwards-compatible. */
370
374
  declare const V3_MAX_TIERS = 3;
375
+ /** Upper bound on v4 rule tiers. Separate from V3_MAX_TIERS so the two versions can diverge. */
376
+ declare const V4_MAX_TIERS = 3;
371
377
  declare const PolicySchema: z.ZodUnion<readonly [z.ZodObject<{
372
378
  version: z.ZodLiteral<1>;
373
379
  statements: z.ZodArray<z.ZodObject<{
@@ -790,6 +796,147 @@ declare const PolicySchema: z.ZodUnion<readonly [z.ZodObject<{
790
796
  }, z.core.$strict>]>>;
791
797
  }, z.core.$strict>]>>>;
792
798
  }, z.core.$strict>>>;
799
+ }, z.core.$strict>, z.ZodObject<{
800
+ version: z.ZodLiteral<4>;
801
+ rules: z.ZodArray<z.ZodArray<z.ZodObject<{
802
+ id: z.ZodString;
803
+ effect: z.ZodEnum<{
804
+ allow: "allow";
805
+ deny: "deny";
806
+ ask: "ask";
807
+ }>;
808
+ permissions: z.ZodArray<z.ZodString>;
809
+ resources: z.ZodArray<z.ZodString>;
810
+ conditions: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
811
+ negate: z.ZodOptional<z.ZodBoolean>;
812
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
813
+ operator: z.ZodLiteral<"equals">;
814
+ value: z.ZodJSONSchema;
815
+ }, z.core.$strict>, z.ZodObject<{
816
+ negate: z.ZodOptional<z.ZodBoolean>;
817
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
818
+ operator: z.ZodLiteral<"in">;
819
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
820
+ }, z.core.$strict>, z.ZodObject<{
821
+ negate: z.ZodOptional<z.ZodBoolean>;
822
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
823
+ operator: z.ZodLiteral<"matches">;
824
+ value: z.ZodString;
825
+ }, z.core.$strict>, z.ZodObject<{
826
+ negate: z.ZodOptional<z.ZodBoolean>;
827
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
828
+ operator: z.ZodLiteral<"matches_host">;
829
+ value: z.ZodString;
830
+ }, z.core.$strict>, z.ZodObject<{
831
+ negate: z.ZodOptional<z.ZodBoolean>;
832
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
833
+ operator: z.ZodLiteral<"matches_path">;
834
+ value: z.ZodString;
835
+ }, z.core.$strict>, z.ZodObject<{
836
+ negate: z.ZodOptional<z.ZodBoolean>;
837
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
838
+ operator: z.ZodLiteral<"range">;
839
+ value: z.ZodObject<{
840
+ min: z.ZodOptional<z.ZodNumber>;
841
+ max: z.ZodOptional<z.ZodNumber>;
842
+ }, z.core.$strict>;
843
+ }, z.core.$strict>, z.ZodObject<{
844
+ negate: z.ZodOptional<z.ZodBoolean>;
845
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
846
+ operator: z.ZodLiteral<"matches_url">;
847
+ value: z.ZodString;
848
+ }, z.core.$strict>, z.ZodObject<{
849
+ negate: z.ZodOptional<z.ZodBoolean>;
850
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
851
+ operator: z.ZodLiteral<"values_in">;
852
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
853
+ }, z.core.$strict>, z.ZodObject<{
854
+ negate: z.ZodOptional<z.ZodBoolean>;
855
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
856
+ operator: z.ZodLiteral<"exists">;
857
+ }, z.core.$strict>, z.ZodObject<{
858
+ negate: z.ZodOptional<z.ZodBoolean>;
859
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
860
+ operator: z.ZodLiteral<"keys_in">;
861
+ value: z.ZodArray<z.ZodString>;
862
+ }, z.core.$strict>, z.ZodObject<{
863
+ negate: z.ZodOptional<z.ZodBoolean>;
864
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
865
+ operator: z.ZodLiteral<"size">;
866
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
867
+ min: z.ZodOptional<z.ZodNumber>;
868
+ max: z.ZodOptional<z.ZodNumber>;
869
+ }, z.core.$strict>]>;
870
+ }, z.core.$strict>, z.ZodObject<{
871
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
872
+ operator: z.ZodEnum<{
873
+ some: "some";
874
+ all: "all";
875
+ none: "none";
876
+ }>;
877
+ where: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
878
+ negate: z.ZodOptional<z.ZodBoolean>;
879
+ operator: z.ZodLiteral<"equals">;
880
+ value: z.ZodJSONSchema;
881
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
882
+ }, z.core.$strict>, z.ZodObject<{
883
+ negate: z.ZodOptional<z.ZodBoolean>;
884
+ operator: z.ZodLiteral<"in">;
885
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
886
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
887
+ }, z.core.$strict>, z.ZodObject<{
888
+ negate: z.ZodOptional<z.ZodBoolean>;
889
+ operator: z.ZodLiteral<"matches">;
890
+ value: z.ZodString;
891
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
892
+ }, z.core.$strict>, z.ZodObject<{
893
+ negate: z.ZodOptional<z.ZodBoolean>;
894
+ operator: z.ZodLiteral<"matches_host">;
895
+ value: z.ZodString;
896
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
897
+ }, z.core.$strict>, z.ZodObject<{
898
+ negate: z.ZodOptional<z.ZodBoolean>;
899
+ operator: z.ZodLiteral<"matches_path">;
900
+ value: z.ZodString;
901
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
902
+ }, z.core.$strict>, z.ZodObject<{
903
+ negate: z.ZodOptional<z.ZodBoolean>;
904
+ operator: z.ZodLiteral<"range">;
905
+ value: z.ZodObject<{
906
+ min: z.ZodOptional<z.ZodNumber>;
907
+ max: z.ZodOptional<z.ZodNumber>;
908
+ }, z.core.$strict>;
909
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
910
+ }, z.core.$strict>, z.ZodObject<{
911
+ negate: z.ZodOptional<z.ZodBoolean>;
912
+ operator: z.ZodLiteral<"matches_url">;
913
+ value: z.ZodString;
914
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
915
+ }, z.core.$strict>, z.ZodObject<{
916
+ negate: z.ZodOptional<z.ZodBoolean>;
917
+ operator: z.ZodLiteral<"values_in">;
918
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
919
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
920
+ }, z.core.$strict>, z.ZodObject<{
921
+ negate: z.ZodOptional<z.ZodBoolean>;
922
+ operator: z.ZodLiteral<"exists">;
923
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
924
+ }, z.core.$strict>, z.ZodObject<{
925
+ negate: z.ZodOptional<z.ZodBoolean>;
926
+ operator: z.ZodLiteral<"keys_in">;
927
+ value: z.ZodArray<z.ZodString>;
928
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
929
+ }, z.core.$strict>, z.ZodObject<{
930
+ negate: z.ZodOptional<z.ZodBoolean>;
931
+ operator: z.ZodLiteral<"size">;
932
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
933
+ min: z.ZodOptional<z.ZodNumber>;
934
+ max: z.ZodOptional<z.ZodNumber>;
935
+ }, z.core.$strict>]>;
936
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
937
+ }, z.core.$strict>]>>;
938
+ }, z.core.$strict>]>>>;
939
+ }, z.core.$strict>>>;
793
940
  }, z.core.$strict>, z.ZodObject<{
794
941
  version: z.ZodLiteral<"rc-rubberduck">;
795
942
  id: z.ZodString;
@@ -943,15 +1090,17 @@ type Policy = z.infer<typeof PolicySchema>;
943
1090
  declare const CanonicalHashSchema: z.ZodCustom<CanonicalHash, CanonicalHash>;
944
1091
  /**
945
1092
  * Runtime schema for the Override type. Each variant has exactly one target
946
- * field (statementHash for v1/v2, statementId or policyId for rc-rubberduck) and exactly
1093
+ * field (statementHash for v1/v2, statementId for v3/rc-rubberduck, policyId for
1094
+ * rc-rubberduck, ruleId for v4) and exactly
947
1095
  * one binding mode (match, contextHash, or conditionals). `.strict()` keeps
948
1096
  * the variants mutually exclusive (an object carrying fields from two variants
949
1097
  * fails all branches).
950
1098
  *
951
1099
  * Targets are all non-null:
952
1100
  * - statementHash: targets a v1/v2 statement by canonical hash
953
- * - statementId: targets an rc-rubberduck statement by id
1101
+ * - statementId: targets a v3/rc-rubberduck statement by id
954
1102
  * - policyId: targets that policy's default effect (rc-rubberduck only)
1103
+ * - ruleId: targets a v4 rule by id
955
1104
  *
956
1105
  * Empty conditionals is intentional (the "blank check"): the override applies
957
1106
  * whenever the targeted ask statement fires, with no extra conditions. Do not
@@ -1386,6 +1535,149 @@ declare const OverrideSchema: z.ZodUnion<readonly [z.ZodObject<{
1386
1535
  id: z.ZodOptional<z.ZodString>;
1387
1536
  policyId: z.ZodString;
1388
1537
  effect: z.ZodLiteral<"allow">;
1538
+ }, z.core.$strict>, z.ZodObject<{
1539
+ match: z.ZodLiteral<"any">;
1540
+ id: z.ZodOptional<z.ZodString>;
1541
+ ruleId: z.ZodString;
1542
+ effect: z.ZodLiteral<"allow">;
1543
+ }, z.core.$strict>, z.ZodObject<{
1544
+ contextHash: z.ZodCustom<CanonicalHash, CanonicalHash>;
1545
+ id: z.ZodOptional<z.ZodString>;
1546
+ ruleId: z.ZodString;
1547
+ effect: z.ZodLiteral<"allow">;
1548
+ }, z.core.$strict>, z.ZodObject<{
1549
+ conditionals: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
1550
+ negate: z.ZodOptional<z.ZodBoolean>;
1551
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1552
+ operator: z.ZodLiteral<"equals">;
1553
+ value: z.ZodJSONSchema;
1554
+ }, z.core.$strict>, z.ZodObject<{
1555
+ negate: z.ZodOptional<z.ZodBoolean>;
1556
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1557
+ operator: z.ZodLiteral<"in">;
1558
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1559
+ }, z.core.$strict>, z.ZodObject<{
1560
+ negate: z.ZodOptional<z.ZodBoolean>;
1561
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1562
+ operator: z.ZodLiteral<"matches">;
1563
+ value: z.ZodString;
1564
+ }, z.core.$strict>, z.ZodObject<{
1565
+ negate: z.ZodOptional<z.ZodBoolean>;
1566
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1567
+ operator: z.ZodLiteral<"matches_host">;
1568
+ value: z.ZodString;
1569
+ }, z.core.$strict>, z.ZodObject<{
1570
+ negate: z.ZodOptional<z.ZodBoolean>;
1571
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1572
+ operator: z.ZodLiteral<"matches_path">;
1573
+ value: z.ZodString;
1574
+ }, z.core.$strict>, z.ZodObject<{
1575
+ negate: z.ZodOptional<z.ZodBoolean>;
1576
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1577
+ operator: z.ZodLiteral<"range">;
1578
+ value: z.ZodObject<{
1579
+ min: z.ZodOptional<z.ZodNumber>;
1580
+ max: z.ZodOptional<z.ZodNumber>;
1581
+ }, z.core.$strict>;
1582
+ }, z.core.$strict>, z.ZodObject<{
1583
+ negate: z.ZodOptional<z.ZodBoolean>;
1584
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1585
+ operator: z.ZodLiteral<"matches_url">;
1586
+ value: z.ZodString;
1587
+ }, z.core.$strict>, z.ZodObject<{
1588
+ negate: z.ZodOptional<z.ZodBoolean>;
1589
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1590
+ operator: z.ZodLiteral<"values_in">;
1591
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1592
+ }, z.core.$strict>, z.ZodObject<{
1593
+ negate: z.ZodOptional<z.ZodBoolean>;
1594
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1595
+ operator: z.ZodLiteral<"exists">;
1596
+ }, z.core.$strict>, z.ZodObject<{
1597
+ negate: z.ZodOptional<z.ZodBoolean>;
1598
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1599
+ operator: z.ZodLiteral<"keys_in">;
1600
+ value: z.ZodArray<z.ZodString>;
1601
+ }, z.core.$strict>, z.ZodObject<{
1602
+ negate: z.ZodOptional<z.ZodBoolean>;
1603
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1604
+ operator: z.ZodLiteral<"size">;
1605
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
1606
+ min: z.ZodOptional<z.ZodNumber>;
1607
+ max: z.ZodOptional<z.ZodNumber>;
1608
+ }, z.core.$strict>]>;
1609
+ }, z.core.$strict>, z.ZodObject<{
1610
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1611
+ operator: z.ZodEnum<{
1612
+ some: "some";
1613
+ all: "all";
1614
+ none: "none";
1615
+ }>;
1616
+ where: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
1617
+ negate: z.ZodOptional<z.ZodBoolean>;
1618
+ operator: z.ZodLiteral<"equals">;
1619
+ value: z.ZodJSONSchema;
1620
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1621
+ }, z.core.$strict>, z.ZodObject<{
1622
+ negate: z.ZodOptional<z.ZodBoolean>;
1623
+ operator: z.ZodLiteral<"in">;
1624
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1625
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1626
+ }, z.core.$strict>, z.ZodObject<{
1627
+ negate: z.ZodOptional<z.ZodBoolean>;
1628
+ operator: z.ZodLiteral<"matches">;
1629
+ value: z.ZodString;
1630
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1631
+ }, z.core.$strict>, z.ZodObject<{
1632
+ negate: z.ZodOptional<z.ZodBoolean>;
1633
+ operator: z.ZodLiteral<"matches_host">;
1634
+ value: z.ZodString;
1635
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1636
+ }, z.core.$strict>, z.ZodObject<{
1637
+ negate: z.ZodOptional<z.ZodBoolean>;
1638
+ operator: z.ZodLiteral<"matches_path">;
1639
+ value: z.ZodString;
1640
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1641
+ }, z.core.$strict>, z.ZodObject<{
1642
+ negate: z.ZodOptional<z.ZodBoolean>;
1643
+ operator: z.ZodLiteral<"range">;
1644
+ value: z.ZodObject<{
1645
+ min: z.ZodOptional<z.ZodNumber>;
1646
+ max: z.ZodOptional<z.ZodNumber>;
1647
+ }, z.core.$strict>;
1648
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1649
+ }, z.core.$strict>, z.ZodObject<{
1650
+ negate: z.ZodOptional<z.ZodBoolean>;
1651
+ operator: z.ZodLiteral<"matches_url">;
1652
+ value: z.ZodString;
1653
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1654
+ }, z.core.$strict>, z.ZodObject<{
1655
+ negate: z.ZodOptional<z.ZodBoolean>;
1656
+ operator: z.ZodLiteral<"values_in">;
1657
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1658
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1659
+ }, z.core.$strict>, z.ZodObject<{
1660
+ negate: z.ZodOptional<z.ZodBoolean>;
1661
+ operator: z.ZodLiteral<"exists">;
1662
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1663
+ }, z.core.$strict>, z.ZodObject<{
1664
+ negate: z.ZodOptional<z.ZodBoolean>;
1665
+ operator: z.ZodLiteral<"keys_in">;
1666
+ value: z.ZodArray<z.ZodString>;
1667
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1668
+ }, z.core.$strict>, z.ZodObject<{
1669
+ negate: z.ZodOptional<z.ZodBoolean>;
1670
+ operator: z.ZodLiteral<"size">;
1671
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
1672
+ min: z.ZodOptional<z.ZodNumber>;
1673
+ max: z.ZodOptional<z.ZodNumber>;
1674
+ }, z.core.$strict>]>;
1675
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1676
+ }, z.core.$strict>]>>;
1677
+ }, z.core.$strict>]>>;
1678
+ id: z.ZodOptional<z.ZodString>;
1679
+ ruleId: z.ZodString;
1680
+ effect: z.ZodLiteral<"allow">;
1389
1681
  }, z.core.$strict>]>;
1390
1682
  /** Derived from OverrideSchema — the schema is the single source of truth. */
1391
1683
  type Override = z.infer<typeof OverrideSchema>;
@@ -1432,6 +1724,18 @@ interface StatementTrace {
1432
1724
  passed: boolean;
1433
1725
  overrides?: OverrideTrace[];
1434
1726
  }
1727
+ /**
1728
+ * v4 rule trace. Identical to StatementTrace but the matched object is keyed
1729
+ * `rule` instead of `statement`, matching the v4 `statements`->`rules` rename.
1730
+ */
1731
+ interface RuleTrace {
1732
+ rule: Statement;
1733
+ permissionMatched: boolean;
1734
+ resourceMatched: boolean;
1735
+ conditionResults?: ConditionTrace[];
1736
+ passed: boolean;
1737
+ overrides?: OverrideTrace[];
1738
+ }
1435
1739
  interface EvaluatePolicyOptions {
1436
1740
  policy: Policy;
1437
1741
  request: PolicyRequest;
@@ -1447,6 +1751,19 @@ interface PolicyResult {
1447
1751
  /** Trace mode only. Present when no statement matched and an ask default was consulted (never attached for deny defaults). */
1448
1752
  default?: DefaultTrace;
1449
1753
  }
1754
+ /**
1755
+ * Result of evaluating a v4 policy. Version-gated shape: the trace array is
1756
+ * keyed `rules` (of RuleTrace) instead of `statements`, matching the v4
1757
+ * `statements`->`rules` rename. Only ever returned for a v4 policy; all other
1758
+ * versions return {@link PolicyResult}. v4 has no ask default (no
1759
+ * defaultEffect), so there is no `default` field.
1760
+ */
1761
+ interface PolicyResultV4 {
1762
+ effect: Effect;
1763
+ explicit: boolean;
1764
+ reason: PolicyReason;
1765
+ rules: RuleTrace[];
1766
+ }
1450
1767
  interface ParsedUrl {
1451
1768
  scheme: string;
1452
1769
  host: string;
@@ -1476,10 +1793,11 @@ interface OpenApiSchema {
1476
1793
  properties?: Record<string, OpenApiSchema>;
1477
1794
  items?: OpenApiSchema;
1478
1795
  enum?: (string | number)[];
1796
+ const?: string | number;
1479
1797
  description?: string;
1480
1798
  minItems?: number;
1481
1799
  oneOf?: OpenApiSchema[];
1482
1800
  anyOf?: OpenApiSchema[];
1483
1801
  }
1484
1802
 
1485
- export { type ArrayOperator, ArrayOperatorSchema, type CanonicalHash, CanonicalHashSchema, type Condition, ConditionSchema, type ConditionTrace, type ConditionValue, ConditionValueSchema, type DefaultEffect, DefaultEffectSchema, type DefaultTrace, type Effect, type EvaluatePolicyOptions, ID_BASED_VERSIONS, type JsonValue, type OpenApiSchema, type Operator, OperatorSchema, type OtherOperator, OtherOperatorSchema, type Override, OverrideSchema, type OverrideTrace, type ParsedUrl, type Policy, type PolicyActionRequest, type PolicyHttpRequest, type PolicyId, PolicyIdSchema, type PolicyReason, type PolicyRequest, type PolicyResult, PolicySchema, RUBBERDUCK_POLICY_VERSION, type RangeValue, RangeValueSchema, type ScalarOperator, ScalarOperatorSchema, type SizeValue, SizeValueSchema, type Statement, type StatementId, StatementIdSchema, StatementSchema, type StatementTrace, type UrlOperator, UrlOperatorSchema, V3_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
1803
+ export { type ArrayOperator, ArrayOperatorSchema, type CanonicalHash, CanonicalHashSchema, type Condition, ConditionSchema, type ConditionTrace, type ConditionValue, ConditionValueSchema, type DefaultEffect, DefaultEffectSchema, type DefaultTrace, type Effect, type EvaluatePolicyOptions, ID_BASED_VERSIONS, type JsonValue, type OpenApiSchema, type Operator, OperatorSchema, type OtherOperator, OtherOperatorSchema, type Override, OverrideSchema, type OverrideTrace, type ParsedUrl, type Policy, type PolicyActionRequest, type PolicyHttpRequest, type PolicyId, PolicyIdSchema, type PolicyReason, type PolicyRequest, type PolicyResult, type PolicyResultV4, PolicySchema, RUBBERDUCK_POLICY_VERSION, type RangeValue, RangeValueSchema, type RuleId, RuleIdSchema, type RuleTrace, type ScalarOperator, ScalarOperatorSchema, type SizeValue, SizeValueSchema, type Statement, type StatementId, StatementIdSchema, StatementSchema, type StatementTrace, type UrlOperator, UrlOperatorSchema, V3_MAX_TIERS, V4_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
package/dist/index.d.ts CHANGED
@@ -3,14 +3,14 @@ import { z } from 'zod';
3
3
  /** Release-candidate policy document version for the current experimental shape. */
4
4
  declare const RUBBERDUCK_POLICY_VERSION: "rc-rubberduck";
5
5
  /** Supported policy document versions. */
6
- declare const VALID_VERSIONS: readonly [1, 2, 3, "rc-rubberduck"];
6
+ declare const VALID_VERSIONS: readonly [1, 2, 3, 4, "rc-rubberduck"];
7
7
  /**
8
8
  * Versions whose statements carry an id (s_/p_) and are therefore targeted by
9
9
  * that id rather than canonical hash. Single source of truth shared with the
10
10
  * engine's usesIds override-targeting predicate: "requires an id" and
11
11
  * "targeted by id" coincide for every supported version.
12
12
  */
13
- declare const ID_BASED_VERSIONS: Set<"rc-rubberduck" | 1 | 2 | 3>;
13
+ declare const ID_BASED_VERSIONS: Set<"rc-rubberduck" | 1 | 2 | 3 | 4>;
14
14
  /** Valid policy effects. */
15
15
  declare const VALID_EFFECTS: readonly ["allow", "ask", "deny"];
16
16
  /** Derived from VALID_EFFECTS — the constant is the single source of truth. */
@@ -359,6 +359,10 @@ type StatementId = z.infer<typeof StatementIdSchema>;
359
359
  declare const PolicyIdSchema: z.ZodString;
360
360
  /** Derived from PolicyIdSchema — the schema is the single source of truth. */
361
361
  type PolicyId = z.infer<typeof PolicyIdSchema>;
362
+ /** Rule identifier for v4 policies. Must match ^r_\S+$ and be ≤512 chars. */
363
+ declare const RuleIdSchema: z.ZodString;
364
+ /** Derived from RuleIdSchema — the schema is the single source of truth. */
365
+ type RuleId = z.infer<typeof RuleIdSchema>;
362
366
  /** Effect applied when no statement matches (rc-rubberduck, required). deny == implicit deny. */
363
367
  declare const DefaultEffectSchema: z.ZodEnum<{
364
368
  deny: "deny";
@@ -368,6 +372,8 @@ declare const DefaultEffectSchema: z.ZodEnum<{
368
372
  type DefaultEffect = z.infer<typeof DefaultEffectSchema>;
369
373
  /** Upper bound on v3 statement tiers. Raising this later is backwards-compatible. */
370
374
  declare const V3_MAX_TIERS = 3;
375
+ /** Upper bound on v4 rule tiers. Separate from V3_MAX_TIERS so the two versions can diverge. */
376
+ declare const V4_MAX_TIERS = 3;
371
377
  declare const PolicySchema: z.ZodUnion<readonly [z.ZodObject<{
372
378
  version: z.ZodLiteral<1>;
373
379
  statements: z.ZodArray<z.ZodObject<{
@@ -790,6 +796,147 @@ declare const PolicySchema: z.ZodUnion<readonly [z.ZodObject<{
790
796
  }, z.core.$strict>]>>;
791
797
  }, z.core.$strict>]>>>;
792
798
  }, z.core.$strict>>>;
799
+ }, z.core.$strict>, z.ZodObject<{
800
+ version: z.ZodLiteral<4>;
801
+ rules: z.ZodArray<z.ZodArray<z.ZodObject<{
802
+ id: z.ZodString;
803
+ effect: z.ZodEnum<{
804
+ allow: "allow";
805
+ deny: "deny";
806
+ ask: "ask";
807
+ }>;
808
+ permissions: z.ZodArray<z.ZodString>;
809
+ resources: z.ZodArray<z.ZodString>;
810
+ conditions: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
811
+ negate: z.ZodOptional<z.ZodBoolean>;
812
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
813
+ operator: z.ZodLiteral<"equals">;
814
+ value: z.ZodJSONSchema;
815
+ }, z.core.$strict>, z.ZodObject<{
816
+ negate: z.ZodOptional<z.ZodBoolean>;
817
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
818
+ operator: z.ZodLiteral<"in">;
819
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
820
+ }, z.core.$strict>, z.ZodObject<{
821
+ negate: z.ZodOptional<z.ZodBoolean>;
822
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
823
+ operator: z.ZodLiteral<"matches">;
824
+ value: z.ZodString;
825
+ }, z.core.$strict>, z.ZodObject<{
826
+ negate: z.ZodOptional<z.ZodBoolean>;
827
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
828
+ operator: z.ZodLiteral<"matches_host">;
829
+ value: z.ZodString;
830
+ }, z.core.$strict>, z.ZodObject<{
831
+ negate: z.ZodOptional<z.ZodBoolean>;
832
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
833
+ operator: z.ZodLiteral<"matches_path">;
834
+ value: z.ZodString;
835
+ }, z.core.$strict>, z.ZodObject<{
836
+ negate: z.ZodOptional<z.ZodBoolean>;
837
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
838
+ operator: z.ZodLiteral<"range">;
839
+ value: z.ZodObject<{
840
+ min: z.ZodOptional<z.ZodNumber>;
841
+ max: z.ZodOptional<z.ZodNumber>;
842
+ }, z.core.$strict>;
843
+ }, z.core.$strict>, z.ZodObject<{
844
+ negate: z.ZodOptional<z.ZodBoolean>;
845
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
846
+ operator: z.ZodLiteral<"matches_url">;
847
+ value: z.ZodString;
848
+ }, z.core.$strict>, z.ZodObject<{
849
+ negate: z.ZodOptional<z.ZodBoolean>;
850
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
851
+ operator: z.ZodLiteral<"values_in">;
852
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
853
+ }, z.core.$strict>, z.ZodObject<{
854
+ negate: z.ZodOptional<z.ZodBoolean>;
855
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
856
+ operator: z.ZodLiteral<"exists">;
857
+ }, z.core.$strict>, z.ZodObject<{
858
+ negate: z.ZodOptional<z.ZodBoolean>;
859
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
860
+ operator: z.ZodLiteral<"keys_in">;
861
+ value: z.ZodArray<z.ZodString>;
862
+ }, z.core.$strict>, z.ZodObject<{
863
+ negate: z.ZodOptional<z.ZodBoolean>;
864
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
865
+ operator: z.ZodLiteral<"size">;
866
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
867
+ min: z.ZodOptional<z.ZodNumber>;
868
+ max: z.ZodOptional<z.ZodNumber>;
869
+ }, z.core.$strict>]>;
870
+ }, z.core.$strict>, z.ZodObject<{
871
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
872
+ operator: z.ZodEnum<{
873
+ some: "some";
874
+ all: "all";
875
+ none: "none";
876
+ }>;
877
+ where: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
878
+ negate: z.ZodOptional<z.ZodBoolean>;
879
+ operator: z.ZodLiteral<"equals">;
880
+ value: z.ZodJSONSchema;
881
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
882
+ }, z.core.$strict>, z.ZodObject<{
883
+ negate: z.ZodOptional<z.ZodBoolean>;
884
+ operator: z.ZodLiteral<"in">;
885
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
886
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
887
+ }, z.core.$strict>, z.ZodObject<{
888
+ negate: z.ZodOptional<z.ZodBoolean>;
889
+ operator: z.ZodLiteral<"matches">;
890
+ value: z.ZodString;
891
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
892
+ }, z.core.$strict>, z.ZodObject<{
893
+ negate: z.ZodOptional<z.ZodBoolean>;
894
+ operator: z.ZodLiteral<"matches_host">;
895
+ value: z.ZodString;
896
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
897
+ }, z.core.$strict>, z.ZodObject<{
898
+ negate: z.ZodOptional<z.ZodBoolean>;
899
+ operator: z.ZodLiteral<"matches_path">;
900
+ value: z.ZodString;
901
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
902
+ }, z.core.$strict>, z.ZodObject<{
903
+ negate: z.ZodOptional<z.ZodBoolean>;
904
+ operator: z.ZodLiteral<"range">;
905
+ value: z.ZodObject<{
906
+ min: z.ZodOptional<z.ZodNumber>;
907
+ max: z.ZodOptional<z.ZodNumber>;
908
+ }, z.core.$strict>;
909
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
910
+ }, z.core.$strict>, z.ZodObject<{
911
+ negate: z.ZodOptional<z.ZodBoolean>;
912
+ operator: z.ZodLiteral<"matches_url">;
913
+ value: z.ZodString;
914
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
915
+ }, z.core.$strict>, z.ZodObject<{
916
+ negate: z.ZodOptional<z.ZodBoolean>;
917
+ operator: z.ZodLiteral<"values_in">;
918
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
919
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
920
+ }, z.core.$strict>, z.ZodObject<{
921
+ negate: z.ZodOptional<z.ZodBoolean>;
922
+ operator: z.ZodLiteral<"exists">;
923
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
924
+ }, z.core.$strict>, z.ZodObject<{
925
+ negate: z.ZodOptional<z.ZodBoolean>;
926
+ operator: z.ZodLiteral<"keys_in">;
927
+ value: z.ZodArray<z.ZodString>;
928
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
929
+ }, z.core.$strict>, z.ZodObject<{
930
+ negate: z.ZodOptional<z.ZodBoolean>;
931
+ operator: z.ZodLiteral<"size">;
932
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
933
+ min: z.ZodOptional<z.ZodNumber>;
934
+ max: z.ZodOptional<z.ZodNumber>;
935
+ }, z.core.$strict>]>;
936
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
937
+ }, z.core.$strict>]>>;
938
+ }, z.core.$strict>]>>>;
939
+ }, z.core.$strict>>>;
793
940
  }, z.core.$strict>, z.ZodObject<{
794
941
  version: z.ZodLiteral<"rc-rubberduck">;
795
942
  id: z.ZodString;
@@ -943,15 +1090,17 @@ type Policy = z.infer<typeof PolicySchema>;
943
1090
  declare const CanonicalHashSchema: z.ZodCustom<CanonicalHash, CanonicalHash>;
944
1091
  /**
945
1092
  * Runtime schema for the Override type. Each variant has exactly one target
946
- * field (statementHash for v1/v2, statementId or policyId for rc-rubberduck) and exactly
1093
+ * field (statementHash for v1/v2, statementId for v3/rc-rubberduck, policyId for
1094
+ * rc-rubberduck, ruleId for v4) and exactly
947
1095
  * one binding mode (match, contextHash, or conditionals). `.strict()` keeps
948
1096
  * the variants mutually exclusive (an object carrying fields from two variants
949
1097
  * fails all branches).
950
1098
  *
951
1099
  * Targets are all non-null:
952
1100
  * - statementHash: targets a v1/v2 statement by canonical hash
953
- * - statementId: targets an rc-rubberduck statement by id
1101
+ * - statementId: targets a v3/rc-rubberduck statement by id
954
1102
  * - policyId: targets that policy's default effect (rc-rubberduck only)
1103
+ * - ruleId: targets a v4 rule by id
955
1104
  *
956
1105
  * Empty conditionals is intentional (the "blank check"): the override applies
957
1106
  * whenever the targeted ask statement fires, with no extra conditions. Do not
@@ -1386,6 +1535,149 @@ declare const OverrideSchema: z.ZodUnion<readonly [z.ZodObject<{
1386
1535
  id: z.ZodOptional<z.ZodString>;
1387
1536
  policyId: z.ZodString;
1388
1537
  effect: z.ZodLiteral<"allow">;
1538
+ }, z.core.$strict>, z.ZodObject<{
1539
+ match: z.ZodLiteral<"any">;
1540
+ id: z.ZodOptional<z.ZodString>;
1541
+ ruleId: z.ZodString;
1542
+ effect: z.ZodLiteral<"allow">;
1543
+ }, z.core.$strict>, z.ZodObject<{
1544
+ contextHash: z.ZodCustom<CanonicalHash, CanonicalHash>;
1545
+ id: z.ZodOptional<z.ZodString>;
1546
+ ruleId: z.ZodString;
1547
+ effect: z.ZodLiteral<"allow">;
1548
+ }, z.core.$strict>, z.ZodObject<{
1549
+ conditionals: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
1550
+ negate: z.ZodOptional<z.ZodBoolean>;
1551
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1552
+ operator: z.ZodLiteral<"equals">;
1553
+ value: z.ZodJSONSchema;
1554
+ }, z.core.$strict>, z.ZodObject<{
1555
+ negate: z.ZodOptional<z.ZodBoolean>;
1556
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1557
+ operator: z.ZodLiteral<"in">;
1558
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1559
+ }, z.core.$strict>, z.ZodObject<{
1560
+ negate: z.ZodOptional<z.ZodBoolean>;
1561
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1562
+ operator: z.ZodLiteral<"matches">;
1563
+ value: z.ZodString;
1564
+ }, z.core.$strict>, z.ZodObject<{
1565
+ negate: z.ZodOptional<z.ZodBoolean>;
1566
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1567
+ operator: z.ZodLiteral<"matches_host">;
1568
+ value: z.ZodString;
1569
+ }, z.core.$strict>, z.ZodObject<{
1570
+ negate: z.ZodOptional<z.ZodBoolean>;
1571
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1572
+ operator: z.ZodLiteral<"matches_path">;
1573
+ value: z.ZodString;
1574
+ }, z.core.$strict>, z.ZodObject<{
1575
+ negate: z.ZodOptional<z.ZodBoolean>;
1576
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1577
+ operator: z.ZodLiteral<"range">;
1578
+ value: z.ZodObject<{
1579
+ min: z.ZodOptional<z.ZodNumber>;
1580
+ max: z.ZodOptional<z.ZodNumber>;
1581
+ }, z.core.$strict>;
1582
+ }, z.core.$strict>, z.ZodObject<{
1583
+ negate: z.ZodOptional<z.ZodBoolean>;
1584
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1585
+ operator: z.ZodLiteral<"matches_url">;
1586
+ value: z.ZodString;
1587
+ }, z.core.$strict>, z.ZodObject<{
1588
+ negate: z.ZodOptional<z.ZodBoolean>;
1589
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1590
+ operator: z.ZodLiteral<"values_in">;
1591
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1592
+ }, z.core.$strict>, z.ZodObject<{
1593
+ negate: z.ZodOptional<z.ZodBoolean>;
1594
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1595
+ operator: z.ZodLiteral<"exists">;
1596
+ }, z.core.$strict>, z.ZodObject<{
1597
+ negate: z.ZodOptional<z.ZodBoolean>;
1598
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1599
+ operator: z.ZodLiteral<"keys_in">;
1600
+ value: z.ZodArray<z.ZodString>;
1601
+ }, z.core.$strict>, z.ZodObject<{
1602
+ negate: z.ZodOptional<z.ZodBoolean>;
1603
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1604
+ operator: z.ZodLiteral<"size">;
1605
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
1606
+ min: z.ZodOptional<z.ZodNumber>;
1607
+ max: z.ZodOptional<z.ZodNumber>;
1608
+ }, z.core.$strict>]>;
1609
+ }, z.core.$strict>, z.ZodObject<{
1610
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1611
+ operator: z.ZodEnum<{
1612
+ some: "some";
1613
+ all: "all";
1614
+ none: "none";
1615
+ }>;
1616
+ where: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
1617
+ negate: z.ZodOptional<z.ZodBoolean>;
1618
+ operator: z.ZodLiteral<"equals">;
1619
+ value: z.ZodJSONSchema;
1620
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1621
+ }, z.core.$strict>, z.ZodObject<{
1622
+ negate: z.ZodOptional<z.ZodBoolean>;
1623
+ operator: z.ZodLiteral<"in">;
1624
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1625
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1626
+ }, z.core.$strict>, z.ZodObject<{
1627
+ negate: z.ZodOptional<z.ZodBoolean>;
1628
+ operator: z.ZodLiteral<"matches">;
1629
+ value: z.ZodString;
1630
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1631
+ }, z.core.$strict>, z.ZodObject<{
1632
+ negate: z.ZodOptional<z.ZodBoolean>;
1633
+ operator: z.ZodLiteral<"matches_host">;
1634
+ value: z.ZodString;
1635
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1636
+ }, z.core.$strict>, z.ZodObject<{
1637
+ negate: z.ZodOptional<z.ZodBoolean>;
1638
+ operator: z.ZodLiteral<"matches_path">;
1639
+ value: z.ZodString;
1640
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1641
+ }, z.core.$strict>, z.ZodObject<{
1642
+ negate: z.ZodOptional<z.ZodBoolean>;
1643
+ operator: z.ZodLiteral<"range">;
1644
+ value: z.ZodObject<{
1645
+ min: z.ZodOptional<z.ZodNumber>;
1646
+ max: z.ZodOptional<z.ZodNumber>;
1647
+ }, z.core.$strict>;
1648
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1649
+ }, z.core.$strict>, z.ZodObject<{
1650
+ negate: z.ZodOptional<z.ZodBoolean>;
1651
+ operator: z.ZodLiteral<"matches_url">;
1652
+ value: z.ZodString;
1653
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1654
+ }, z.core.$strict>, z.ZodObject<{
1655
+ negate: z.ZodOptional<z.ZodBoolean>;
1656
+ operator: z.ZodLiteral<"values_in">;
1657
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
1658
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1659
+ }, z.core.$strict>, z.ZodObject<{
1660
+ negate: z.ZodOptional<z.ZodBoolean>;
1661
+ operator: z.ZodLiteral<"exists">;
1662
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1663
+ }, z.core.$strict>, z.ZodObject<{
1664
+ negate: z.ZodOptional<z.ZodBoolean>;
1665
+ operator: z.ZodLiteral<"keys_in">;
1666
+ value: z.ZodArray<z.ZodString>;
1667
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1668
+ }, z.core.$strict>, z.ZodObject<{
1669
+ negate: z.ZodOptional<z.ZodBoolean>;
1670
+ operator: z.ZodLiteral<"size">;
1671
+ value: z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
1672
+ min: z.ZodOptional<z.ZodNumber>;
1673
+ max: z.ZodOptional<z.ZodNumber>;
1674
+ }, z.core.$strict>]>;
1675
+ path: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
1676
+ }, z.core.$strict>]>>;
1677
+ }, z.core.$strict>]>>;
1678
+ id: z.ZodOptional<z.ZodString>;
1679
+ ruleId: z.ZodString;
1680
+ effect: z.ZodLiteral<"allow">;
1389
1681
  }, z.core.$strict>]>;
1390
1682
  /** Derived from OverrideSchema — the schema is the single source of truth. */
1391
1683
  type Override = z.infer<typeof OverrideSchema>;
@@ -1432,6 +1724,18 @@ interface StatementTrace {
1432
1724
  passed: boolean;
1433
1725
  overrides?: OverrideTrace[];
1434
1726
  }
1727
+ /**
1728
+ * v4 rule trace. Identical to StatementTrace but the matched object is keyed
1729
+ * `rule` instead of `statement`, matching the v4 `statements`->`rules` rename.
1730
+ */
1731
+ interface RuleTrace {
1732
+ rule: Statement;
1733
+ permissionMatched: boolean;
1734
+ resourceMatched: boolean;
1735
+ conditionResults?: ConditionTrace[];
1736
+ passed: boolean;
1737
+ overrides?: OverrideTrace[];
1738
+ }
1435
1739
  interface EvaluatePolicyOptions {
1436
1740
  policy: Policy;
1437
1741
  request: PolicyRequest;
@@ -1447,6 +1751,19 @@ interface PolicyResult {
1447
1751
  /** Trace mode only. Present when no statement matched and an ask default was consulted (never attached for deny defaults). */
1448
1752
  default?: DefaultTrace;
1449
1753
  }
1754
+ /**
1755
+ * Result of evaluating a v4 policy. Version-gated shape: the trace array is
1756
+ * keyed `rules` (of RuleTrace) instead of `statements`, matching the v4
1757
+ * `statements`->`rules` rename. Only ever returned for a v4 policy; all other
1758
+ * versions return {@link PolicyResult}. v4 has no ask default (no
1759
+ * defaultEffect), so there is no `default` field.
1760
+ */
1761
+ interface PolicyResultV4 {
1762
+ effect: Effect;
1763
+ explicit: boolean;
1764
+ reason: PolicyReason;
1765
+ rules: RuleTrace[];
1766
+ }
1450
1767
  interface ParsedUrl {
1451
1768
  scheme: string;
1452
1769
  host: string;
@@ -1476,10 +1793,11 @@ interface OpenApiSchema {
1476
1793
  properties?: Record<string, OpenApiSchema>;
1477
1794
  items?: OpenApiSchema;
1478
1795
  enum?: (string | number)[];
1796
+ const?: string | number;
1479
1797
  description?: string;
1480
1798
  minItems?: number;
1481
1799
  oneOf?: OpenApiSchema[];
1482
1800
  anyOf?: OpenApiSchema[];
1483
1801
  }
1484
1802
 
1485
- export { type ArrayOperator, ArrayOperatorSchema, type CanonicalHash, CanonicalHashSchema, type Condition, ConditionSchema, type ConditionTrace, type ConditionValue, ConditionValueSchema, type DefaultEffect, DefaultEffectSchema, type DefaultTrace, type Effect, type EvaluatePolicyOptions, ID_BASED_VERSIONS, type JsonValue, type OpenApiSchema, type Operator, OperatorSchema, type OtherOperator, OtherOperatorSchema, type Override, OverrideSchema, type OverrideTrace, type ParsedUrl, type Policy, type PolicyActionRequest, type PolicyHttpRequest, type PolicyId, PolicyIdSchema, type PolicyReason, type PolicyRequest, type PolicyResult, PolicySchema, RUBBERDUCK_POLICY_VERSION, type RangeValue, RangeValueSchema, type ScalarOperator, ScalarOperatorSchema, type SizeValue, SizeValueSchema, type Statement, type StatementId, StatementIdSchema, StatementSchema, type StatementTrace, type UrlOperator, UrlOperatorSchema, V3_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
1803
+ export { type ArrayOperator, ArrayOperatorSchema, type CanonicalHash, CanonicalHashSchema, type Condition, ConditionSchema, type ConditionTrace, type ConditionValue, ConditionValueSchema, type DefaultEffect, DefaultEffectSchema, type DefaultTrace, type Effect, type EvaluatePolicyOptions, ID_BASED_VERSIONS, type JsonValue, type OpenApiSchema, type Operator, OperatorSchema, type OtherOperator, OtherOperatorSchema, type Override, OverrideSchema, type OverrideTrace, type ParsedUrl, type Policy, type PolicyActionRequest, type PolicyHttpRequest, type PolicyId, PolicyIdSchema, type PolicyReason, type PolicyRequest, type PolicyResult, type PolicyResultV4, PolicySchema, RUBBERDUCK_POLICY_VERSION, type RangeValue, RangeValueSchema, type RuleId, RuleIdSchema, type RuleTrace, type ScalarOperator, ScalarOperatorSchema, type SizeValue, SizeValueSchema, type Statement, type StatementId, StatementIdSchema, StatementSchema, type StatementTrace, type UrlOperator, UrlOperatorSchema, V3_MAX_TIERS, V4_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
package/dist/index.mjs CHANGED
@@ -2,9 +2,10 @@ import { z } from 'zod';
2
2
 
3
3
  // src/schema.ts
4
4
  var RUBBERDUCK_POLICY_VERSION = "rc-rubberduck";
5
- var VALID_VERSIONS = [1, 2, 3, RUBBERDUCK_POLICY_VERSION];
5
+ var VALID_VERSIONS = [1, 2, 3, 4, RUBBERDUCK_POLICY_VERSION];
6
6
  var ID_BASED_VERSIONS = /* @__PURE__ */ new Set([
7
7
  3,
8
+ 4,
8
9
  RUBBERDUCK_POLICY_VERSION
9
10
  ]);
10
11
  var VALID_EFFECTS = ["allow", "ask", "deny"];
@@ -189,6 +190,7 @@ var V2StatementSchema = z.object({
189
190
  var StatementSchema = V2StatementSchema;
190
191
  var StatementIdSchema = z.string().regex(/^s_\S+$/, { error: "Statement id must match ^s_\\S+$" }).max(512);
191
192
  var PolicyIdSchema = z.string().regex(/^p_\S+$/, { error: "Policy id must match ^p_\\S+$" }).max(512);
193
+ var RuleIdSchema = z.string().regex(/^r_\S+$/, { error: "Rule id must match ^r_\\S+$" }).max(512);
192
194
  var V1PolicySchema = z.object({
193
195
  version: z.literal(1),
194
196
  statements: z.array(V1StatementSchema)
@@ -225,10 +227,25 @@ var V3PolicySchema = z.object({
225
227
  // empty (e.g. `[[]]` denies everything).
226
228
  statements: z.array(z.array(V3StatementSchema)).min(1).max(V3_MAX_TIERS)
227
229
  }).strict();
230
+ var V4_MAX_TIERS = 3;
231
+ var V4RuleSchema = z.object({
232
+ id: RuleIdSchema,
233
+ effect: z.enum(["allow", "ask", "deny"]),
234
+ permissions: z.array(z.string()).min(1),
235
+ resources: z.array(z.string()).min(1),
236
+ conditions: z.array(ConditionSchema).optional()
237
+ }).strict();
238
+ var V4PolicySchema = z.object({
239
+ version: z.literal(4),
240
+ // Ordered tiers, in priority order. 1..V4_MAX_TIERS tiers; a tier may be
241
+ // empty (e.g. `[[]]` denies everything). Renamed from v3's `statements`.
242
+ rules: z.array(z.array(V4RuleSchema)).min(1).max(V4_MAX_TIERS)
243
+ }).strict();
228
244
  var PolicySchema = z.union([
229
245
  V1PolicySchema,
230
246
  V2PolicySchema,
231
247
  V3PolicySchema,
248
+ V4PolicySchema,
232
249
  RcRubberduckPolicySchema
233
250
  ]);
234
251
  var CanonicalHashSchema = z.custom(
@@ -263,6 +280,12 @@ var OverridePolicyIdBaseShape = {
263
280
  policyId: PolicyIdSchema,
264
281
  effect: z.literal("allow")
265
282
  };
283
+ var OverrideRuleIdBaseShape = {
284
+ id: z.string().optional(),
285
+ /** Targets a v4 rule by id (r_ prefix, non-null). */
286
+ ruleId: RuleIdSchema,
287
+ effect: z.literal("allow")
288
+ };
266
289
  var OverrideSchema = z.union([
267
290
  // statementHash × match
268
291
  z.object({ ...OverrideHashBaseShape, match: z.literal("any") }).strict(),
@@ -290,6 +313,15 @@ var OverrideSchema = z.union([
290
313
  z.object({
291
314
  ...OverridePolicyIdBaseShape,
292
315
  conditionals: z.array(ConditionSchema)
316
+ }).strict(),
317
+ // ruleId × match
318
+ z.object({ ...OverrideRuleIdBaseShape, match: z.literal("any") }).strict(),
319
+ // ruleId × contextHash
320
+ z.object({ ...OverrideRuleIdBaseShape, contextHash: CanonicalHashSchema }).strict(),
321
+ // ruleId × conditionals
322
+ z.object({
323
+ ...OverrideRuleIdBaseShape,
324
+ conditionals: z.array(ConditionSchema)
293
325
  }).strict()
294
326
  ]);
295
327
  function getOpenApiSchema() {
@@ -304,7 +336,7 @@ function getOpenApiSchema() {
304
336
  effect: {
305
337
  type: "string",
306
338
  enum: [...VALID_EFFECTS].sort(),
307
- description: "Whether to allow, ask, or deny the specified permissions. The 'ask' effect is valid in version 2, version 3, and rc-rubberduck policies."
339
+ description: "Whether to allow, ask, or deny the specified permissions. The 'ask' effect is valid in version 2, version 3, version 4, and rc-rubberduck policies."
308
340
  },
309
341
  permissions: {
310
342
  type: "array",
@@ -383,42 +415,123 @@ function getOpenApiSchema() {
383
415
  }
384
416
  }
385
417
  };
386
- return {
387
- type: "object",
388
- required: ["version", "statements"],
418
+ const ruleItems = {
419
+ ...statementItems,
389
420
  properties: {
390
- version: {
391
- anyOf: [
392
- { type: "integer", enum: [1, 2, 3] },
393
- { type: "string", enum: [RUBBERDUCK_POLICY_VERSION] }
394
- ],
395
- description: "Policy schema version. Stable versions are 1, 2, and 3; supported release candidates are rc-rubberduck."
396
- },
421
+ ...statementItems.properties,
397
422
  id: {
398
423
  type: "string",
399
- description: "Policy id (required for rc-rubberduck policies; not permitted on any other version). Must use the p_ prefix and contain no whitespace."
424
+ description: "Rule identifier. Required for v4 policies; must use the r_ prefix with no whitespace."
400
425
  },
401
- defaultEffect: {
402
- type: "string",
403
- enum: [...DefaultEffectSchema.options],
404
- description: "Effect applied when no statement matches. Required on rc-rubberduck policies (deny equals implicit deny); not permitted on any other version."
426
+ resources: {
427
+ ...statementItems.properties.resources,
428
+ description: "List of resource identifiers the rule applies to."
405
429
  },
406
- statements: {
407
- type: "array",
408
- description: "Policy statements. For v1/v2/rc-rubberduck each entry is a statement. For v3 each entry is a tier (a list of statements); tiers are in priority order and evaluated first-to-last (the first tier with a match decides), so `statements` is a list of lists.",
409
- items: {
410
- anyOf: [
411
- statementItems,
412
- {
413
- type: "array",
414
- description: "A v3 tier: statements evaluated together with deny > ask > allow precedence.",
415
- items: statementItems
416
- }
417
- ]
418
- }
430
+ conditions: {
431
+ ...statementItems.properties.conditions,
432
+ description: "Optional conditions that must be met for the rule to apply."
419
433
  }
420
434
  }
421
435
  };
436
+ const versionDescription = "Policy schema version. Stable versions are 1, 2, 3, and 4; supported release candidates are rc-rubberduck.";
437
+ const flatStatements = {
438
+ type: "array",
439
+ description: "Policy statements; each entry is a statement.",
440
+ items: statementItems
441
+ };
442
+ const statementTiers = {
443
+ type: "array",
444
+ description: "v3 tiers in priority order; each entry is a tier (a list of statements) evaluated first-to-last (the first tier with a match decides), so `statements` is a list of lists.",
445
+ items: {
446
+ type: "array",
447
+ description: "A v3 tier: statements evaluated together with deny > ask > allow precedence.",
448
+ items: statementItems
449
+ }
450
+ };
451
+ const ruleTiers = {
452
+ type: "array",
453
+ description: "v4 rules in priority order; each entry is a tier (a list of rules) evaluated first-to-last (the first tier with a match decides), so `rules` is a list of lists.",
454
+ items: {
455
+ type: "array",
456
+ description: "A v4 tier: rules evaluated together with deny > ask > allow precedence.",
457
+ items: ruleItems
458
+ }
459
+ };
460
+ const idProperty = {
461
+ type: "string",
462
+ description: "Policy id (rc-rubberduck only). Must use the p_ prefix and contain no whitespace."
463
+ };
464
+ const defaultEffectProperty = {
465
+ type: "string",
466
+ enum: [...DefaultEffectSchema.options],
467
+ description: "Effect applied when no statement matches (rc-rubberduck only; deny equals implicit deny)."
468
+ };
469
+ return {
470
+ oneOf: [
471
+ {
472
+ type: "object",
473
+ required: ["version", "statements"],
474
+ properties: {
475
+ version: {
476
+ type: "integer",
477
+ const: 1,
478
+ description: versionDescription
479
+ },
480
+ statements: flatStatements
481
+ }
482
+ },
483
+ {
484
+ type: "object",
485
+ required: ["version", "statements"],
486
+ properties: {
487
+ version: {
488
+ type: "integer",
489
+ const: 2,
490
+ description: versionDescription
491
+ },
492
+ statements: flatStatements
493
+ }
494
+ },
495
+ {
496
+ type: "object",
497
+ required: ["version", "statements"],
498
+ properties: {
499
+ version: {
500
+ type: "integer",
501
+ const: 3,
502
+ description: versionDescription
503
+ },
504
+ statements: statementTiers
505
+ }
506
+ },
507
+ {
508
+ type: "object",
509
+ required: ["version", "rules"],
510
+ properties: {
511
+ version: {
512
+ type: "integer",
513
+ const: 4,
514
+ description: versionDescription
515
+ },
516
+ rules: ruleTiers
517
+ }
518
+ },
519
+ {
520
+ type: "object",
521
+ required: ["version", "id", "defaultEffect", "statements"],
522
+ properties: {
523
+ version: {
524
+ type: "string",
525
+ const: RUBBERDUCK_POLICY_VERSION,
526
+ description: versionDescription
527
+ },
528
+ id: idProperty,
529
+ defaultEffect: defaultEffectProperty,
530
+ statements: flatStatements
531
+ }
532
+ }
533
+ ]
534
+ };
422
535
  }
423
536
 
424
- export { ArrayOperatorSchema, CanonicalHashSchema, ConditionSchema, ConditionValueSchema, DefaultEffectSchema, ID_BASED_VERSIONS, OperatorSchema, OtherOperatorSchema, OverrideSchema, PolicyIdSchema, PolicySchema, RUBBERDUCK_POLICY_VERSION, RangeValueSchema, ScalarOperatorSchema, SizeValueSchema, StatementIdSchema, StatementSchema, UrlOperatorSchema, V3_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
537
+ export { ArrayOperatorSchema, CanonicalHashSchema, ConditionSchema, ConditionValueSchema, DefaultEffectSchema, ID_BASED_VERSIONS, OperatorSchema, OtherOperatorSchema, OverrideSchema, PolicyIdSchema, PolicySchema, RUBBERDUCK_POLICY_VERSION, RangeValueSchema, RuleIdSchema, ScalarOperatorSchema, SizeValueSchema, StatementIdSchema, StatementSchema, UrlOperatorSchema, V3_MAX_TIERS, V4_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/policy-schema",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Zod schemas and TypeScript types for the Zapier policy engine",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",