permdock 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/{adoption-scan-DKCOsfcF.js → adoption-scan-oks22j9J.js} +1 -1
  2. package/dist/{arazzo-CTH5iN--.js → arazzo-BzVfXbhf.js} +1 -1
  3. package/dist/{arazzo-BXaEhGHM.js → arazzo-CBBIhC40.js} +1 -1
  4. package/dist/better-supabase/index.d.ts +8 -0
  5. package/dist/better-supabase/index.js +27 -0
  6. package/dist/{casbin-DTi6HSsl.js → casbin-BunKq48W.js} +1 -1
  7. package/dist/{casbin-BuTZnSPv.js → casbin-C8r5trwy.js} +1 -1
  8. package/dist/cli/bin.js +1 -1
  9. package/dist/cli/index.js +1 -1
  10. package/dist/{cloud-DiAiYFRo.js → cloud-C_Mau1q2.js} +1 -1
  11. package/dist/{codemod-_q_ZI01S.js → codemod-BqYPToUJ.js} +2 -2
  12. package/dist/{codemod-C1U8_Zu3.js → codemod-CpMCbp0G.js} +1 -1
  13. package/dist/{completion-DLTuCf53.js → completion-Cq4fbT2S.js} +1 -1
  14. package/dist/{config-CmydKZr2.js → config-0xxCmoD8.js} +1 -1
  15. package/dist/{config-report-5AwptLr2.js → config-report-C1-r59VY.js} +1 -1
  16. package/dist/{doctor-ktitU5Dl.js → doctor-CDfzrMQB.js} +7 -7
  17. package/dist/{doctor-DxyVeUEE.js → doctor-Dea6IqRw.js} +2 -2
  18. package/dist/{import-t1yqnCyG.js → import-PP7uH8rC.js} +1 -1
  19. package/dist/{init-DFwylmiN.js → init-BuUt4bLJ.js} +1 -1
  20. package/dist/{init-CY7THM2v.js → init-n1gkKHvc.js} +3 -3
  21. package/dist/{login-C5XLYZSF.js → login-DyQNWJok.js} +3 -3
  22. package/dist/{login-B92-dZMK.js → login-YsrgkAQo.js} +1 -1
  23. package/dist/{manifest-Cav1rjNQ.js → manifest-Cl6yHiVK.js} +29 -29
  24. package/dist/{openapi-gMQQSzNn.js → openapi-BFyBIe9J.js} +2 -2
  25. package/dist/{openapi-CuePQL4z.js → openapi-D8JQtO8k.js} +1 -1
  26. package/dist/{output-CtyBwTNB.js → output-DpCUAST3.js} +1 -1
  27. package/dist/{powersync-CD8-Ypao.js → powersync-BTLk8GSl.js} +1 -1
  28. package/dist/{powersync-CBcsEvdr.js → powersync-Bm3ZjHwD.js} +2 -2
  29. package/dist/powersync-CObULVRk.js +1 -0
  30. package/dist/pull-3OQCgxUf.js +1 -0
  31. package/dist/{pull-CAeaAioC.js → pull-iVPQZsnI.js} +1 -1
  32. package/dist/{rls-CtRaRrn3.js → rls-BItB4vkC.js} +29 -14
  33. package/dist/{rls-BYHN5bcJ.js → rls-C8nxX0RV.js} +1 -1
  34. package/dist/run-BqW8S5TM.js +1 -0
  35. package/dist/{run-B5nyNKe8.js → run-CjFdAzqE.js} +2 -2
  36. package/dist/{scan-CvFWW_fU.js → scan-COIQvTMg.js} +1 -1
  37. package/dist/{schema-CQzIJdEd.js → schema-CeQZY6bO.js} +1 -1
  38. package/dist/{skills-Bar7ZqwM.js → skills-5XWpNzCi.js} +1 -1
  39. package/dist/{skills-BZc81VLt.js → skills-CfkYP7GG.js} +1 -1
  40. package/dist/{supautils-C-_jqylY.js → supautils-PRhfO-bL.js} +1 -1
  41. package/dist/usage-BDiTP763.js +1 -0
  42. package/dist/{usage-heV5KOiU.js → usage-BfzSfOpD.js} +1 -1
  43. package/dist/{usage-Y6xZUfE-.js → usage-hMYVFsN5.js} +2 -2
  44. package/dist/{verify-6EauWrkP.js → verify-CmTiQ-yM.js} +1 -1
  45. package/package.json +1 -1
  46. package/dist/powersync-6vgKevSi.js +0 -1
  47. package/dist/pull-DfJALUbS.js +0 -1
  48. package/dist/run-0vOholnN.js +0 -1
  49. package/dist/usage-CNca1i1W.js +0 -1
@@ -1,5 +1,5 @@
1
1
  import { r as listSourceFiles } from "./files-BSjrtIHw.js";
2
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
2
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { relative } from "node:path";
5
5
  //#region src/cli/adoption-scan.ts
@@ -3,7 +3,7 @@ import { c as missingInput } from "./errors-Vpu6rKHl.js";
3
3
  import { i as readJsonFile } from "./files-BSjrtIHw.js";
4
4
  import { t as ARAZZO_REPORT_SCHEMA } from "./version-DMQ0dnN5.js";
5
5
  import { a as loadModule, s as pickNamed, t as asPermissionTree } from "./load-DjSU5KXO.js";
6
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
6
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
7
7
  import { existsSync } from "node:fs";
8
8
  import { resolve } from "node:path";
9
9
  //#region src/cli/arazzo.ts
@@ -21,7 +21,7 @@ const check = defineLeaf({
21
21
  ...permissionsFlag
22
22
  },
23
23
  async run(ctx, { flags }) {
24
- return (await import("./arazzo-CTH5iN--.js")).runArazzo({
24
+ return (await import("./arazzo-BzVfXbhf.js")).runArazzo({
25
25
  cwd: ctx.cwd,
26
26
  config: ctx.config,
27
27
  doc: given(flags.doc),
@@ -414,6 +414,14 @@ type AuthorizationProviderOptions = {
414
414
  * stays unset and doctor BS409 has nothing to compare.
415
415
  */
416
416
  readonly activeTenant?: BetterSupabaseActiveTenant;
417
+ /**
418
+ * Fill better-supabase's `rowsWith` (and `rowsWithFor` when the `_for`
419
+ * helpers exist) from the `rls.rowHelpers` helpers the manifest names, so a
420
+ * `config.records.<type>.permissions.<action>` entry with `rows` checks the
421
+ * rows the caller holds a permission on. Needs better-supabase 0.22 or later,
422
+ * whose config takes those templates.
423
+ */
424
+ readonly rowHelpers?: boolean;
417
425
  };
418
426
  /**
419
427
  * better-supabase's `authorization` config value, built from PermDock's
@@ -928,6 +928,32 @@ function permittedTemplate(schema, scopes, tenantScope, catalog) {
928
928
  const otherwise = complete === void 0 ? atTenant : complete.length === 0 ? `false and ${atTenant}` : `{permission} in (${complete.join(", ")}) and ${atTenant}`;
929
929
  return `case ${branches.join(" ")} else ${otherwise} end`;
930
930
  }
931
+ /**
932
+ * better-supabase's `rowsWith` and `rowsWithFor` over the
933
+ * `permitted_<resource>_rows` helpers: `{resource}` is the resource's SQL name,
934
+ * `rowsWith` takes the candidate ids being checked, and `rowsWithFor` runs the
935
+ * helper's `_for` form, which `rls generate` writes beside it for Supabase.
936
+ */
937
+ function rowTemplatesOf(schema, manifest, requires, problems) {
938
+ const rows = Object.values(manifest.names?.resources ?? {}).flatMap((names) => names.rows === void 0 ? [] : [names.rows]);
939
+ if (rows.length === 0) {
940
+ problems.push("rowHelpers: the manifest names no permitted_<resource>_rows helper. Set rls.rowHelpers, run `permdock rls generate`, then `permdock supabase inspect --out`.");
941
+ return {};
942
+ }
943
+ for (const name of rows) requires.push({
944
+ function: `${schema}.${sqlIdent(name)}`,
945
+ args: "text, text[]",
946
+ role: "authenticated"
947
+ }, {
948
+ function: `${schema}.${sqlIdent(`${name}_for`)}`,
949
+ args: "uuid, text, jsonb",
950
+ role: "postgres"
951
+ });
952
+ return {
953
+ rowsWith: `${schema}.permitted_{resource}_rows({permission}, {ids})`,
954
+ rowsWithFor: `${schema}.permitted_{resource}_rows_for({user}, {permission})`
955
+ };
956
+ }
931
957
  const RISKS = {
932
958
  low: "low",
933
959
  medium: "medium",
@@ -1099,6 +1125,7 @@ function authorizationProvider(options) {
1099
1125
  }
1100
1126
  const permitted = listed("idsWith", tenantScope) ? permittedTemplate(schema, scopeNames.filter((scope) => listed("idsWith", scope)), tenantScope, catalog) : void 0;
1101
1127
  const functions = {
1128
+ ...options.rowHelpers === true ? rowTemplatesOf(schema, manifest, requires, problems) : {},
1102
1129
  ...optional,
1103
1130
  idsWith: `${schema}.${TEMPLATES.idsWith.sql}`,
1104
1131
  isPlatform: `${schema}.${TEMPLATES.isPlatform.sql}`,
@@ -1,4 +1,4 @@
1
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
1
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
2
2
  import { t as applyFiles } from "./files-B3AA05f1.js";
3
3
  import { n as isIdentifier, t as emitPolicyModule } from "./policy-module-xym9fnSq.js";
4
4
  import { existsSync, readFileSync } from "node:fs";
@@ -16,7 +16,7 @@ const casbinImport = defineLeaf({
16
16
  ...outFlag
17
17
  },
18
18
  async run(ctx, { flags }) {
19
- const { runCasbinImport } = await import("./casbin-DTi6HSsl.js");
19
+ const { runCasbinImport } = await import("./casbin-BunKq48W.js");
20
20
  return runCasbinImport({
21
21
  cwd: ctx.cwd,
22
22
  model: given(flags.model),
package/dist/cli/bin.js CHANGED
@@ -11,7 +11,7 @@ if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
11
11
  const [{ isCi }, { trapSignals }, { run }, { streamHasColors }] = await Promise.all([
12
12
  import("../env-DJZrPjRi.js"),
13
13
  import("../signals-B1vHR3fN.js"),
14
- import("../run-0vOholnN.js"),
14
+ import("../run-BqW8S5TM.js"),
15
15
  import("../style-DblOtSga.js")
16
16
  ]);
17
17
  const signals = trapSignals();
package/dist/cli/index.js CHANGED
@@ -2,7 +2,7 @@ import { t as catalogPath } from "../catalog-path-CnI0XTFK.js";
2
2
  import { t as defineConfig } from "../config-BR23fL8m.js";
3
3
  import { n as parsePushedPolicy, r as pushedPolicy, t as PUSHED_POLICY_SCHEMA } from "../pushed-policy-CDuOL2de.js";
4
4
  import { c as parseGrantsMarker, l as parseHookMarker } from "../markers-BeHZ4x9Q.js";
5
- import { t as run } from "../run-B5nyNKe8.js";
5
+ import { t as run } from "../run-CjFdAzqE.js";
6
6
  //#region src/cli/token-schemas.ts
7
7
  const ID = {
8
8
  type: "string",
@@ -70,7 +70,7 @@ const pull = defineLeaf({
70
70
  },
71
71
  async run(ctx, { flags }) {
72
72
  const env = await withStdinSecret(ctx, "token", flags["token-stdin"], "PERMDOCK_CLOUD_TOKEN");
73
- return (await import("./pull-DfJALUbS.js")).runCloudPull({
73
+ return (await import("./pull-3OQCgxUf.js")).runCloudPull({
74
74
  cwd: ctx.cwd,
75
75
  config: ctx.config,
76
76
  url: given(flags.url),
@@ -7,7 +7,7 @@ const codemod = defineLeaf({
7
7
  description: "The release to migrate to (0.5)",
8
8
  required: true,
9
9
  async complete() {
10
- return (await import("./codemod-C1U8_Zu3.js")).codemodVersions();
10
+ return (await import("./codemod-CpMCbp0G.js")).codemodVersions();
11
11
  }
12
12
  }, {
13
13
  name: "paths",
@@ -22,7 +22,7 @@ const codemod = defineLeaf({
22
22
  }
23
23
  },
24
24
  async run(ctx, { flags, args, rest }) {
25
- return (await import("./codemod-C1U8_Zu3.js")).runCodemod({
25
+ return (await import("./codemod-CpMCbp0G.js")).runCodemod({
26
26
  cwd: ctx.cwd,
27
27
  version: args["version"],
28
28
  paths: rest,
@@ -1,7 +1,7 @@
1
1
  import { n as CliError } from "./errors-Vpu6rKHl.js";
2
2
  import { a as rel } from "./files-BSjrtIHw.js";
3
3
  import { t as beforeFirst } from "./text-BMFRfBqc.js";
4
- import { n as ROOT } from "./manifest-Cav1rjNQ.js";
4
+ import { n as ROOT } from "./manifest-Cl6yHiVK.js";
5
5
  import { i as renameMessage, n as DEFAULT_CHANGES, r as flagRename, t as COMMAND_MOVES } from "./renames-CGj7YwcQ.js";
6
6
  import { i as refuseDirty, t as applyFiles } from "./files-B3AA05f1.js";
7
7
  import { readFileSync, readdirSync, statSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import { n as CliError } from "./errors-Vpu6rKHl.js";
2
2
  import { a as suggestion, o as GLOBAL_FLAGS } from "./args-0FrVs5EH.js";
3
- import { a as resolve } from "./manifest-Cav1rjNQ.js";
3
+ import { a as resolve } from "./manifest-Cl6yHiVK.js";
4
4
  import { t as defineLeaf } from "./command-L8EstFNm.js";
5
5
  //#region src/cli/core/completion.ts
6
6
  const SHELLS = [
@@ -10,7 +10,7 @@ const config = defineLeaf({
10
10
  ...failOnFlag
11
11
  },
12
12
  async run(ctx, { flags }) {
13
- const { runConfig } = await import("./config-report-5AwptLr2.js");
13
+ const { runConfig } = await import("./config-report-C1-r59VY.js");
14
14
  return runConfig({
15
15
  cwd: ctx.cwd,
16
16
  config: ctx.config,
@@ -3,7 +3,7 @@ import { r as CONFIG_REPORT_SCHEMA } from "./version-DMQ0dnN5.js";
3
3
  import { t as catalogPath } from "./catalog-path-CnI0XTFK.js";
4
4
  import { n as guessPermissions, t as barrelFile } from "./collect-WCl8cJQV.js";
5
5
  import "./sources-C4vkzZJV.js";
6
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
6
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
7
7
  import { n as MIGRATION_DIRS, t as DEFAULT_SENSITIVE_ACTIONS } from "./constants-DcJABbLW.js";
8
8
  //#region src/cli/config-report.ts
9
9
  function configReport(input) {
@@ -24,10 +24,10 @@ import { t as parseCatalog } from "./parse-1_ZheTW5.js";
24
24
  import { n as activeTenantMismatch } from "./active-tenant-DVee32-T.js";
25
25
  import "./markers-BeHZ4x9Q.js";
26
26
  import { t as didYouMean } from "./args-0FrVs5EH.js";
27
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
27
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
28
28
  import { t as createStyle } from "./style-Ds11Hmk4.js";
29
- import { a as nonEmptyOr, r as findingOf } from "./output-CtyBwTNB.js";
30
- import { i as runSkillsInstall } from "./skills-Bar7ZqwM.js";
29
+ import { a as nonEmptyOr, r as findingOf } from "./output-DpCUAST3.js";
30
+ import { i as runSkillsInstall } from "./skills-5XWpNzCi.js";
31
31
  import { n as MIGRATION_DIRS, t as DEFAULT_SENSITIVE_ACTIONS } from "./constants-DcJABbLW.js";
32
32
  import { l as partPath } from "./output-files-BIQ_9pIs.js";
33
33
  import { _ as readSupabaseConfig, a as supabaseHookManifest, c as activeSource, d as resolveSchemas, g as pd074, h as pd045, l as pd039, m as attrsPlan, n as hookOut, v as supabaseConfig } from "./supabase-hook-CyuD9Wau.js";
@@ -37,10 +37,10 @@ import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
37
37
  import { i as group, n as membershipColumns, o as sqlStatements, r as tableKey } from "./deciding-columns-NB4wcyBf.js";
38
38
  import { t as FIELD_VIEWS } from "./fields-DI8s3cPl.js";
39
39
  import { t as resolveNames } from "./resolve-names-Y1Cb22cp.js";
40
- import { a as pd010, c as pd014, d as pd038, f as pd041, i as pd008, l as pd015, n as pd001, o as pd011, p as pd057, r as pd007, s as pd013, t as runUsage, u as pd036 } from "./usage-Y6xZUfE-.js";
41
- import { d as rowConditionMessage, f as commandFor, m as tableFor, p as compileGrants, s as ROW_CONDITION_FIX, u as helperTablePolicies } from "./verify-6EauWrkP.js";
42
- import { a as streamOf, i as scopeSourcesOf, r as staleFiles, t as powersyncFiles } from "./powersync-CD8-Ypao.js";
43
- import { a as buildContext, n as generateOptions, r as isRefusal, s as inheritTargets, t as supautilsRejections } from "./supautils-C-_jqylY.js";
40
+ import { a as pd010, c as pd014, d as pd038, f as pd041, i as pd008, l as pd015, n as pd001, o as pd011, p as pd057, r as pd007, s as pd013, t as runUsage, u as pd036 } from "./usage-hMYVFsN5.js";
41
+ import { d as rowConditionMessage, f as commandFor, m as tableFor, p as compileGrants, s as ROW_CONDITION_FIX, u as helperTablePolicies } from "./verify-CmTiQ-yM.js";
42
+ import { a as streamOf, i as scopeSourcesOf, r as staleFiles, t as powersyncFiles } from "./powersync-BTLk8GSl.js";
43
+ import { a as buildContext, n as generateOptions, r as isRefusal, s as inheritTargets, t as supautilsRejections } from "./supautils-PRhfO-bL.js";
44
44
  import { n as legacyHelperNames, r as retirePlan, t as legacyHelperCalls } from "./retire-B2xffQgY.js";
45
45
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
46
46
  import { basename, dirname, join, resolve } from "node:path";
@@ -10,7 +10,7 @@ const doctor = defineLeaf({
10
10
  description: "Check codes or groups to run",
11
11
  valueHint: "code",
12
12
  async complete() {
13
- const { DOCTOR_CHECKS } = await import("./doctor-ktitU5Dl.js");
13
+ const { DOCTOR_CHECKS } = await import("./doctor-CDfzrMQB.js");
14
14
  return [.../* @__PURE__ */ new Set([...DOCTOR_CHECKS.map(({ code }) => code), ...DOCTOR_CHECKS.flatMap(({ groups }) => groups)])];
15
15
  }
16
16
  },
@@ -27,7 +27,7 @@ const doctor = defineLeaf({
27
27
  if (answer === void 0) return CANCELLED;
28
28
  fix = answer;
29
29
  }
30
- return (await import("./doctor-ktitU5Dl.js")).runDoctor({
30
+ return (await import("./doctor-CDfzrMQB.js")).runDoctor({
31
31
  cwd: ctx.cwd,
32
32
  config: ctx.config,
33
33
  only: flags.only,
@@ -4,7 +4,7 @@ import { i as readJsonFile } from "./files-BSjrtIHw.js";
4
4
  import { t as applyFiles } from "./files-B3AA05f1.js";
5
5
  import { n as httpRequest } from "./http-DOL33cO0.js";
6
6
  import { i as schemaExpression, t as emitPermissionsModule } from "./generate-CezbCaTI.js";
7
- import { t as validateOpenapi } from "./schema-CQzIJdEd.js";
7
+ import { t as validateOpenapi } from "./schema-CeQZY6bO.js";
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  import { resolve } from "node:path";
10
10
  //#region ../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/browser/dist/nodes/identity.js
@@ -1,6 +1,6 @@
1
1
  import { n as CliError } from "./errors-Vpu6rKHl.js";
2
2
  import { a as decodePreset, g as resolvePreset, i as presetFlags, n as generatePreset, o as encodePreset } from "./preset-BjLzeFpp.js";
3
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
3
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
4
4
  import { t as addLine } from "./peer-Q0fuZ6hx.js";
5
5
  import { t as applyFiles } from "./files-B3AA05f1.js";
6
6
  import { existsSync } from "node:fs";
@@ -48,7 +48,7 @@ const init = defineLeaf({
48
48
  ...forceFlag
49
49
  },
50
50
  async run(ctx, { flags }) {
51
- const [{ runInit }, { packageManagerOf }] = await Promise.all([import("./init-DFwylmiN.js"), import("./peer-C9LvBCcb.js")]);
51
+ const [{ runInit }, { packageManagerOf }] = await Promise.all([import("./init-BuUt4bLJ.js"), import("./peer-C9LvBCcb.js")]);
52
52
  const agent = await packageManagerOf(ctx.cwd);
53
53
  const selection = {
54
54
  archetype: flags.archetype,
@@ -79,7 +79,7 @@ const presetDecode = defineLeaf({
79
79
  positionals: presetCode,
80
80
  flags: {},
81
81
  async run(_ctx, { args }) {
82
- const { runPreset } = await import("./init-DFwylmiN.js");
82
+ const { runPreset } = await import("./init-BuUt4bLJ.js");
83
83
  return runPreset("decode", args["code"] ?? "");
84
84
  }
85
85
  });
@@ -87,7 +87,7 @@ const presetUrl = defineLeaf({
87
87
  positionals: presetCode,
88
88
  flags: {},
89
89
  async run(_ctx, { args }) {
90
- const { runPreset } = await import("./init-DFwylmiN.js");
90
+ const { runPreset } = await import("./init-BuUt4bLJ.js");
91
91
  return runPreset("url", args["code"] ?? "");
92
92
  }
93
93
  });
@@ -27,7 +27,7 @@ const login = defineLeaf({
27
27
  }
28
28
  },
29
29
  async run(ctx, { flags }) {
30
- const { runLogin } = await import("./login-B92-dZMK.js");
30
+ const { runLogin } = await import("./login-YsrgkAQo.js");
31
31
  return runLogin({
32
32
  cwd: ctx.cwd,
33
33
  url: given(flags.url),
@@ -43,7 +43,7 @@ const login = defineLeaf({
43
43
  const logout = defineLeaf({
44
44
  flags: { ...urlFlag },
45
45
  async run(ctx, { flags }) {
46
- const { runLogout } = await import("./login-B92-dZMK.js");
46
+ const { runLogout } = await import("./login-YsrgkAQo.js");
47
47
  return runLogout({
48
48
  url: given(flags.url),
49
49
  json: ctx.json,
@@ -55,7 +55,7 @@ const logout = defineLeaf({
55
55
  const whoami = defineLeaf({
56
56
  flags: { ...urlFlag },
57
57
  async run(ctx, { flags }) {
58
- const { runWhoami } = await import("./login-B92-dZMK.js");
58
+ const { runWhoami } = await import("./login-YsrgkAQo.js");
59
59
  return runWhoami({
60
60
  url: given(flags.url),
61
61
  json: ctx.json,
@@ -3,7 +3,7 @@ import { n as CliError } from "./errors-Vpu6rKHl.js";
3
3
  import { i as postForm, r as parseToken, s as runDeviceFlow } from "./device-BwbJNmb2.js";
4
4
  import { i as writeCredentials, n as deleteCredentials, r as readCredentials, t as credentialsPath } from "./storage-C2bWdvJH.js";
5
5
  import { n as randomToken, t as pkce } from "./pkce-CnmmYqUZ.js";
6
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
6
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
7
7
  import { n as httpRequest } from "./http-DOL33cO0.js";
8
8
  import { c as READ_SCOPES, d as issuerMetadata, i as credentialsInsideRepository, l as cloudUrl, n as cliRuntime, r as credentialStorage, s as CLI_CLIENT_ID, t as CREDENTIAL_SERVICE } from "./auth--yPyZNgr.js";
9
9
  import { spawn } from "node:child_process";
@@ -30,20 +30,20 @@ const SECTIONS = [
30
30
  docs: "init",
31
31
  effects: ["files"],
32
32
  config: false,
33
- load: async () => (await import("./init-CY7THM2v.js")).init
33
+ load: async () => (await import("./init-n1gkKHvc.js")).init
34
34
  }),
35
35
  group("preset", "Decode a preset code, or print its permdock.com/create link", "preset", [leaf("decode", "Print what a preset code holds", {
36
36
  examples: ["permdock preset decode p1AQIDBAc"],
37
37
  docs: "preset",
38
38
  effects: [],
39
39
  config: false,
40
- load: async () => (await import("./init-CY7THM2v.js")).presetDecode
40
+ load: async () => (await import("./init-n1gkKHvc.js")).presetDecode
41
41
  }), leaf("url", "Print the permdock.com/create link that opens a code", {
42
42
  examples: ["permdock preset url p1AQIDBAc"],
43
43
  docs: "preset",
44
44
  effects: [],
45
45
  config: false,
46
- load: async () => (await import("./init-CY7THM2v.js")).presetUrl
46
+ load: async () => (await import("./init-n1gkKHvc.js")).presetUrl
47
47
  })]),
48
48
  group("skills", "Install, update or list the PermDock Agent Skills", "skills", [
49
49
  leaf("install", "Copy the Agent Skills into the agent folders", {
@@ -55,21 +55,21 @@ const SECTIONS = [
55
55
  docs: "skills",
56
56
  effects: ["files"],
57
57
  config: false,
58
- load: async () => (await import("./skills-BZc81VLt.js")).install
58
+ load: async () => (await import("./skills-CfkYP7GG.js")).install
59
59
  }),
60
60
  leaf("update", "Replace installed skills with this version's", {
61
61
  examples: ["permdock skills update"],
62
62
  docs: "skills",
63
63
  effects: ["files"],
64
64
  config: false,
65
- load: async () => (await import("./skills-BZc81VLt.js")).update
65
+ load: async () => (await import("./skills-CfkYP7GG.js")).update
66
66
  }),
67
67
  leaf("list", "List the skills and where each is installed", {
68
68
  examples: ["permdock skills list"],
69
69
  docs: "skills",
70
70
  effects: [],
71
71
  config: false,
72
- load: async () => (await import("./skills-BZc81VLt.js")).list
72
+ load: async () => (await import("./skills-CfkYP7GG.js")).list
73
73
  })
74
74
  ]),
75
75
  leaf("doctor", "Check a PermDock installation and print a fix for each finding", {
@@ -82,7 +82,7 @@ const SECTIONS = [
82
82
  effects: ["files"],
83
83
  config: true,
84
84
  findings: true,
85
- load: async () => (await import("./doctor-DxyVeUEE.js")).doctor
85
+ load: async () => (await import("./doctor-Dea6IqRw.js")).doctor
86
86
  }),
87
87
  leaf("completion", "Print the shell completion script for bash, zsh or fish", {
88
88
  examples: ["permdock completion zsh > ~/.zfunc/_permdock", "eval \"$(permdock completion bash)\""],
@@ -90,7 +90,7 @@ const SECTIONS = [
90
90
  effects: [],
91
91
  config: false,
92
92
  shellOutput: true,
93
- load: async () => (await import("./completion-DLTuCf53.js")).completion
93
+ load: async () => (await import("./completion-Cq4fbT2S.js")).completion
94
94
  })
95
95
  ]
96
96
  },
@@ -132,7 +132,7 @@ const SECTIONS = [
132
132
  effects: [],
133
133
  config: true,
134
134
  findings: true,
135
- load: async () => (await import("./usage-heV5KOiU.js")).usage
135
+ load: async () => (await import("./usage-BfzSfOpD.js")).usage
136
136
  }),
137
137
  leaf("diff", "Compare two policies or catalogs; exit 1 on a breaking change", {
138
138
  examples: ["permdock diff main.catalog.json permissions.catalog.json", "permdock diff ./old-policy.ts ./src/policy.ts --impact"],
@@ -147,7 +147,7 @@ const SECTIONS = [
147
147
  effects: [],
148
148
  config: true,
149
149
  findings: true,
150
- load: async () => (await import("./config-CmydKZr2.js")).config
150
+ load: async () => (await import("./config-0xxCmoD8.js")).config
151
151
  })
152
152
  ]
153
153
  },
@@ -164,14 +164,14 @@ const SECTIONS = [
164
164
  "network"
165
165
  ],
166
166
  config: true,
167
- load: async () => (await import("./rls-BYHN5bcJ.js")).generate
167
+ load: async () => (await import("./rls-C8nxX0RV.js")).generate
168
168
  }),
169
169
  leaf("import", "Turn existing RLS policies into a generated definition", {
170
170
  examples: ["permdock rls import --sql schema.sql --out src/permissions.generated.ts", "permdock rls import --db-url-stdin --out src/permissions.generated.ts"],
171
171
  docs: "rls",
172
172
  effects: ["files", "database:read"],
173
173
  config: true,
174
- load: async () => (await import("./rls-BYHN5bcJ.js")).importPolicies
174
+ load: async () => (await import("./rls-C8nxX0RV.js")).importPolicies
175
175
  }),
176
176
  leaf("verify", "Run the fixtures against the policies, or compare a live database", {
177
177
  examples: [
@@ -183,21 +183,21 @@ const SECTIONS = [
183
183
  effects: ["files", "database:read"],
184
184
  config: true,
185
185
  findings: true,
186
- load: async () => (await import("./rls-BYHN5bcJ.js")).verify
186
+ load: async () => (await import("./rls-C8nxX0RV.js")).verify
187
187
  }),
188
188
  leaf("migrate", "Rewrite hand-written policies onto the generated helpers", {
189
189
  examples: ["permdock rls migrate --sql supabase/migrations --dry-run", "permdock rls migrate --sql supabase/migrations"],
190
190
  docs: "rls",
191
191
  effects: ["rewrite", "files"],
192
192
  config: true,
193
- load: async () => (await import("./rls-BYHN5bcJ.js")).migrate
193
+ load: async () => (await import("./rls-C8nxX0RV.js")).migrate
194
194
  }),
195
195
  leaf("explain", "Show the tables, membership sources and triggers the SQL binds to", {
196
196
  examples: ["permdock rls explain", "permdock rls explain --db-url-stdin --json"],
197
197
  docs: "rls",
198
198
  effects: ["database:read"],
199
199
  config: true,
200
- load: async () => (await import("./rls-BYHN5bcJ.js")).explain
200
+ load: async () => (await import("./rls-C8nxX0RV.js")).explain
201
201
  })
202
202
  ]),
203
203
  group("supabase", "Generate the Supabase Custom Access Token Hook, or inspect its manifest", "supabase", [group("hook", "The Custom Access Token Hook", "supabase", [leaf("generate", "Compile the membership sources into the hook migration", {
@@ -218,13 +218,13 @@ const SECTIONS = [
218
218
  docs: "powersync",
219
219
  effects: ["files"],
220
220
  config: true,
221
- load: async () => (await import("./powersync-CBcsEvdr.js")).generate
221
+ load: async () => (await import("./powersync-Bm3ZjHwD.js")).generate
222
222
  }), leaf("verify", "Fail on stale streams or a stream that syncs a denied row", {
223
223
  examples: ["permdock powersync verify", "permdock powersync verify --db-url-stdin"],
224
224
  docs: "powersync",
225
225
  effects: ["database:read"],
226
226
  config: true,
227
- load: async () => (await import("./powersync-CBcsEvdr.js")).verify
227
+ load: async () => (await import("./powersync-Bm3ZjHwD.js")).verify
228
228
  })]),
229
229
  group("scim", "Generate the Postgres tables postgresDirectoryStore keeps a SCIM directory in", "scim", [leaf("generate", "Write the tenant-keyed users, groups and members tables", {
230
230
  examples: [
@@ -247,13 +247,13 @@ const SECTIONS = [
247
247
  docs: "openapi",
248
248
  effects: ["files"],
249
249
  config: true,
250
- load: async () => (await import("./openapi-gMQQSzNn.js")).emit
250
+ load: async () => (await import("./openapi-BFyBIe9J.js")).emit
251
251
  }), leaf("import", "Turn an OpenAPI document into a generated definition", {
252
252
  examples: ["permdock openapi import --doc openapi.yaml --out src/permissions.generated.ts"],
253
253
  docs: "openapi",
254
254
  effects: ["files", "network"],
255
255
  config: false,
256
- load: async () => (await import("./openapi-gMQQSzNn.js")).importDocument
256
+ load: async () => (await import("./openapi-BFyBIe9J.js")).importDocument
257
257
  })]),
258
258
  group("arazzo", "Check Arazzo workflows against the permissions", "arazzo", [leaf("check", "Resolve every Arazzo step to x-permdock-permissions", {
259
259
  examples: ["permdock arazzo check --doc arazzo.json --openapi openapi.json"],
@@ -261,7 +261,7 @@ const SECTIONS = [
261
261
  effects: [],
262
262
  config: true,
263
263
  findings: true,
264
- load: async () => (await import("./arazzo-BXaEhGHM.js")).check
264
+ load: async () => (await import("./arazzo-CBBIhC40.js")).check
265
265
  })]),
266
266
  leaf("scan", "List authorization checks written without PermDock, to adopt it in an existing app", {
267
267
  examples: ["permdock scan", "permdock scan --src app,lib --pattern isBeta"],
@@ -269,7 +269,7 @@ const SECTIONS = [
269
269
  effects: [],
270
270
  config: true,
271
271
  findings: true,
272
- load: async () => (await import("./scan-CvFWW_fU.js")).scan
272
+ load: async () => (await import("./scan-COIQvTMg.js")).scan
273
273
  }),
274
274
  group("casl", "Import CASL rules into a generated module", "casl", [leaf("import", "Turn CASL rules per role into a permissions and policy module", {
275
275
  examples: ["permdock casl import --rules rules.json --out src/permissions.generated.ts"],
@@ -283,7 +283,7 @@ const SECTIONS = [
283
283
  docs: "casbin",
284
284
  effects: ["files"],
285
285
  config: false,
286
- load: async () => (await import("./casbin-BuTZnSPv.js")).casbinImport
286
+ load: async () => (await import("./casbin-C8r5trwy.js")).casbinImport
287
287
  })])
288
288
  ]
289
289
  },
@@ -295,34 +295,34 @@ const SECTIONS = [
295
295
  docs: "login",
296
296
  effects: ["network", "files"],
297
297
  config: false,
298
- load: async () => (await import("./login-C5XLYZSF.js")).login
298
+ load: async () => (await import("./login-DyQNWJok.js")).login
299
299
  }),
300
300
  leaf("logout", "Revoke and delete the stored PermDock Cloud login", {
301
301
  examples: ["permdock logout"],
302
302
  docs: "login",
303
303
  effects: ["network", "files"],
304
304
  config: false,
305
- load: async () => (await import("./login-C5XLYZSF.js")).logout
305
+ load: async () => (await import("./login-DyQNWJok.js")).logout
306
306
  }),
307
307
  leaf("whoami", "Print the PermDock Cloud identity the CLI acts as", {
308
308
  examples: ["permdock whoami", "permdock whoami --json"],
309
309
  docs: "login",
310
310
  effects: [],
311
311
  config: false,
312
- load: async () => (await import("./login-C5XLYZSF.js")).whoami
312
+ load: async () => (await import("./login-DyQNWJok.js")).whoami
313
313
  }),
314
314
  group("cloud", "Publish to or pull from a PermDock Cloud project", "cloud", [leaf("push", "Publish the catalog, policy and manifest to a Cloud project", {
315
315
  examples: ["permdock cloud push --dry-run", "permdock cloud push --project prj_123"],
316
316
  docs: "cloud",
317
317
  effects: ["network", "cloud:write"],
318
318
  config: true,
319
- load: async () => (await import("./cloud-DiAiYFRo.js")).push
319
+ load: async () => (await import("./cloud-C_Mau1q2.js")).push
320
320
  }), leaf("pull", "Write a Cloud project's published catalog and manifest", {
321
321
  examples: ["permdock cloud pull --project prj_123"],
322
322
  docs: "cloud",
323
323
  effects: ["network", "files"],
324
324
  config: true,
325
- load: async () => (await import("./cloud-DiAiYFRo.js")).pull
325
+ load: async () => (await import("./cloud-C_Mau1q2.js")).pull
326
326
  })])
327
327
  ]
328
328
  },
@@ -333,7 +333,7 @@ const SECTIONS = [
333
333
  docs: "codemod",
334
334
  effects: ["rewrite"],
335
335
  config: false,
336
- load: async () => (await import("./codemod-_q_ZI01S.js")).codemod
336
+ load: async () => (await import("./codemod-BqYPToUJ.js")).codemod
337
337
  })]
338
338
  }
339
339
  ];
@@ -345,7 +345,7 @@ const COMPLETE_LEAF = leaf("__complete", "Print completion candidates for a part
345
345
  config: false,
346
346
  shellOutput: true,
347
347
  hidden: true,
348
- load: async () => (await import("./completion-DLTuCf53.js")).complete
348
+ load: async () => (await import("./completion-Cq4fbT2S.js")).complete
349
349
  });
350
350
  const ROOT = group("permdock", "Collect, export, diff and check the permissions a PermDock policy declares", "", [...SECTIONS.flatMap((section) => section.entries), COMPLETE_LEAF]);
351
351
  /** Each leaf with its path from the root, in manifest order. */
@@ -80,7 +80,7 @@ const emit = defineLeaf({
80
80
  }
81
81
  },
82
82
  async run(ctx, { flags }) {
83
- return (await import("./openapi-CuePQL4z.js")).runOpenapi({
83
+ return (await import("./openapi-D8JQtO8k.js")).runOpenapi({
84
84
  cwd: ctx.cwd,
85
85
  config: ctx.config,
86
86
  rest: ["emit"],
@@ -122,7 +122,7 @@ const importDocument = defineLeaf({
122
122
  async run(ctx, { flags }) {
123
123
  const doc = given(flags.doc);
124
124
  if (doc === void 0) throw missingInput("openapi import", ["--doc"], "the OpenAPI document to import");
125
- return (await import("./import-t1yqnCyG.js")).runOpenapiImport({
125
+ return (await import("./import-PP7uH8rC.js")).runOpenapiImport({
126
126
  cwd: ctx.cwd,
127
127
  doc,
128
128
  out: fileOut("openapi import", flags.out),
@@ -7,7 +7,7 @@ import { a as loadModule, n as asPolicy, s as pickNamed, t as asPermissionTree }
7
7
  import { t as shortDiff } from "./text-diff-BtfSmdUo.js";
8
8
  import { c as openapiVersion, o as createPermDock } from "./openapi-BOsx0xn9.js";
9
9
  import { i as fileDrift } from "./output-files-BIQ_9pIs.js";
10
- import { n as validateOverlay, t as validateOpenapi } from "./schema-CQzIJdEd.js";
10
+ import { n as validateOverlay, t as validateOpenapi } from "./schema-CeQZY6bO.js";
11
11
  import { existsSync } from "node:fs";
12
12
  import { resolve } from "node:path";
13
13
  //#region src/cli/openapi/openapi.ts
@@ -1,5 +1,5 @@
1
1
  import { s as cliVersion } from "./version-DMQ0dnN5.js";
2
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
2
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
3
3
  //#region src/cli/core/output.ts
4
4
  const CLI_RESULT_SCHEMA = "https://permdock.com/schemas/cli-result-v1.json";
5
5
  function envelope(input) {
@@ -19,7 +19,7 @@ import { r as tableNames } from "./tables-DimBcVWY.js";
19
19
  import { d as andConditions, l as collectGrants, o as assignableRoles, p as contextRefs, t as ownershipRules, u as roleNames } from "./ownership-qqBuGJXr.js";
20
20
  import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
21
21
  import { n as pgConnector } from "./pg-DQTqRrou.js";
22
- import { m as tableFor, t as canFixture } from "./verify-6EauWrkP.js";
22
+ import { m as tableFor, t as canFixture } from "./verify-CmTiQ-yM.js";
23
23
  import { resolve } from "node:path";
24
24
  //#region src/cli/powersync/names.ts
25
25
  function ident(name) {
@@ -14,7 +14,7 @@ const generate = defineLeaf({
14
14
  async run(ctx, { flags }) {
15
15
  const out = fileOut("powersync", flags.out);
16
16
  const from = given(flags.policy);
17
- const { runPowerSync } = await import("./powersync-6vgKevSi.js");
17
+ const { runPowerSync } = await import("./powersync-CObULVRk.js");
18
18
  return runPowerSync({
19
19
  cwd: ctx.cwd,
20
20
  config: ctx.config,
@@ -45,7 +45,7 @@ const verify = defineLeaf({
45
45
  const db = await dbUrl(ctx, flags, { required: true });
46
46
  const fixtures = given(flags.fixtures);
47
47
  const from = given(flags.policy);
48
- const { runPowerSync } = await import("./powersync-6vgKevSi.js");
48
+ const { runPowerSync } = await import("./powersync-CObULVRk.js");
49
49
  return runPowerSync({
50
50
  cwd: ctx.cwd,
51
51
  config: ctx.config,
@@ -0,0 +1 @@
1
+ export { n as runPowerSync } from "./powersync-BTLk8GSl.js";
@@ -0,0 +1 @@
1
+ export { n as runCloudPull } from "./pull-iVPQZsnI.js";
@@ -7,7 +7,7 @@ import { o as formatCatalogJson } from "./catalog-doc-DjMKzJp9.js";
7
7
  import { t as catalogPath } from "./catalog-path-CnI0XTFK.js";
8
8
  import { n as parseSupabaseManifest } from "./manifest-BXTbHNIV.js";
9
9
  import { t as parseCatalog } from "./parse-1_ZheTW5.js";
10
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
10
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
11
11
  import { t as POLICY_NOT_PUBLISHED } from "./problem-types-DSsRfONQ.js";
12
12
  import { n as httpRequest, t as authFailure } from "./http-DOL33cO0.js";
13
13
  import { l as cloudUrl, n as cliRuntime, o as readAuth } from "./auth--yPyZNgr.js";
@@ -11,10 +11,10 @@ import "./sources-C4vkzZJV.js";
11
11
  import { t as policyFromCatalog } from "./policy-from-catalog-CU3An8uP.js";
12
12
  import { t as beforeFirst } from "./text-BMFRfBqc.js";
13
13
  import { i as INDEXES_MARKER, n as GRANTS_MARKER, o as SEEDS_MARKER, t as BACKFILL_MARKER } from "./markers-BeHZ4x9Q.js";
14
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
14
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
15
15
  import { i as requirePeer, n as packageManagerOf, t as addLine } from "./peer-Q0fuZ6hx.js";
16
16
  import { t as POLICY_NOT_PUBLISHED } from "./problem-types-DSsRfONQ.js";
17
- import { t as fetchProjectPolicy } from "./pull-CAeaAioC.js";
17
+ import { t as fetchProjectPolicy } from "./pull-iVPQZsnI.js";
18
18
  import { n as MIGRATION_DIRS } from "./constants-DcJABbLW.js";
19
19
  import { a as isSeedsDirectory, c as parseSplit, d as seedsMigration, f as writeOutputFiles, l as partPath, n as dataMigrationFile, r as driftOf, u as pgDeltaPath } from "./output-files-BIQ_9pIs.js";
20
20
  import { b as rbacScaffold, d as resolveSchemas, f as schemasRefusal, o as supabaseHookSql, p as declarativeSql, s as approvalsSql, t as grantsLabel, u as isSupabaseSchemas, y as parseRbacAuthorize } from "./supabase-hook-CyuD9Wau.js";
@@ -25,8 +25,8 @@ import { a as splitUndiffed } from "./deciding-columns-NB4wcyBf.js";
25
25
  import { t as connectPgPool } from "./pg-DQTqRrou.js";
26
26
  import { a as fieldViewsSql, c as ACTOR_MAY_WRITE, f as readOnlyActorKinds, i as fieldViews, l as actorMayWriteSql, n as columnGrantSql, o as rowBranches, p as readOnlyActorPolicies, r as columnRevokeSql, u as assemblePolicies } from "./fields-DI8s3cPl.js";
27
27
  import { n as permissionHoldersSql } from "./resolve-names-Y1Cb22cp.js";
28
- import { _ as indexTargets, a as verifyResult, b as readTableIndexFacts, c as callsHelper, g as INDEX_COLUMNS_SQL, h as COLUMNS_SQL, i as verifyIgnores, l as helperCallKeys, m as tableFor, n as runRlsVerify, o as HELPER_TABLES, p as compileGrants, r as rlsFinding, v as indexesSql, y as pruneIndexTargets } from "./verify-6EauWrkP.js";
29
- import { a as buildContext, c as levelKeys, d as rebuildPlan, i as splitsPart, l as rowHelpersSql, n as generateOptions, o as CANDIDATES, r as isRefusal, s as inheritTargets, t as supautilsRejections, u as DROPPED_LIST } from "./supautils-C-_jqylY.js";
28
+ import { _ as indexTargets, a as verifyResult, b as readTableIndexFacts, c as callsHelper, g as INDEX_COLUMNS_SQL, h as COLUMNS_SQL, i as verifyIgnores, l as helperCallKeys, m as tableFor, n as runRlsVerify, o as HELPER_TABLES, p as compileGrants, r as rlsFinding, v as indexesSql, y as pruneIndexTargets } from "./verify-CmTiQ-yM.js";
29
+ import { a as buildContext, c as levelKeys, d as rebuildPlan, i as splitsPart, l as rowHelpersSql, n as generateOptions, o as CANDIDATES, r as isRefusal, s as inheritTargets, t as supautilsRejections, u as DROPPED_LIST } from "./supautils-PRhfO-bL.js";
30
30
  import { readFileSync } from "node:fs";
31
31
  import { basename, delimiter, join } from "node:path";
32
32
  import { spawn } from "node:child_process";
@@ -1027,11 +1027,13 @@ function realtimeStoragePolicies(ctx, facts, config, warnings = []) {
1027
1027
  }
1028
1028
  //#endregion
1029
1029
  //#region src/cli/rls/generate-policies.ts
1030
+ const ACTOR_GUARDS_HEADER = "-- rls.readOnlyActors with --helpers-only: restrictive policies on the write commands, which narrow the hand-written policies and grant nothing. A table that does not exist yet is skipped.";
1030
1031
  const SERVICE_TABLES_HEADER = "-- Supabase Realtime and Storage: rls.realtime and rls.storage. RLS on both tables is Supabase's.";
1031
1032
  /**
1032
- * A `realtime.messages` or `storage.objects` policy that applies only where the
1033
- * table exists. The Realtime and Storage services create those tables, so a
1034
- * local stack with the service off, and pg-delta's shadow database, have none.
1033
+ * A policy that applies only where its table exists. The Realtime and Storage
1034
+ * services create `realtime.messages` and `storage.objects`, so a local stack
1035
+ * with the service off, and pg-delta's shadow database, have none; under
1036
+ * `--helpers-only` the app's own migrations create its tables.
1035
1037
  */
1036
1038
  function guardedPolicySql(item) {
1037
1039
  const body = policySql(item).trimEnd().split("\n").map((line) => ` ${line}`).join("\n");
@@ -1053,6 +1055,18 @@ function withHelperTables(sql, helperTables) {
1053
1055
  const tables = `${helperTables.trimEnd()}\n`;
1054
1056
  return sql.trim() === "" ? tables : `${sql.trimEnd()}\n\n${tables}`;
1055
1057
  }
1058
+ /**
1059
+ * The read-only actor policies `--helpers-only` writes: those on a table
1060
+ * `rls.tables` names. A resource it does not name has no known table, so its
1061
+ * write commands get a warning instead.
1062
+ */
1063
+ function helpersOnlyActorGuards(guards, tables, actorMayWrite, warnings) {
1064
+ const named = new Set(Object.values(tableNames(tables) ?? {}).map((table) => qualifySqlTable(table)));
1065
+ const kept = guards.filter((guard) => named.has(qualifySqlTable(guard.table)));
1066
+ const unmapped = [...new Set(guards.filter((guard) => !named.has(qualifySqlTable(guard.table))).map((guard) => qualifySqlTable(guard.table)))].toSorted();
1067
+ if (unmapped.length > 0) warnings.push(`rls.readOnlyActors with --helpers-only writes read-only actor policies only on tables rls.tables names; for ${unmapped.join(", ")}, name the table in rls.tables, or add \`and (select ${actorMayWrite}())\` to each hand-written insert, update and delete policy`);
1068
+ return kept;
1069
+ }
1056
1070
  function databaseModeWarnings(policy, { ctx }, warnings) {
1057
1071
  if (ctx.authorize !== "database") return;
1058
1072
  for (const { name } of ctx.scopes) if (policy.grants.some((grant) => grant.scope === name) && scopeTable(ctx, name) === void 0 && scopeSources(ctx, name).length === 0) warnings.push(`${name}-scoped grants read the ${name} memberships table in database mode: set rls.memberships.scopes.${name}, rls.membershipSources or supabase.hook.memberships (or --memberships for the first scope), otherwise they deny`);
@@ -1077,12 +1091,12 @@ function generatePolicies(input, policy, options, built, warnings) {
1077
1091
  const force = input.force === true || rls?.force === true;
1078
1092
  if (force && views.some((view) => view.companion !== void 0)) warnings.push("--force with --revoke-columns: each <table>_visible_fields companion reads as its owner, so its owner needs BYPASSRLS or the restricted columns read as null");
1079
1093
  const policyName = input.policyName ?? rls?.policyName;
1080
- const assembled = helpersOnly ? [] : assemblePolicies(fieldsMode === void 0 ? compiled.branches : rowBranches(compiled.branches), {
1094
+ const assembled = assemblePolicies(fieldsMode === void 0 ? compiled.branches : rowBranches(compiled.branches), {
1081
1095
  perRole: input.policyPerRole === true || rls?.policyPerRole === true,
1082
1096
  ...policyName === void 0 ? {} : { name: policyName }
1083
1097
  });
1084
1098
  const actorKinds = readOnlyActorKinds(rls?.readOnlyActors);
1085
- if (actorKinds !== void 0 && helpersOnly) warnings.push(`rls.readOnlyActors with --helpers-only writes no policies: add \`and (select ${qualified$1(ctx, ACTOR_MAY_WRITE)}())\` to each hand-written insert, update and delete policy, or a restrictive policy that calls it`);
1099
+ const actorGuards = actorKinds === void 0 || !helpersOnly ? [] : helpersOnlyActorGuards(readOnlyActorPolicies(assembled, subjectClaimJsonSql(ctx, "act"), actorKinds), rls?.tables, qualified$1(ctx, ACTOR_MAY_WRITE), warnings);
1086
1100
  const withActors = (items) => actorKinds === void 0 ? items : [...items, ...readOnlyActorPolicies(items, subjectClaimJsonSql(ctx, "act"), actorKinds)];
1087
1101
  let tablePolicies;
1088
1102
  try {
@@ -1104,7 +1118,8 @@ function generatePolicies(input, policy, options, built, warnings) {
1104
1118
  helpersOnly,
1105
1119
  force,
1106
1120
  views,
1107
- policies: withActors(assembled)
1121
+ policies: helpersOnly ? [] : withActors(assembled),
1122
+ ...actorGuards.length === 0 ? {} : { actorGuards: [ACTOR_GUARDS_HEADER, ...actorGuards.map(guardedPolicySql)].join("\n") }
1108
1123
  };
1109
1124
  if (servicePolicies.length === 0) return generated;
1110
1125
  return {
@@ -1543,7 +1558,7 @@ function tableColumns(rowColumns, tables, helpersOnly, warnings) {
1543
1558
  function emitTarget(input, built, generated, preamble) {
1544
1559
  const rls = input.config.rls;
1545
1560
  const { ctx } = built;
1546
- const { policies, force, views, helperTables } = generated;
1561
+ const { policies, force, views, helperTables, actorGuards } = generated;
1547
1562
  const outRel = input.out ?? rls?.out ?? defaultOut(input.target);
1548
1563
  const migration = input.target === "sql" ? "" : dialectRoles(migrationSql(policies, withHelperTables(preamble, helperTables), force, views), ctx.dialect);
1549
1564
  const migrationRel = migration === "" ? void 0 : migrationOut(outRel);
@@ -1551,7 +1566,7 @@ function emitTarget(input, built, generated, preamble) {
1551
1566
  let text;
1552
1567
  switch (input.target) {
1553
1568
  case "sql":
1554
- text = dialectRoles(withHelperTables(emitSql(policies, preamble, force, views), helperTables), ctx.dialect);
1569
+ text = dialectRoles(withHelperTables(withHelperTables(emitSql(policies, preamble, force, views), helperTables), actorGuards), ctx.dialect);
1555
1570
  break;
1556
1571
  case "drizzle":
1557
1572
  text = emitDrizzle(policies, compact({
@@ -1594,7 +1609,7 @@ function generateSql(input, policy, options, built, warnings) {
1594
1609
  const { preamble, audit } = assembled;
1595
1610
  if (generated.force) forceWarnings(built, warnings);
1596
1611
  const emitted = emitTarget(input, built, generated, preamble);
1597
- const { policies, force, views, helperTables, guardedTables, helpersOnly } = generated;
1612
+ const { policies, force, views, helperTables, guardedTables, helpersOnly, actorGuards } = generated;
1598
1613
  const seedsSplit = splitsPart(input.split, "seeds");
1599
1614
  return {
1600
1615
  ...generated,
@@ -1605,7 +1620,7 @@ function generateSql(input, policy, options, built, warnings) {
1605
1620
  policies: dialectRoles(withHelperTables(emitSql(policies, "", force, views), helperTables), ctx.dialect),
1606
1621
  declarativePolicies: dialectRoles(emitSql(policies, "", force, views), ctx.dialect),
1607
1622
  ...guardedTables === void 0 ? {} : { servicePolicies: dialectRoles(guardedTables, ctx.dialect) },
1608
- helpers: dialectRoles(emitSql([], preamble, false, helpersOnly ? views : []), ctx.dialect),
1623
+ helpers: dialectRoles(withHelperTables(emitSql([], preamble, false, helpersOnly ? views : []), actorGuards), ctx.dialect),
1609
1624
  seeds: `${SEEDS_MARKER} schema=${built.schema}\n${seedsBody(ctx, built.schema, generated.compiled.rolePermissions)}\n${seedsSplit ? audit : ""}`,
1610
1625
  indexes: `${INDEXES_MARKER}\n${indexesSql(indexes)}\n`
1611
1626
  })
@@ -192,7 +192,7 @@ async function rlsInput(ctx, verb, shape, options) {
192
192
  };
193
193
  }
194
194
  async function runRls(input) {
195
- return (await import("./rls-CtRaRrn3.js")).runRls(input);
195
+ return (await import("./rls-BItB4vkC.js")).runRls(input);
196
196
  }
197
197
  const generate = defineLeaf({
198
198
  flags: {
@@ -0,0 +1 @@
1
+ export { t as run } from "./run-CjFdAzqE.js";
@@ -5,9 +5,9 @@ import { a as resolveCwd, i as readConfig } from "./config-BR23fL8m.js";
5
5
  import { t as beforeFirst } from "./text-BMFRfBqc.js";
6
6
  import { a as suggestion, i as parseLeaf, n as flagSpellings, o as GLOBAL_FLAGS, r as parseFlags } from "./args-0FrVs5EH.js";
7
7
  import { r as progressReporter, t as redact } from "./credentials-CdDxp7jN.js";
8
- import { a as resolve, i as leaves, n as ROOT, r as SECTIONS, t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
8
+ import { a as resolve, i as leaves, n as ROOT, r as SECTIONS, t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
9
9
  import { t as createStyle } from "./style-Ds11Hmk4.js";
10
- import { i as githubAnnotations, n as envelope, o as sarifLog, t as dataOf } from "./output-CtyBwTNB.js";
10
+ import { i as githubAnnotations, n as envelope, o as sarifLog, t as dataOf } from "./output-DpCUAST3.js";
11
11
  import { i as renameMessage, r as flagRename, t as COMMAND_MOVES } from "./renames-CGj7YwcQ.js";
12
12
  import { stripVTControlCharacters } from "node:util";
13
13
  import { existsSync, statSync } from "node:fs";
@@ -16,7 +16,7 @@ const scan = defineLeaf({
16
16
  }
17
17
  },
18
18
  async run(ctx, { flags }) {
19
- const { runAdoptionScan } = await import("./adoption-scan-DKCOsfcF.js");
19
+ const { runAdoptionScan } = await import("./adoption-scan-oks22j9J.js");
20
20
  return runAdoptionScan({
21
21
  cwd: ctx.cwd,
22
22
  srcPath: flags.src.length > 0 ? flags.src : ctx.config.collect?.srcPath ?? ["src"],
@@ -1,7 +1,7 @@
1
1
  import { r as isRecord } from "./compact-CAf557ZB.js";
2
2
  import { l as packageRoot } from "./version-DMQ0dnN5.js";
3
3
  import { t as DRAFT_PINS } from "./pins-nqarl3oE.js";
4
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
4
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
5
5
  import { t as require__2020 } from "./2020-CJbO8Bvz.js";
6
6
  import { readFileSync } from "node:fs";
7
7
  import { join } from "node:path";
@@ -2,7 +2,7 @@ import { t as byCodePoint } from "./compare-DTfyUunF.js";
2
2
  import { i as readJsonFile } from "./files-BSjrtIHw.js";
3
3
  import { l as packageRoot } from "./version-DMQ0dnN5.js";
4
4
  import { t as didYouMean } from "./args-0FrVs5EH.js";
5
- import { a as nonEmptyOr } from "./output-CtyBwTNB.js";
5
+ import { a as nonEmptyOr } from "./output-DpCUAST3.js";
6
6
  import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, writeFileSync } from "node:fs";
7
7
  import { basename, dirname, join, relative, resolve } from "node:path";
8
8
  import { pathToFileURL } from "node:url";
@@ -1,7 +1,7 @@
1
1
  import { t as defineLeaf } from "./command-L8EstFNm.js";
2
2
  import { t as checkFlag } from "./flags-DXjoTVbS.js";
3
3
  import { r as pick, t as CANCELLED } from "./prompt-BYc6drYW.js";
4
- import { n as detectedAgents, r as runSkills, t as SKILL_AGENTS } from "./skills-Bar7ZqwM.js";
4
+ import { n as detectedAgents, r as runSkills, t as SKILL_AGENTS } from "./skills-5XWpNzCi.js";
5
5
  //#region src/cli/commands/skills.ts
6
6
  const forFlag = { for: {
7
7
  type: "string",
@@ -16,7 +16,7 @@ import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
16
16
  import { i as group, o as sqlStatements } from "./deciding-columns-NB4wcyBf.js";
17
17
  import { d as orSql, h as branchClauses } from "./fields-DI8s3cPl.js";
18
18
  import { t as resolveNames } from "./resolve-names-Y1Cb22cp.js";
19
- import { m as tableFor } from "./verify-6EauWrkP.js";
19
+ import { m as tableFor } from "./verify-CmTiQ-yM.js";
20
20
  //#region src/cli/rls/custom-role-rebuild.ts
21
21
  const PREFIX = "PermDock CLI";
22
22
  /** A column of the roles-table row `r`. */
@@ -0,0 +1 @@
1
+ export { t as runUsage } from "./usage-hMYVFsN5.js";
@@ -16,7 +16,7 @@ const usage = defineLeaf({
16
16
  }
17
17
  },
18
18
  async run(ctx, { flags }) {
19
- return (await import("./usage-CNca1i1W.js")).runUsage({
19
+ return (await import("./usage-BDiTP763.js")).runUsage({
20
20
  cwd: ctx.cwd,
21
21
  config: ctx.config,
22
22
  ignore: flags.ignore,
@@ -6,8 +6,8 @@ import { c as jsonSchemaOf } from "./catalog-doc-DjMKzJp9.js";
6
6
  import { a as loadModule, n as asPolicy, s as pickNamed } from "./load-DjSU5KXO.js";
7
7
  import { r as runCollect } from "./collect-WCl8cJQV.js";
8
8
  import { t as beforeFirst } from "./text-BMFRfBqc.js";
9
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
10
- import { r as findingOf } from "./output-CtyBwTNB.js";
9
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
10
+ import { r as findingOf } from "./output-DpCUAST3.js";
11
11
  import { existsSync, readFileSync, realpathSync } from "node:fs";
12
12
  import { dirname, resolve } from "node:path";
13
13
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -22,7 +22,7 @@ import { a as scopeMembershipTable, i as scopeColumn } from "./compile-EzEMB-7v.
22
22
  import "./budget-BELja5A9.js";
23
23
  import "./sources-C4vkzZJV.js";
24
24
  import { t as beforeFirst } from "./text-BMFRfBqc.js";
25
- import { t as DOCS_BASE } from "./manifest-Cav1rjNQ.js";
25
+ import { t as DOCS_BASE } from "./manifest-Cl6yHiVK.js";
26
26
  import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-A1_sp_tR.js";
27
27
  import { A as quoteQualified, N as arrayColumnsOf, O as quoteIdent, P as columnTypesOf, _ as subjectClaimJsonSql, a as customRolesOn, j as quoteTable, k as quoteLiteral, r as HELPERS, v as subjectClaimSql } from "./shared-DrGLwM3s.js";
28
28
  import { C as accessSql, D as tokenScopeAllowsCall, S as requiresSql, ht as scopeTable, k as capabilityAccessSql, n as tableNameOf, o as graphPlan, q as apiKeyAllowsCall, r as tableNames } from "./tables-DimBcVWY.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permdock",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Typed permissions for TypeScript apps, APIs, databases, and AI agents, with the permdock CLI and test runners.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -1 +0,0 @@
1
- export { n as runPowerSync } from "./powersync-CD8-Ypao.js";
@@ -1 +0,0 @@
1
- export { n as runCloudPull } from "./pull-CAeaAioC.js";
@@ -1 +0,0 @@
1
- export { t as run } from "./run-B5nyNKe8.js";
@@ -1 +0,0 @@
1
- export { t as runUsage } from "./usage-Y6xZUfE-.js";