permdock 0.14.0 → 0.16.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 (61) hide show
  1. package/dist/{adoption-scan-BGtwGfoH.js → adoption-scan-C2BCiUEG.js} +1 -1
  2. package/dist/{arazzo-Bol9OgF-.js → arazzo-CHoYnQXO.js} +1 -1
  3. package/dist/{arazzo-9uhbmKf5.js → arazzo-ClYz2Z0W.js} +1 -1
  4. package/dist/better-supabase/index.d.ts +6 -2
  5. package/dist/{better-supabase-83Gsetrv.js → better-supabase-Dirf_8Ec.js} +20 -4
  6. package/dist/{casbin-CBXD5rFx.js → casbin-C9EvElBb.js} +1 -1
  7. package/dist/{casbin-1PVgMHOR.js → casbin-D_rVQ31U.js} +1 -1
  8. package/dist/cli/bin.js +1 -1
  9. package/dist/cli/index.js +2 -2
  10. package/dist/{cloud-BfJVKpcN.js → cloud-DdaJ7ILM.js} +1 -1
  11. package/dist/{codemod-DWfH-KHk.js → codemod-B1USJS7a.js} +2 -2
  12. package/dist/{codemod-BXLd4K3j.js → codemod-ammy6wv4.js} +1 -1
  13. package/dist/{collect-lZgCKMe_.js → collect-DnXL4B8c.js} +1 -1
  14. package/dist/{completion-fJ19i8Jk.js → completion--Y4YItFZ.js} +1 -1
  15. package/dist/{config-BYSk3iKV.js → config-BQ77xMZk.js} +1 -1
  16. package/dist/{config-ZZdtyQZy.js → config-CcLlvEYW.js} +2 -2
  17. package/dist/{config-report-BpztJ3HP.js → config-report-DVMOLJ7F.js} +1 -1
  18. package/dist/{doctor-NxCr_hba.js → doctor-CwYc2lJb.js} +2 -2
  19. package/dist/{doctor-eNc7A_7I.js → doctor-D1Xnmy6u.js} +12 -11
  20. package/dist/{import-efm6qPgx.js → import-Dfe2unKO.js} +1 -1
  21. package/dist/{init-D6rpLPwM.js → init-BUo2G-FI.js} +1 -1
  22. package/dist/{init-BtWY1L0J.js → init-C6cWP3xW.js} +3 -3
  23. package/dist/{login-cLflB63Z.js → login-D1MHOomI.js} +1 -1
  24. package/dist/{login-B8s6VI8Z.js → login-DCKE5pVy.js} +3 -3
  25. package/dist/{manifest-BWIR_EEB.js → manifest-Cft51TLK.js} +30 -30
  26. package/dist/mcp/index.d.ts +15 -6
  27. package/dist/mcp/index.js +43 -16
  28. package/dist/next/plugin.js +1 -1
  29. package/dist/{openapi-CT55v3WD.js → openapi-BwWkHxvW.js} +2 -2
  30. package/dist/{openapi-DDENzRbG.js → openapi-DSzQN7Zu.js} +1 -1
  31. package/dist/{output-3iB3_Hfg.js → output-DlICTjBh.js} +1 -1
  32. package/dist/powersync-0QVP_DuR.js +1 -0
  33. package/dist/{powersync-DVbskWD9.js → powersync-CBhTBhG2.js} +1 -1
  34. package/dist/{powersync-COjQTHRD.js → powersync-DAfo0EL5.js} +2 -2
  35. package/dist/pull-CCAChKuy.js +1 -0
  36. package/dist/{pull-Er4aBHlL.js → pull-CQCGy1gv.js} +1 -1
  37. package/dist/react-native/index.d.ts +14 -1
  38. package/dist/react-native/index.js +46 -1
  39. package/dist/{rls-DjjOHlKE.js → rls-C5ZGzR_B.js} +1 -1
  40. package/dist/{rls-DY3Fc18U.js → rls-tHu7N_Pe.js} +4 -4
  41. package/dist/run-C5P3x1EI.js +1 -0
  42. package/dist/{run-BlxXmaDS.js → run-DUq1xU4K.js} +3 -3
  43. package/dist/{scan-CR_xm71k.js → scan-C3HRH9Fs.js} +1 -1
  44. package/dist/{schema-CnrIP94n.js → schema-DvXvxPhQ.js} +1 -1
  45. package/dist/{skills-uzsVqEi9.js → skills-B2qTbaQa.js} +1 -1
  46. package/dist/{skills-CY-MY_Oj.js → skills-Celu2W6v.js} +1 -1
  47. package/dist/{supautils-DZ95xxW-.js → supautils-SwE642N1.js} +22 -9
  48. package/dist/unplugin/index.js +1 -1
  49. package/dist/{usage-WYRc7Kqg.js → usage-CPXeuAdT.js} +2 -2
  50. package/dist/{usage-Dm2hn0pS.js → usage-CfZCQd0F.js} +1 -1
  51. package/dist/usage-DmmO_xjJ.js +1 -0
  52. package/dist/{verify-CHQgLrtS.js → verify-DfT0QYNh.js} +1 -1
  53. package/dist/watch-CuPz0D2M.js +1 -0
  54. package/dist/{watch-CTfSlIGD.js → watch-DAkjm75Y.js} +1 -1
  55. package/package.json +1 -1
  56. package/skills/permdock-data/references/queries-and-rls.md +1 -1
  57. package/dist/powersync-k8lUeC37.js +0 -1
  58. package/dist/pull-BY1METGf.js +0 -1
  59. package/dist/run-DGSLvGty.js +0 -1
  60. package/dist/usage-CH7oISI6.js +0 -1
  61. package/dist/watch-C44FtM79.js +0 -1
@@ -112,15 +112,24 @@ type McpProcedureEnforcement = {
112
112
  readonly permissionFor: (name: string) => Permission | undefined;
113
113
  readonly oauthScopesFor?: (name: string) => readonly string[] | undefined;
114
114
  };
115
+ /** What an `onError` sink receives next to the thrown error. */
116
+ type McpErrorContext = {
117
+ /** The key of the permission being checked. */
118
+ readonly permission: string | null;
119
+ /** The tool, resource or prompt name. */
120
+ readonly name: string;
121
+ /**
122
+ * The tenant the `tenant` option resolves for the call, or `null` when it
123
+ * resolves none or throws.
124
+ */
125
+ readonly tenant: string | null;
126
+ };
115
127
  /**
116
128
  * Receives an error a check threw: a loader, `decide`, an approval input or a
117
129
  * subject a list cannot build. The call still refuses; a sink that throws is
118
- * ignored.
130
+ * ignored. A `BetterSupabaseOnError` from `permdock/better-supabase` fits.
119
131
  */
120
- type McpOnError = (error: unknown, context: {
121
- readonly permission: string | null;
122
- readonly name: string;
123
- }) => void;
132
+ type McpOnError = (error: unknown, context: McpErrorContext) => void;
124
133
  type McpPermDockOptions<TUser = unknown> = InstanceOptions & {
125
134
  /**
126
135
  * Receives the verified auth info, or `{}` on a transport without one
@@ -299,4 +308,4 @@ type McpSubjectOptions = Pick<JwtSubjectOptions, "claims" | "groupRoles" | "sche
299
308
  */
300
309
  export declare function subjectFromMcp(authInfo: McpAuthInfo | undefined, options?: McpSubjectOptions): Subject<McpPrincipal>;
301
310
  //#endregion
302
- export type { DeniedText, DeniedTextEvent, GuardedMcpServer, McpAuthInfo, McpOnError, McpPermDock, McpPermDockOptions, McpPrincipal, McpProcedureEnforcement, McpProcedureToolConfig, McpPromptConfig, McpResourceConfig, McpResourceGuardOptions, McpResourceGuardResult, McpSubjectOptions, McpToolConfig, McpUiResourceConfig, ProcedureMcpServer, ProtectedResourceMetadataOptions };
311
+ export type { DeniedText, DeniedTextEvent, GuardedMcpServer, McpAuthInfo, McpErrorContext, McpOnError, McpPermDock, McpPermDockOptions, McpPrincipal, McpProcedureEnforcement, McpProcedureToolConfig, McpPromptConfig, McpResourceConfig, McpResourceGuardOptions, McpResourceGuardResult, McpSubjectOptions, McpToolConfig, McpUiResourceConfig, ProcedureMcpServer, ProtectedResourceMetadataOptions };
package/dist/mcp/index.js CHANGED
@@ -222,6 +222,15 @@ function report(sink, error, context) {
222
222
  sink(error, context);
223
223
  } catch {}
224
224
  }
225
+ /** The call's tenant for an error report; `null` when it has none or throws. */
226
+ async function reportedTenant(tenant, authInfo) {
227
+ if (typeof tenant !== "function") return tenant ?? null;
228
+ try {
229
+ return await tenant(authInfo ?? {}) ?? null;
230
+ } catch {
231
+ return null;
232
+ }
233
+ }
225
234
  //#endregion
226
235
  //#region src/mcp/call-check.ts
227
236
  function reachOf(policy, permission, declared) {
@@ -264,16 +273,11 @@ function credentialRefusal(checks, permission, reach, authInfo) {
264
273
  * The completion re-check of a long-running tool: the call must still be
265
274
  * allowed, or approved with the same token.
266
275
  */
267
- async function recheck(checks, binding, args, call, permission, approved, tool, name) {
276
+ async function recheck(checks, binding, args, call, permission, approved, tool, onError) {
268
277
  const rechecked = await checks.kernel.check(binding, args, call, compact({
269
278
  simulate: true,
270
279
  tool,
271
- onError: (error) => {
272
- report(checks.onError, error, {
273
- permission: permission.key,
274
- name
275
- });
276
- }
280
+ onError
277
281
  }));
278
282
  if (!rechecked.ok) return {
279
283
  ok: false,
@@ -289,14 +293,33 @@ async function recheck(checks, binding, args, call, permission, approved, tool,
289
293
  } : plainRefusal(permission, `Denied: ${permission.key} was revoked before the call completed.`, {})
290
294
  };
291
295
  }
296
+ /**
297
+ * Sends the errors a check collected to the `onError` sink, after the check, so
298
+ * the tenant resolves only when there is something to report.
299
+ */
300
+ async function flush(checks, authInfo, errors, permission, name) {
301
+ if (errors.length === 0 || checks.onError === void 0) return;
302
+ const tenant = await reportedTenant(checks.options.tenant, authInfo);
303
+ for (const error of errors) report(checks.onError, error, {
304
+ permission,
305
+ name,
306
+ tenant
307
+ });
308
+ }
292
309
  async function decideCall(checks, permission, reach, args, load, context, handlerContext, capabilities, completion, tool, name = tool ?? permission.key) {
293
310
  const authInfo = context.http?.authInfo;
311
+ const errors = [];
294
312
  const onError = (error) => {
295
- report(checks.onError, error, {
296
- permission: permission.key,
297
- name
298
- });
313
+ errors.push(error);
299
314
  };
315
+ try {
316
+ return await decided(checks, permission, reach, args, load, context, handlerContext, capabilities, completion, tool, onError);
317
+ } finally {
318
+ await flush(checks, authInfo, errors, permission.key, name);
319
+ }
320
+ }
321
+ async function decided(checks, permission, reach, args, load, context, handlerContext, capabilities, completion, tool, onError) {
322
+ const authInfo = context.http?.authInfo;
300
323
  const refused = credentialRefusal(checks, permission, reach, authInfo);
301
324
  if (refused !== void 0) return {
302
325
  ok: false,
@@ -307,7 +330,7 @@ async function decideCall(checks, permission, reach, args, load, context, handle
307
330
  data: load === void 0 ? void 0 : () => load()
308
331
  });
309
332
  const call = compact({ authInfo });
310
- if (completion !== void 0) return recheck(checks, binding, args, call, permission, completion.approved, tool, name);
333
+ if (completion !== void 0) return recheck(checks, binding, args, call, permission, completion.approved, tool, onError);
311
334
  const checked = await checks.kernel.check(binding, args, call, compact({
312
335
  resumeToken: approvalTokenOf(context),
313
336
  tool,
@@ -393,10 +416,14 @@ async function visible(checks, context, permissionOf, keys, declaredOf = () => v
393
416
  try {
394
417
  permdock = await checks.kernel.instance(compact({ authInfo }));
395
418
  } catch (error) {
396
- for (const entry of guarded) report(checks.onError, error, {
397
- permission: entry.permission.key,
398
- name: entry.key
399
- });
419
+ if (checks.onError !== void 0) {
420
+ const tenant = await reportedTenant(checks.options.tenant, authInfo);
421
+ for (const entry of guarded) report(checks.onError, error, {
422
+ permission: entry.permission.key,
423
+ name: entry.key,
424
+ tenant
425
+ });
426
+ }
400
427
  return shown;
401
428
  }
402
429
  for (const entry of guarded) if ((!tools || actorMayCallTool(permdock.subject.actor, entry.key)) && permdock.mayUse(entry.permission)) shown.add(entry.key);
@@ -1,5 +1,5 @@
1
1
  import { a as describeError } from "../errors-Vpu6rKHl.js";
2
- import { n as createCollectScheduler, r as report, t as collectOnce } from "../watch-CTfSlIGD.js";
2
+ import { n as createCollectScheduler, r as report, t as collectOnce } from "../watch-DAkjm75Y.js";
3
3
  //#region src/cli/plugin.ts
4
4
  const PHASE_BUILD = "phase-production-build";
5
5
  const PHASE_DEV = "phase-development-server";
@@ -80,7 +80,7 @@ const emit = defineLeaf({
80
80
  }
81
81
  },
82
82
  async run(ctx, { flags }) {
83
- return (await import("./openapi-DDENzRbG.js")).runOpenapi({
83
+ return (await import("./openapi-DSzQN7Zu.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-efm6qPgx.js")).runOpenapiImport({
125
+ return (await import("./import-Dfe2unKO.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-CnrIP94n.js";
10
+ import { n as validateOverlay, t as validateOpenapi } from "./schema-DvXvxPhQ.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-BWIR_EEB.js";
2
+ import { t as DOCS_BASE } from "./manifest-Cft51TLK.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) {
@@ -0,0 +1 @@
1
+ export { n as runPowerSync } from "./powersync-CBhTBhG2.js";
@@ -19,7 +19,7 @@ import { n as tableNameOf, 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-CHQgLrtS.js";
22
+ import { m as tableFor, t as canFixture } from "./verify-DfT0QYNh.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-k8lUeC37.js");
17
+ const { runPowerSync } = await import("./powersync-0QVP_DuR.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-k8lUeC37.js");
48
+ const { runPowerSync } = await import("./powersync-0QVP_DuR.js");
49
49
  return runPowerSync({
50
50
  cwd: ctx.cwd,
51
51
  config: ctx.config,
@@ -0,0 +1 @@
1
+ export { n as runCloudPull } from "./pull-CQCGy1gv.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-3iB3_Hfg.js";
10
+ import { a as nonEmptyOr } from "./output-DlICTjBh.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";
@@ -124,6 +124,19 @@ type PowerSyncSourceOptions<Q extends string> = {
124
124
  * as an argument; `permdock/react-native` never imports `@powersync/*`.
125
125
  */
126
126
  export declare function powerSyncSource<Q extends string>(db: PowerSyncWatchable, options: PowerSyncSourceOptions<Q>): SnapshotSource;
127
+ /** `powerSyncSource` over one database, memoised per manifest and options. */
128
+ type PowerSyncSourceCache = {
129
+ readonly source: <Q extends string>(options: PowerSyncSourceOptions<Q>) => SnapshotSource;
130
+ };
131
+ /**
132
+ * A cache of `powerSyncSource` sources over `db`. `source` answers the same
133
+ * source for the same `manifest` object and, with `user`, the same `user`,
134
+ * `tenant` and `attributes` content, or, with `queries`, the same `queries` and
135
+ * `read` objects. A render that rebuilds its options then keeps the source, so
136
+ * `PermDockProvider` neither resubscribes nor rereads. Entries live as long as
137
+ * their manifest; create one cache per database, outside render.
138
+ */
139
+ export declare function powerSyncSourceCache(db: PowerSyncWatchable): PowerSyncSourceCache;
127
140
  //#endregion
128
141
  //#region src/react-native/provider.d.ts
129
142
  export declare function PermDockProvider(props: ReactNativePermDockProviderProps): ReactElement;
@@ -209,4 +222,4 @@ type NativeStoreOptions = Omit<ReactNativePermDockProviderProps, "children" | "h
209
222
  export declare function connectSource(store: ClientStore, source: SnapshotSource, subscribeForeground?: SubscribeForeground): () => void;
210
223
  export declare function nativeStore(options: NativeStoreOptions): ClientStore;
211
224
  //#endregion
212
- export { type ActionResult, type AppStateModule, type ApprovalHandle, type ApprovalState, type ClientPermDock, type ClientStatus, type EventSourceRefreshOptions, type FilterResult, type GateMode, type GateView, type LocalMembershipRows, type LocalRowColumn, type LocalRowValue, type LocalSnapshotData, type LocalSnapshotManifest, type LocalSnapshotOptions, type LocalSnapshotRows, type MmkvInstance, type NativeRevalidate, type NetInfoModule, type PermDockDefaults, type PermissionSet, type PermissionState, type PowerSyncQuery, type PowerSyncSourceOptions, type PowerSyncWatchable, Protected, type ProtectedAction, type ProtectedActionHandle, type ProtectedActionView, type ProtectedProps, type ReactNativePermDockProviderProps, type RefreshTrigger, type RolesOptions, type SecureStoreModule, type SnapshotSource, type SnapshotStorage, type SubjectView, type SubscribeForeground, type SubscribeOnline, type TenantView, approvalHeaders, describe, eventSourceRefresh, parseLocalSnapshotManifest, requiredPlans, useApproval, useAssignablePermissions, useAssignableRoles, useDescribe, useFilter, useGate, useMemberships, usePermDock, usePermission, usePermissions, useProtectedAction, useRoles, useSubject, useTenant };
225
+ export { type ActionResult, type AppStateModule, type ApprovalHandle, type ApprovalState, type ClientPermDock, type ClientStatus, type EventSourceRefreshOptions, type FilterResult, type GateMode, type GateView, type LocalMembershipRows, type LocalRowColumn, type LocalRowValue, type LocalSnapshotData, type LocalSnapshotManifest, type LocalSnapshotOptions, type LocalSnapshotRows, type MmkvInstance, type NativeRevalidate, type NetInfoModule, type PermDockDefaults, type PermissionSet, type PermissionState, type PowerSyncQuery, type PowerSyncSourceCache, type PowerSyncSourceOptions, type PowerSyncWatchable, Protected, type ProtectedAction, type ProtectedActionHandle, type ProtectedActionView, type ProtectedProps, type ReactNativePermDockProviderProps, type RefreshTrigger, type RolesOptions, type SecureStoreModule, type SnapshotSource, type SnapshotStorage, type SubjectView, type SubscribeForeground, type SubscribeOnline, type TenantView, approvalHeaders, describe, eventSourceRefresh, parseLocalSnapshotManifest, requiredPlans, useApproval, useAssignablePermissions, useAssignableRoles, useDescribe, useFilter, useGate, useMemberships, usePermDock, usePermission, usePermissions, useProtectedAction, useRoles, useSubject, useTenant };
@@ -603,6 +603,51 @@ function powerSyncSource(db, options) {
603
603
  }
604
604
  };
605
605
  }
606
+ /**
607
+ * A cache of `powerSyncSource` sources over `db`. `source` answers the same
608
+ * source for the same `manifest` object and, with `user`, the same `user`,
609
+ * `tenant` and `attributes` content, or, with `queries`, the same `queries` and
610
+ * `read` objects. A render that rebuilds its options then keeps the source, so
611
+ * `PermDockProvider` neither resubscribes nor rereads. Entries live as long as
612
+ * their manifest; create one cache per database, outside render.
613
+ */
614
+ function powerSyncSourceCache(db) {
615
+ const byManifest = /* @__PURE__ */ new WeakMap();
616
+ return Object.freeze({ source: (options) => {
617
+ let entries = byManifest.get(options.manifest);
618
+ if (entries === void 0) {
619
+ entries = {
620
+ rows: /* @__PURE__ */ new Map(),
621
+ custom: /* @__PURE__ */ new WeakMap()
622
+ };
623
+ byManifest.set(options.manifest, entries);
624
+ }
625
+ if ("user" in options) {
626
+ const key = JSON.stringify([
627
+ options.user,
628
+ options.tenant ?? null,
629
+ options.attributes ?? null
630
+ ]);
631
+ let source = entries.rows.get(key);
632
+ if (source === void 0) {
633
+ source = powerSyncSource(db, options);
634
+ entries.rows.set(key, source);
635
+ }
636
+ return source;
637
+ }
638
+ let byRead = entries.custom.get(options.queries);
639
+ if (byRead === void 0) {
640
+ byRead = /* @__PURE__ */ new WeakMap();
641
+ entries.custom.set(options.queries, byRead);
642
+ }
643
+ let source = byRead.get(options.read);
644
+ if (source === void 0) {
645
+ source = powerSyncSource(db, options);
646
+ byRead.set(options.read, source);
647
+ }
648
+ return source;
649
+ } });
650
+ }
606
651
  //#endregion
607
652
  //#region src/react-native/storage.ts
608
653
  const SNAPSHOT_KEY = "permdock.snapshot";
@@ -1081,4 +1126,4 @@ function netInfoOnline(netInfo) {
1081
1126
  });
1082
1127
  }
1083
1128
  //#endregion
1084
- export { PermDockProvider, Protected, appStateForeground, approvalHeaders, buildLocalSnapshot, connectSource, describe, eventSourceRefresh, localSnapshot, memorySnapshotStorage, mmkvStorage, nativeStore, netInfoOnline, parseLocalSnapshotManifest, powerSyncSource, requiredPlans, secureStoreStorage, useApproval, useAssignablePermissions, useAssignableRoles, useDescribe, useFilter, useGate, useMemberships, usePermDock, usePermission, usePermissionGuard, usePermissions, useProtectedAction, useRoles, useSnapshotReady, useSubject, useTenant };
1129
+ export { PermDockProvider, Protected, appStateForeground, approvalHeaders, buildLocalSnapshot, connectSource, describe, eventSourceRefresh, localSnapshot, memorySnapshotStorage, mmkvStorage, nativeStore, netInfoOnline, parseLocalSnapshotManifest, powerSyncSource, powerSyncSourceCache, requiredPlans, secureStoreStorage, useApproval, useAssignablePermissions, useAssignableRoles, useDescribe, useFilter, useGate, useMemberships, usePermDock, usePermission, usePermissionGuard, usePermissions, useProtectedAction, useRoles, useSnapshotReady, useSubject, useTenant };
@@ -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-DY3Fc18U.js")).runRls(input);
195
+ return (await import("./rls-tHu7N_Pe.js")).runRls(input);
196
196
  }
197
197
  const generate = defineLeaf({
198
198
  flags: {
@@ -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-3iB3_Hfg.js";
14
+ import { a as nonEmptyOr } from "./output-DlICTjBh.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-Er4aBHlL.js";
17
+ import { t as fetchProjectPolicy } from "./pull-CQCGy1gv.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-HPgwYb9V.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-CHQgLrtS.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-DZ95xxW-.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-DfT0QYNh.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-SwE642N1.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";
@@ -0,0 +1 @@
1
+ export { t as run } from "./run-DUq1xU4K.js";
@@ -1,13 +1,13 @@
1
1
  import { i as dateAt } from "./time-nR1JesoH.js";
2
2
  import { a as describeError, l as problemOf, n as CliError, r as cliProblem, s as kindOfCode } from "./errors-Vpu6rKHl.js";
3
3
  import { s as cliVersion } from "./version-DMQ0dnN5.js";
4
- import { a as resolveCwd, i as readConfig } from "./config-ZZdtyQZy.js";
4
+ import { a as resolveCwd, i as readConfig } from "./config-CcLlvEYW.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-BWIR_EEB.js";
8
+ import { a as resolve, i as leaves, n as ROOT, r as SECTIONS, t as DOCS_BASE } from "./manifest-Cft51TLK.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-3iB3_Hfg.js";
10
+ import { i as githubAnnotations, n as envelope, o as sarifLog, t as dataOf } from "./output-DlICTjBh.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-BGtwGfoH.js");
19
+ const { runAdoptionScan } = await import("./adoption-scan-C2BCiUEG.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-3iB3_Hfg.js";
4
+ import { a as nonEmptyOr } from "./output-DlICTjBh.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";
@@ -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-CY-MY_Oj.js";
4
+ import { n as detectedAgents, r as runSkills, t as SKILL_AGENTS } from "./skills-Celu2W6v.js";
5
5
  //#region src/cli/commands/skills.ts
6
6
  const forFlag = { for: {
7
7
  type: "string",
@@ -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-3iB3_Hfg.js";
5
+ import { a as nonEmptyOr } from "./output-DlICTjBh.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";
@@ -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-CHQgLrtS.js";
19
+ import { m as tableFor } from "./verify-DfT0QYNh.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`. */
@@ -199,8 +199,8 @@ const DROPPED_LIST = `(select string_agg(g.role || ': ' || g.items, '; ' order b
199
199
  * shared role holds is skipped with a warning (`name-taken`). Before-row
200
200
  * triggers hold a signed-in caller at trigger depth 1 to the checks of
201
201
  * `permdock_replace_custom_role_grants`: `permdock_custom_role_guard` on each
202
- * role the row names, then `permdock_custom_role_beyond` on the entry it adds;
203
- * a refusal aborts the write.
202
+ * role the row names, the entry it adds against the ceiling, then
203
+ * `permdock_custom_role_beyond` on that entry; a refusal aborts the write.
204
204
  */
205
205
  function customRoleSyncSql(ctx, from, migrate) {
206
206
  const plan = rebuildPlan(ctx, from, migrate);
@@ -305,6 +305,17 @@ begin
305
305
  end if;
306
306
  v_dropped := '{}'::text[];
307
307
  ${plan.collectNew[name]}
308
+ if cardinality(v_dropped) > 0 then
309
+ raise exception using
310
+ errcode = '22023',
311
+ message = 'permdock: ' || v_dropped[1],
312
+ hint = case
313
+ when v_dropped[1] like '%outside the ceiling of %' then 'outside-ceiling'
314
+ when v_dropped[1] like '%, level % is not declared' then 'unknown-level'
315
+ when v_dropped[1] like '%, not a declared role' then 'unknown-role'
316
+ else 'unknown-permission'
317
+ end;
318
+ end if;
308
319
  select string_agg(b, ', ' order by b) into v_beyond
309
320
  from ${beyond}(v.tenant_id, v.scope, v.scope_id, v_allow, v_deny, v_include) b;
310
321
  if v_beyond is not null then
@@ -389,10 +400,11 @@ function permissionCase(key, branches) {
389
400
  const granted = `coalesce((${allow}), false)`;
390
401
  return ` when ${quoteLiteral(key)} then ${deny === void 0 ? granted : `${granted} and (${deny}) is not true`}`;
391
402
  }
392
- function forUserSql(ctx, fn, helper) {
403
+ function forUserSql(ctx, fn, helper, ids) {
393
404
  const user = ctx.dialect === "supabase" ? "uuid" : "text";
394
- const signature = `${user}, text, jsonb`;
395
- const head = `create or replace function ${fn}(p_user ${user}, p_permission text, p_claims jsonb default '{}')
405
+ const signature = `${user}, text, ${ids ? "text[], " : ""}jsonb`;
406
+ const call = `${helper}(p_permission${ids ? ", p_ids" : ""})`;
407
+ const head = `create or replace function ${fn}(p_user ${user}, p_permission text, ${ids ? "p_ids text[], " : ""}p_claims jsonb default '{}')
396
408
  returns setof text
397
409
  language plpgsql
398
410
  volatile
@@ -409,7 +421,7 @@ begin
409
421
  (coalesce(p_claims, '{}'::jsonb) || pg_catalog.jsonb_build_object('sub', p_user::text, 'role', 'authenticated'))::text,
410
422
  true
411
423
  );
412
- return query select r.id from ${helper}(p_permission) r(id);
424
+ return query select r.id from ${call} r(id);
413
425
  perform pg_catalog.set_config('request.jwt.claims', coalesce(v_claims, ''), true);
414
426
  end
415
427
  $$;
@@ -429,7 +441,7 @@ begin
429
441
  v_saved := v_saved || pg_catalog.jsonb_build_object(v_key, coalesce(pg_catalog.current_setting(${quoteLiteral(`${prefix}.`)} || v_key, true), ''));
430
442
  perform pg_catalog.set_config(${quoteLiteral(`${prefix}.`)} || v_key, v_value, true);
431
443
  end loop;
432
- return query select r.id from ${helper}(p_permission) r(id);
444
+ return query select r.id from ${call} r(id);
433
445
  for v_key, v_value in select s.key, s.value from pg_catalog.jsonb_each_text(v_saved) s loop
434
446
  perform pg_catalog.set_config(${quoteLiteral(`${prefix}.`)} || v_key, v_value, true);
435
447
  end loop;
@@ -540,7 +552,8 @@ ${rowSql(ctx, name, table, near)}`;
540
552
  if (ctx.dialect === "neon") return rows;
541
553
  return `${rows}
542
554
  -- the same rows for a user the caller names, with claims it supplies; no client role may execute it
543
- ${forUserSql(ctx, qualified(ctx, `${name}_for`), fn)}`;
555
+ ${forUserSql(ctx, qualified(ctx, `${name}_for`), fn, false)}
556
+ ${forUserSql(ctx, qualified(ctx, `${name}_for`), fn, true)}`;
544
557
  }
545
558
  /**
546
559
  * `permitted_<resource>_rows(p_permission)` for each resource `select` names
@@ -1,4 +1,4 @@
1
- import { n as createCollectScheduler, r as report } from "../watch-CTfSlIGD.js";
1
+ import { n as createCollectScheduler, r as report } from "../watch-DAkjm75Y.js";
2
2
  import { r as peerHint } from "../peer-Q0fuZ6hx.js";
3
3
  import { createRequire } from "node:module";
4
4
  //#region src/unplugin/index.ts
@@ -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-BWIR_EEB.js";
10
- import { r as findingOf } from "./output-3iB3_Hfg.js";
9
+ import { t as DOCS_BASE } from "./manifest-Cft51TLK.js";
10
+ import { r as findingOf } from "./output-DlICTjBh.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";
@@ -16,7 +16,7 @@ const usage = defineLeaf({
16
16
  }
17
17
  },
18
18
  async run(ctx, { flags }) {
19
- return (await import("./usage-CH7oISI6.js")).runUsage({
19
+ return (await import("./usage-DmmO_xjJ.js")).runUsage({
20
20
  cwd: ctx.cwd,
21
21
  config: ctx.config,
22
22
  ignore: flags.ignore,
@@ -0,0 +1 @@
1
+ export { t as runUsage } from "./usage-CPXeuAdT.js";
@@ -22,7 +22,7 @@ import { i as qualifySqlTable, n as defaultSqlCommand, t as SQL_IDENT } from "./
22
22
  import { a as scopeMembershipTable, i as scopeColumn } from "./compile-EzEMB-7v.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-BWIR_EEB.js";
25
+ import { t as DOCS_BASE } from "./manifest-Cft51TLK.js";
26
26
  import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-Bm4_K1ke.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";
@@ -0,0 +1 @@
1
+ export { i as watchCollect } from "./watch-DAkjm75Y.js";
@@ -2,7 +2,7 @@ import { i as dateAt } from "./time-nR1JesoH.js";
2
2
  import { a as describeError } from "./errors-Vpu6rKHl.js";
3
3
  import { t as defaultSrcPath } from "./files-BSjrtIHw.js";
4
4
  import { r as runCollect } from "./collect-WCl8cJQV.js";
5
- import { n as isConfigFile, r as loadConfig } from "./config-ZZdtyQZy.js";
5
+ import { n as isConfigFile, r as loadConfig } from "./config-CcLlvEYW.js";
6
6
  import { watch } from "node:fs";
7
7
  import { basename, dirname, relative, resolve, sep } from "node:path";
8
8
  //#region src/cli/watch.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permdock",
3
- "version": "0.14.0",
3
+ "version": "0.16.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",
@@ -36,7 +36,7 @@ pnpm exec permdock rls verify --introspect
36
36
  ```
37
37
 
38
38
  - List the scope instances a permission reaches in process with `permdock.permittedIds(permission, scope, { within })`, never a loop of `can` over memberships with a made-up row.
39
- - Generated policies call `permdock_has('<key>')` and one `permitted_<scope>_ids('<key>')` per declared scope, which Postgres runs once per statement. Hand-written policies that only need membership (an organization switcher) call `member_<scope>_ids()` instead of inventing a permission key. A policy that passes a user id, such as better-supabase's team sharing, calls `member_team_ids_of(<user>)` on Supabase: it answers only when the id is the caller's. Trusted SQL that acts for a stored user (a job, a trigger, a deferred approval) calls `permdock_has_for(p_user, '<key>')` and `permitted_<scope>_ids_for(p_user, '<key>')` in `database` mode, or the generated `permitted_<resource>_rows_for`, never a hand-written function that rewrites `request.jwt.claims`. With `rls.rowHelpers`, a hand-written policy or function that needs the rows a permission reaches through the graph calls `permitted_<resource>_rows('<permission key>')` instead of restating closure walks, link hops and `includes`. A table's own `select` or `insert` policy calls the row-valued `permitted_<resource>_row(<table>, '<permission key>')` instead, so `insert ... returning` sees the new row. A function that checks a known set of rows (one tenant's, a page, a batch) calls `permitted_<resource>_rows('<permission key>', <text[] of ids>)`, which reads only those ids, instead of the one-argument form, which reads the whole table, or a `permitted_<resource>_row` call per row. Hand-written policies and functions that apply no row condition call `permitted_<scope>_ids_by_permission('<permission key>')` or `permdock_has_permission('<permission key>')` instead of spelling a positional `#n` grant key. A screen or RPC that checks many permissions on one organization calls `permitted_<scope>_permission_keys(<id>)` once (or `(<id>, array['<key>', ...])` for a few keys) instead of one helper call per key. A role editor or admin screen lists a role's keys with `permdock_role_permissions(p_role, p_scope, p_tenant, p_scope_id)` (declared roles, and custom roles of the caller's tenant in `database` mode) and the catalog with `permdock_permission_keys()` (`permdock_permission_keys('<scope>')` for the keys a role on that scope can hold), instead of joining `role_permissions` and the custom-role tables by hand; server code reads any tenant's roles with `permdock_trusted_role_permissions`, granted through `rls.trustedReaders`. Set `--tenant-type` (or `rls.scopeTypes`, keyed by scope name or `tenant` and `team`) to the scope columns' types.
39
+ - Generated policies call `permdock_has('<key>')` and one `permitted_<scope>_ids('<key>')` per declared scope, which Postgres runs once per statement. Hand-written policies that only need membership (an organization switcher) call `member_<scope>_ids()` instead of inventing a permission key. A policy that passes a user id, such as better-supabase's team sharing, calls `member_team_ids_of(<user>)` on Supabase: it answers only when the id is the caller's. Trusted SQL that acts for a stored user (a job, a trigger, a deferred approval) calls `permdock_has_for(p_user, '<key>')` and `permitted_<scope>_ids_for(p_user, '<key>')` in `database` mode, or the generated `permitted_<resource>_rows_for` (`permitted_<resource>_rows_for(p_user, '<key>', <text[] of ids>)` for a known set of rows, such as a share link checked against its creator's access), never a hand-written function that rewrites `request.jwt.claims`. With `rls.rowHelpers`, a hand-written policy or function that needs the rows a permission reaches through the graph calls `permitted_<resource>_rows('<permission key>')` instead of restating closure walks, link hops and `includes`. A table's own `select` or `insert` policy calls the row-valued `permitted_<resource>_row(<table>, '<permission key>')` instead, so `insert ... returning` sees the new row. A function that checks a known set of rows (one tenant's, a page, a batch) calls `permitted_<resource>_rows('<permission key>', <text[] of ids>)`, which reads only those ids, instead of the one-argument form, which reads the whole table, or a `permitted_<resource>_row` call per row. Hand-written policies and functions that apply no row condition call `permitted_<scope>_ids_by_permission('<permission key>')` or `permdock_has_permission('<permission key>')` instead of spelling a positional `#n` grant key. A screen or RPC that checks many permissions on one organization calls `permitted_<scope>_permission_keys(<id>)` once (or `(<id>, array['<key>', ...])` for a few keys) instead of one helper call per key. A role editor or admin screen lists a role's keys with `permdock_role_permissions(p_role, p_scope, p_tenant, p_scope_id)` (declared roles, and custom roles of the caller's tenant in `database` mode) and the catalog with `permdock_permission_keys()` (`permdock_permission_keys('<scope>')` for the keys a role on that scope can hold), instead of joining `role_permissions` and the custom-role tables by hand; server code reads any tenant's roles with `permdock_trusted_role_permissions`, granted through `rls.trustedReaders`. Set `--tenant-type` (or `rls.scopeTypes`, keyed by scope name or `tenant` and `team`) to the scope columns' types.
40
40
  - `--authorize database` (default) reads the membership tables per statement; map each under `rls.memberships.scopes.<scope>` with `columns` for its id and its ancestors', and `via` so roles with `for` apply: a column name, or `{ value: 'staff' }` when every row has that kind (never add a generated column for it). `--authorize jwt` reads token claims and stays stale until the token refreshes; keep the token lifetime at 3600 seconds or less (PD019).
41
41
  - When the role column holds values that are not role keys, give the membership source `role: { column, map: { value: 'role' } }`; a value mapped to `null` grants nothing. `permdock rls explain` prints the tables, membership sources, custom roles table and triggers a generate run binds to, without writing a file. `rls.seeds: 'function'` writes the `role_permissions` rows as `<schema>.seed()` instead of inserts; run `select <schema>.seed();` after each deploy, and PD054 checks the function body.
42
42
  - `rls verify --introspect` checks each `security definer` helper by its full signature, so every overload is checked on its own. A helper finding id names the signature (`helper-definer:permdock.permdock_has(text):helper`); write `rls.verify.ignore` entries with it.
@@ -1 +0,0 @@
1
- export { n as runPowerSync } from "./powersync-DVbskWD9.js";
@@ -1 +0,0 @@
1
- export { n as runCloudPull } from "./pull-Er4aBHlL.js";
@@ -1 +0,0 @@
1
- export { t as run } from "./run-BlxXmaDS.js";
@@ -1 +0,0 @@
1
- export { t as runUsage } from "./usage-WYRc7Kqg.js";
@@ -1 +0,0 @@
1
- export { i as watchCollect } from "./watch-CTfSlIGD.js";