@zapier/policy-context 2.0.0 → 3.0.0-mr.39.bc800a62

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/README.md CHANGED
@@ -16,7 +16,7 @@ npm install @zapier/policy-context zod
16
16
 
17
17
  ## What it provides
18
18
 
19
- Three layers, each building on the previous:
19
+ Four layers, each building on the previous:
20
20
 
21
21
  1. **Types** — Zod schemas for `HttpRequestContext`, `ActionRunContext`, and the
22
22
  `RequestContext` discriminated union. The canonical shapes stored in
@@ -25,7 +25,11 @@ Three layers, each building on the previous:
25
25
  body coercion) into validated contexts.
26
26
  3. **Conditions / Policies** — Convert stored contexts into
27
27
  [`@zapier/policy-schema`](https://www.npmjs.com/package/@zapier/policy-schema)
28
- `Condition[]` and full `Policy` documents.
28
+ `Condition[]` and full policy documents. Canonical builders emit v4 with one
29
+ rule tier (`rules: [[...]]`) and randomly generated `r_<uuid>` rule IDs;
30
+ `toExactPolicyV3` is an explicit legacy exact-request escape hatch.
31
+ 4. **Zapier policy validation** — `ZapierPolicySchema` composes the generic
32
+ `PolicySchema` with Zapier resource grammar and permission/resource rules.
29
33
 
30
34
  ## Usage
31
35
 
@@ -65,6 +69,27 @@ import { toExactPolicy } from "@zapier/policy-context";
65
69
  const policy = toExactPolicy(context);
66
70
  ```
67
71
 
72
+ `toExactPolicy` is the canonical exact builder and returns
73
+ `Extract<Policy, { version: 4 }>`, with a single rule tier (`rules: [[...]]`)
74
+ and `r_<uuid>` rule IDs. Rule content and ordering are unchanged. Migrate
75
+ `policy.statements[0][i]` to `policy.rules[0][i]` and any statement-ID prefix
76
+ handling from `s_` to `r_`.
77
+
78
+ For a legacy consumer that still requires an exact v3 document, use the explicit
79
+ factory instead:
80
+
81
+ ```ts
82
+ import { toExactPolicyV3 } from "@zapier/policy-context";
83
+
84
+ const legacyPolicy = toExactPolicyV3(context);
85
+ ```
86
+
87
+ `toExactPolicyV3` returns `Extract<Policy, { version: 3 }>`, with `version: 3`,
88
+ one statement tier (`statements: [[...]]`), and `s_<uuid>` statement IDs. It
89
+ preserves the canonical exact builder's conditions, content, and ordering.
90
+ `buildConnectionPolicy` and `buildConnectionlessPolicy` remain v4; this escape
91
+ hatch is only for exact-request policies.
92
+
68
93
  ### Connection-scoped policies
69
94
 
70
95
  ```ts
@@ -76,6 +101,24 @@ const policy = buildConnectionPolicy({
76
101
  });
77
102
  ```
78
103
 
104
+ ### Validate a Zapier policy
105
+
106
+ ```ts
107
+ import { ZapierPolicySchema } from "@zapier/policy-context";
108
+
109
+ const policy = ZapierPolicySchema.parse(input);
110
+ ```
111
+
112
+ `ZapierPolicySchema` output includes v4 documents as well as earlier versions.
113
+ This accepts `http`, `connection/{id|*}`, canonical
114
+ `action/{selected_api}/{action_type|*}/{action_key}` patterns, and the lone `*`
115
+ god-mode resource. It validates structure only; app, API version, and action-key
116
+ existence remain the caller's responsibility. It also rejects a statement or rule
117
+ when one higher-precedence statement or rule (`deny` > `ask` > `allow`) fully
118
+ subsumes it. Validation traverses each ordered tier of v3 `statements` and v4
119
+ `rules`, checking masking only within that tier. Same-effect redundancy and
120
+ coverage assembled from multiple statements or rules remain valid.
121
+
79
122
  ## License
80
123
 
81
124
  Published as part of Zapier's services. Use is governed by the
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AccessLevelSchema: () => AccessLevelSchema,
24
+ ActionResourcePatternSchema: () => ActionResourcePatternSchema,
24
25
  ActionRunContextInputSchema: () => ActionRunContextInputSchema,
25
26
  ActionRunContextSchema: () => ActionRunContextSchema,
26
27
  ActionTypeSchema: () => ActionTypeSchema,
@@ -33,6 +34,7 @@ __export(index_exports, {
33
34
  READ_ACTION_TYPES: () => READ_ACTION_TYPES,
34
35
  READ_HTTP_METHODS: () => READ_HTTP_METHODS,
35
36
  RequestContextSchema: () => RequestContextSchema,
37
+ ZapierPolicySchema: () => ZapierPolicySchema,
36
38
  buildActionResource: () => buildActionResource,
37
39
  buildActionRunContext: () => buildActionRunContext,
38
40
  buildBroadActionResource: () => buildBroadActionResource,
@@ -49,12 +51,28 @@ __export(index_exports, {
49
51
  parseActionResource: () => parseActionResource,
50
52
  parseUrl: () => parseUrl,
51
53
  toAppKey: () => toAppKey,
52
- toExactPolicy: () => toExactPolicy
54
+ toExactPolicy: () => toExactPolicy,
55
+ toExactPolicyV3: () => toExactPolicyV3
53
56
  });
54
57
  module.exports = __toCommonJS(index_exports);
55
58
 
56
59
  // src/types.ts
57
60
  var import_zod = require("zod");
61
+ function containsUnsafeInteger(value) {
62
+ if (typeof value === "number") {
63
+ return Number.isInteger(value) && !Number.isSafeInteger(value);
64
+ }
65
+ if (Array.isArray(value)) {
66
+ return value.some(containsUnsafeInteger);
67
+ }
68
+ if (value !== null && typeof value === "object") {
69
+ return Object.values(value).some(containsUnsafeInteger);
70
+ }
71
+ return false;
72
+ }
73
+ var SafeJsonValueSchema = import_zod.z.json().refine((value) => !containsUnsafeInteger(value), {
74
+ message: "JSON integers must be within the IEEE-754 safe range; encode larger identifiers as strings"
75
+ });
58
76
  var ParsedUrlSchema = import_zod.z.object({
59
77
  scheme: import_zod.z.string().describe('URL scheme, e.g. "https"'),
60
78
  host: import_zod.z.string().describe('Hostname, e.g. "api.example.com"'),
@@ -113,7 +131,7 @@ var HttpRequestContextSchema = import_zod.z.object({
113
131
  * `z.json()` still rejects non-JSON values (`Date`, `undefined`,
114
132
  * functions) at parse time. Same inferred `JsonValue` type either way.
115
133
  */
116
- body_json: import_zod.z.unknown().pipe(import_zod.z.json()).nullable().describe("Parsed JSON body (if applicable)"),
134
+ body_json: import_zod.z.unknown().pipe(SafeJsonValueSchema).nullable().describe("Parsed JSON body (if applicable)"),
117
135
  /** Zapier connection ID used for this request */
118
136
  connection_id: import_zod.z.string().max(255).optional().describe("Zapier connection ID used for this request"),
119
137
  /** Optional human-readable reason for the request */
@@ -131,6 +149,11 @@ var ActionTypeSchema = import_zod.z.enum([
131
149
  ]).meta({ id: "action_type", description: "Canonical Zapier action type." });
132
150
  var ACTION_RESOURCE_SEGMENT_PATTERN = /^[A-Za-z0-9._+-]+(?:@[A-Za-z0-9._+-]+)?$/;
133
151
  var ACTION_KEY_SEGMENT_PATTERN = /^[A-Za-z0-9._+:-]+(?:@[A-Za-z0-9._+:-]+)?$/;
152
+ var CONNECTION_ID_CHARS = "[^/*\\x00-\\x20\\x7f-\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]";
153
+ var CONNECTION_ID_PATTERN = new RegExp(`^${CONNECTION_ID_CHARS}+$`);
154
+ var CONNECTION_RESOURCE_PATTERN = new RegExp(
155
+ `^connection/(?:\\*|${CONNECTION_ID_CHARS}+)$`
156
+ );
134
157
  function parseActionResource(resource) {
135
158
  const segments = resource.split("/");
136
159
  if (segments.length !== 4 || segments[0] !== "action") {
@@ -191,7 +214,7 @@ var ActionRunContextInputSchema = import_zod.z.object({
191
214
  }),
192
215
  connection_id: import_zod.z.string().max(255).optional().describe("Zapier connection ID used for this request"),
193
216
  /** Action input parameters. See body_json above for the pipe rationale. */
194
- inputs: import_zod.z.unknown().pipe(import_zod.z.json()).optional().describe("Action input parameters"),
217
+ inputs: import_zod.z.unknown().pipe(SafeJsonValueSchema).optional().describe("Action input parameters"),
195
218
  reason: import_zod.z.string().max(1024).optional().describe("Optional human-readable reason for the request")
196
219
  }).meta({
197
220
  id: "ActionRunContext",
@@ -222,7 +245,7 @@ var ActionRunContextSchema = ActionRunContextInputSchema.transform(
222
245
  ...input.connection_id !== void 0 && {
223
246
  connection_id: input.connection_id
224
247
  },
225
- ...input.inputs !== void 0 && { inputs: input.inputs },
248
+ ...input.inputs !== void 0 && input.inputs !== null && { inputs: input.inputs },
226
249
  ...input.reason !== void 0 && { reason: input.reason }
227
250
  };
228
251
  }
@@ -297,14 +320,14 @@ function buildExactHttpConditions(context) {
297
320
  conditions.push({
298
321
  path: ["url", "query", key],
299
322
  operator: "equals",
300
- value: values
323
+ value: structuredClone(values)
301
324
  });
302
325
  }
303
326
  if (context.body_json !== null) {
304
327
  conditions.push({
305
328
  path: ["body_json"],
306
329
  operator: "equals",
307
- value: context.body_json
330
+ value: structuredClone(context.body_json)
308
331
  });
309
332
  } else if (context.body !== null) {
310
333
  conditions.push({
@@ -339,7 +362,7 @@ function buildExactActionConditions(context) {
339
362
  conditions.push({
340
363
  path: ["inputs"],
341
364
  operator: "equals",
342
- value: context.inputs
365
+ value: structuredClone(context.inputs)
343
366
  });
344
367
  }
345
368
  return conditions;
@@ -364,7 +387,7 @@ var BuildHttpRequestContextOptionsSchema = import_zod2.z.object({
364
387
  /** Raw body string */
365
388
  body: import_zod2.z.string().nullable().optional(),
366
389
  /** Pre-parsed JSON body (e.g. from Fastify's automatic JSON parsing) */
367
- body_json: import_zod2.z.json().optional(),
390
+ body_json: SafeJsonValueSchema.optional(),
368
391
  /** Connection ID associated with this request */
369
392
  connection_id: import_zod2.z.string().optional()
370
393
  });
@@ -413,7 +436,7 @@ var BuildActionRunContextOptionsSchema = import_zod2.z.object({
413
436
  message: "action_key must contain only [A-Za-z0-9._@:+-] (no globs, slashes, or whitespace)"
414
437
  }),
415
438
  connection_id: import_zod2.z.string().optional(),
416
- inputs: import_zod2.z.json().optional(),
439
+ inputs: SafeJsonValueSchema.optional(),
417
440
  reason: import_zod2.z.string().optional()
418
441
  });
419
442
  function buildActionRunContext(options) {
@@ -432,56 +455,212 @@ function buildActionRunContext(options) {
432
455
  }
433
456
 
434
457
  // src/policies.ts
435
- function buildAuthStatement(authenticationId) {
458
+ function buildAuthStatement(authenticationId, idPrefix) {
436
459
  return {
460
+ id: `${idPrefix}_${crypto.randomUUID()}`,
437
461
  effect: "allow",
438
462
  permissions: ["can_use"],
439
463
  resources: [`connection/${authenticationId}`],
440
464
  conditions: []
441
465
  };
442
466
  }
443
- function httpContextToPolicy(context) {
444
- const statements = [];
445
- if (context.connection_id) {
446
- statements.push(buildAuthStatement(context.connection_id));
447
- }
467
+ function httpContextToTier(context, idPrefix) {
448
468
  const conditions = buildExactHttpConditions(context);
449
469
  conditions.push({
450
470
  path: ["url", "query"],
451
471
  operator: "keys_in",
452
472
  value: Object.keys(context.url.query)
453
473
  });
454
- statements.push({
474
+ const statement = {
475
+ id: `${idPrefix}_${crypto.randomUUID()}`,
455
476
  effect: "allow",
456
477
  permissions: ["can_execute"],
457
478
  resources: ["http"],
458
479
  conditions
459
- });
460
- return { version: 2, statements };
480
+ };
481
+ return context.connection_id ? [buildAuthStatement(context.connection_id, idPrefix), statement] : [statement];
461
482
  }
462
- function actionContextToPolicy(context) {
463
- const statements = [];
464
- if (context.connection_id) {
465
- statements.push(buildAuthStatement(context.connection_id));
466
- }
467
- statements.push({
483
+ function actionContextToTier(context, idPrefix) {
484
+ const statement = {
485
+ id: `${idPrefix}_${crypto.randomUUID()}`,
468
486
  effect: "allow",
469
487
  permissions: ["can_execute"],
470
488
  resources: [context.resource],
471
489
  conditions: buildExactActionConditions(context)
472
- });
473
- return { version: 2, statements };
490
+ };
491
+ return context.connection_id ? [buildAuthStatement(context.connection_id, idPrefix), statement] : [statement];
474
492
  }
475
493
  function toExactPolicy(context) {
476
- if (context.request_type === "http_request") {
477
- return httpContextToPolicy(context);
494
+ return {
495
+ version: 4,
496
+ rules: [
497
+ context.request_type === "http_request" ? httpContextToTier(context, "r") : actionContextToTier(context, "r")
498
+ ]
499
+ };
500
+ }
501
+ function toExactPolicyV3(context) {
502
+ return {
503
+ version: 3,
504
+ statements: [
505
+ context.request_type === "http_request" ? httpContextToTier(context, "s") : actionContextToTier(context, "s")
506
+ ]
507
+ };
508
+ }
509
+
510
+ // src/zapier-policy.ts
511
+ var import_policy = require("@zapier/policy");
512
+ var import_policy_schema = require("@zapier/policy-schema");
513
+ var import_zod3 = require("zod");
514
+ var SELECTED_API_GLOB_PART_PATTERN = /^[A-Za-z0-9._+*-]+$/;
515
+ var ACTION_KEY_GLOB_PART_PATTERN = /^[A-Za-z0-9._+:*-]+$/;
516
+ var PERMISSIONS = /* @__PURE__ */ new Set([
517
+ "can_execute",
518
+ "can_use",
519
+ "can_view",
520
+ "can_edit",
521
+ "*"
522
+ ]);
523
+ var EFFECT_PRECEDENCE = { allow: 0, ask: 1, deny: 2 };
524
+ function isSelectedApiPattern(value) {
525
+ return isGlobSegment(
526
+ value,
527
+ ACTION_RESOURCE_SEGMENT_PATTERN,
528
+ SELECTED_API_GLOB_PART_PATTERN
529
+ );
530
+ }
531
+ function isActionKeyPattern(value) {
532
+ return isGlobSegment(
533
+ value,
534
+ ACTION_KEY_SEGMENT_PATTERN,
535
+ ACTION_KEY_GLOB_PART_PATTERN
536
+ );
537
+ }
538
+ function isGlobSegment(value, exactPattern, globPartPattern) {
539
+ if (exactPattern.test(value)) {
540
+ return true;
541
+ }
542
+ if (!value.includes("*") || value.includes("**")) {
543
+ return false;
544
+ }
545
+ const parts = value.split("@");
546
+ return parts.length <= 2 && parts.every((part) => globPartPattern.test(part));
547
+ }
548
+ function isActionResourcePattern(value) {
549
+ const segments = value.split("/");
550
+ if (segments.length !== 4 || segments[0] !== "action") {
551
+ return false;
552
+ }
553
+ const [, selectedApi, actionType, actionKey] = segments;
554
+ return selectedApi !== void 0 && actionType !== void 0 && actionKey !== void 0 && isSelectedApiPattern(selectedApi) && (actionType === "*" || ActionTypeSchema.safeParse(actionType).success) && isActionKeyPattern(actionKey);
555
+ }
556
+ var ActionResourcePatternSchema = import_zod3.z.string().refine(isActionResourcePattern, {
557
+ message: "Expected action/{selected_api}/{canonical_action_type|*}/{action_key_pattern}"
558
+ });
559
+ function resourceFamily(resource) {
560
+ if (resource === "*" || resource === "http") {
561
+ return resource;
478
562
  }
479
- return actionContextToPolicy(context);
563
+ if (ActionResourcePatternSchema.safeParse(resource).success) {
564
+ return "action";
565
+ }
566
+ if (CONNECTION_RESOURCE_PATTERN.test(resource)) {
567
+ return "connection";
568
+ }
569
+ return void 0;
480
570
  }
571
+ function permissionAppliesTo(permission, family) {
572
+ if (permission === "*" || family === "*") {
573
+ return true;
574
+ }
575
+ return permission === "can_execute" ? family === "action" || family === "http" : family === "connection";
576
+ }
577
+ function statementGroups(policy) {
578
+ if (policy.version === 4) {
579
+ return policy.rules.map((rules, tierIndex) => ({
580
+ statements: rules,
581
+ path: ["rules", tierIndex],
582
+ entry: "rule"
583
+ }));
584
+ }
585
+ if (policy.version === 3) {
586
+ return policy.statements.map((statements, tierIndex) => ({
587
+ statements,
588
+ path: ["statements", tierIndex],
589
+ entry: "statement"
590
+ }));
591
+ }
592
+ return [
593
+ {
594
+ statements: policy.statements,
595
+ path: ["statements"],
596
+ entry: "statement"
597
+ }
598
+ ];
599
+ }
600
+ var ZapierPolicySchema = import_policy_schema.PolicySchema.superRefine((policy, ctx) => {
601
+ statementGroups(policy).forEach(({ statements, path, entry }) => {
602
+ const Entry = entry === "rule" ? "Rule" : "Statement";
603
+ statements.forEach((statement, statementIndex) => {
604
+ const families = statement.resources.map((resource, resourceIndex) => {
605
+ const family = resourceFamily(resource);
606
+ if (family === void 0) {
607
+ ctx.addIssue({
608
+ code: "custom",
609
+ message: 'Expected "http", "connection/{id|*}", a valid action pattern, or "*"',
610
+ path: [...path, statementIndex, "resources", resourceIndex]
611
+ });
612
+ }
613
+ return family;
614
+ });
615
+ statement.permissions.forEach((permission, permissionIndex) => {
616
+ if (!PERMISSIONS.has(permission)) {
617
+ ctx.addIssue({
618
+ code: "custom",
619
+ message: `Unknown Zapier permission "${permission}"`,
620
+ path: [...path, statementIndex, "permissions", permissionIndex]
621
+ });
622
+ return;
623
+ }
624
+ const incompatibleFamily = families.find(
625
+ (family) => family !== void 0 && !permissionAppliesTo(permission, family)
626
+ );
627
+ if (incompatibleFamily !== void 0) {
628
+ ctx.addIssue({
629
+ code: "custom",
630
+ message: `Permission "${permission}" cannot apply to ${incompatibleFamily} resources`,
631
+ path: [...path, statementIndex, "permissions", permissionIndex]
632
+ });
633
+ }
634
+ });
635
+ const maskingStatementIndex = statements.findIndex(
636
+ (maskingStatement, maskingIndex) => maskingIndex !== statementIndex && EFFECT_PRECEDENCE[maskingStatement.effect] > EFFECT_PRECEDENCE[statement.effect] && (0, import_policy.isStatementSubsumedBy)(statement, {
637
+ ...maskingStatement,
638
+ effect: statement.effect
639
+ })
640
+ );
641
+ if (maskingStatementIndex !== -1) {
642
+ const maskingStatement = statements[maskingStatementIndex];
643
+ const maskingId = maskingStatement.id ? ` (id "${maskingStatement.id}")` : "";
644
+ ctx.addIssue({
645
+ code: "custom",
646
+ message: `${Entry} is entirely masked by higher-precedence ${entry} at index ${maskingStatementIndex}${maskingId}`,
647
+ path: [...path, statementIndex]
648
+ });
649
+ }
650
+ });
651
+ });
652
+ });
481
653
 
482
654
  // src/connection-policy.ts
483
- var import_zod3 = require("zod");
484
- var AccessLevelSchema = import_zod3.z.enum(["read", "read_write"]);
655
+ var import_zod4 = require("zod");
656
+ var AccessLevelSchema = import_zod4.z.enum(["read", "read_write"]);
657
+ function requireAccessLevel(accessLevel) {
658
+ if (!AccessLevelSchema.safeParse(accessLevel).success) {
659
+ throw new Error(
660
+ `invalid access_level "${accessLevel}": expected "read" or "read_write"`
661
+ );
662
+ }
663
+ }
485
664
  var READ_HTTP_METHODS = ["GET", "HEAD", "OPTIONS"];
486
665
  var READ_ACTION_TYPES = [
487
666
  "read",
@@ -501,7 +680,19 @@ function isWriteRequest(context) {
501
680
  }
502
681
  function buildConnectionPolicy(options) {
503
682
  const { connectionId, selectedApi, accessLevel } = options;
683
+ requireAccessLevel(accessLevel);
684
+ if (!CONNECTION_ID_PATTERN.test(connectionId)) {
685
+ throw new Error(
686
+ `invalid connection_id "${connectionId}": must not contain "/", "*", or whitespace`
687
+ );
688
+ }
689
+ if (!ACTION_RESOURCE_SEGMENT_PATTERN.test(selectedApi)) {
690
+ throw new Error(
691
+ `invalid selected_api "${selectedApi}": must not contain "/", "*", or whitespace`
692
+ );
693
+ }
504
694
  const authStatement = {
695
+ id: `r_${crypto.randomUUID()}`,
505
696
  effect: "allow",
506
697
  permissions: ["can_use"],
507
698
  resources: [`connection/${connectionId}`],
@@ -521,6 +712,7 @@ function buildConnectionPolicy(options) {
521
712
  });
522
713
  }
523
714
  const httpStatement = {
715
+ id: `r_${crypto.randomUUID()}`,
524
716
  effect: "allow",
525
717
  permissions: ["can_execute"],
526
718
  resources: ["http"],
@@ -529,20 +721,22 @@ function buildConnectionPolicy(options) {
529
721
  const apiPattern = globSelectedApiVersion(selectedApi);
530
722
  const actionResources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
531
723
  const actionStatement = {
724
+ id: `r_${crypto.randomUUID()}`,
532
725
  effect: "allow",
533
726
  permissions: ["can_execute"],
534
727
  resources: actionResources,
535
728
  conditions: [connectionCondition]
536
729
  };
537
730
  return {
538
- version: 2,
539
- statements: [authStatement, httpStatement, actionStatement]
731
+ version: 4,
732
+ rules: [[authStatement, httpStatement, actionStatement]]
540
733
  };
541
734
  }
542
735
 
543
736
  // src/connectionless-policy.ts
544
737
  function buildConnectionlessPolicy(options) {
545
738
  const { context, accessLevel } = options;
739
+ requireAccessLevel(accessLevel);
546
740
  if (context.request_type === "http_request") {
547
741
  const conditions = [
548
742
  {
@@ -559,27 +753,36 @@ function buildConnectionlessPolicy(options) {
559
753
  });
560
754
  }
561
755
  const statement2 = {
756
+ id: `r_${crypto.randomUUID()}`,
562
757
  effect: "allow",
563
758
  permissions: ["can_execute"],
564
759
  resources: ["http"],
565
760
  conditions
566
761
  };
567
- return { version: 2, statements: [statement2] };
762
+ return {
763
+ version: 4,
764
+ rules: [[statement2]]
765
+ };
568
766
  }
569
767
  const { selected_api } = parseActionResource(context.resource);
570
768
  const apiPattern = globSelectedApiVersion(selected_api);
571
769
  const resources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
572
770
  const statement = {
771
+ id: `r_${crypto.randomUUID()}`,
573
772
  effect: "allow",
574
773
  permissions: ["can_execute"],
575
774
  resources,
576
775
  conditions: []
577
776
  };
578
- return { version: 2, statements: [statement] };
777
+ return {
778
+ version: 4,
779
+ rules: [[statement]]
780
+ };
579
781
  }
580
782
  // Annotate the CommonJS export names for ESM import in node:
581
783
  0 && (module.exports = {
582
784
  AccessLevelSchema,
785
+ ActionResourcePatternSchema,
583
786
  ActionRunContextInputSchema,
584
787
  ActionRunContextSchema,
585
788
  ActionTypeSchema,
@@ -592,6 +795,7 @@ function buildConnectionlessPolicy(options) {
592
795
  READ_ACTION_TYPES,
593
796
  READ_HTTP_METHODS,
594
797
  RequestContextSchema,
798
+ ZapierPolicySchema,
595
799
  buildActionResource,
596
800
  buildActionRunContext,
597
801
  buildBroadActionResource,
@@ -608,6 +812,7 @@ function buildConnectionlessPolicy(options) {
608
812
  parseActionResource,
609
813
  parseUrl,
610
814
  toAppKey,
611
- toExactPolicy
815
+ toExactPolicy,
816
+ toExactPolicyV3
612
817
  });
613
818
  //# sourceMappingURL=index.cjs.map