permdock 0.14.0 → 0.15.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 (60) hide show
  1. package/dist/{adoption-scan-BGtwGfoH.js → adoption-scan-DLel53Oa.js} +1 -1
  2. package/dist/{arazzo-9uhbmKf5.js → arazzo-CHK9NT6u.js} +1 -1
  3. package/dist/{arazzo-Bol9OgF-.js → arazzo-CL8CZnpL.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-1PVgMHOR.js → casbin-DRFwgVIP.js} +1 -1
  7. package/dist/{casbin-CBXD5rFx.js → casbin-iAEXjmca.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-eOtmuonF.js} +1 -1
  11. package/dist/{codemod-DWfH-KHk.js → codemod-D6NYu4AK.js} +2 -2
  12. package/dist/{codemod-BXLd4K3j.js → codemod-KEDj_3zl.js} +1 -1
  13. package/dist/{collect-lZgCKMe_.js → collect-DnXL4B8c.js} +1 -1
  14. package/dist/{completion-fJ19i8Jk.js → completion-DCza8aQY.js} +1 -1
  15. package/dist/{config-BYSk3iKV.js → config-BAM3a3z8.js} +1 -1
  16. package/dist/{config-ZZdtyQZy.js → config-CcLlvEYW.js} +2 -2
  17. package/dist/{config-report-BpztJ3HP.js → config-report-VZcQ0ZpS.js} +1 -1
  18. package/dist/{doctor-eNc7A_7I.js → doctor-CMqPKuN_.js} +12 -11
  19. package/dist/{doctor-NxCr_hba.js → doctor-gIO9WTYo.js} +2 -2
  20. package/dist/{import-efm6qPgx.js → import-DK_J8tVE.js} +1 -1
  21. package/dist/{init-BtWY1L0J.js → init-Buibrl9h.js} +3 -3
  22. package/dist/{init-D6rpLPwM.js → init-jLSyPMM7.js} +1 -1
  23. package/dist/{login-cLflB63Z.js → login-CB55O4Gv.js} +1 -1
  24. package/dist/{login-B8s6VI8Z.js → login-VVpkkund.js} +3 -3
  25. package/dist/{manifest-BWIR_EEB.js → manifest-CbEyvPN2.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-DDENzRbG.js → openapi-CleDZYVf.js} +1 -1
  30. package/dist/{openapi-CT55v3WD.js → openapi-eHZ5hwUJ.js} +2 -2
  31. package/dist/{output-3iB3_Hfg.js → output-BVZpQDad.js} +1 -1
  32. package/dist/{powersync-COjQTHRD.js → powersync-CFWILHjb.js} +2 -2
  33. package/dist/powersync-CnGzxvEl.js +1 -0
  34. package/dist/{powersync-DVbskWD9.js → powersync-uqYWxTjq.js} +1 -1
  35. package/dist/{pull-Er4aBHlL.js → pull-BBmCxnqX.js} +1 -1
  36. package/dist/pull-D6dloyrc.js +1 -0
  37. package/dist/react-native/index.d.ts +14 -1
  38. package/dist/react-native/index.js +46 -1
  39. package/dist/{rls-DY3Fc18U.js → rls-BwKXdVEW.js} +4 -4
  40. package/dist/{rls-DjjOHlKE.js → rls-yK2nZUcm.js} +1 -1
  41. package/dist/{run-BlxXmaDS.js → run-D5WIJfs7.js} +3 -3
  42. package/dist/run-DlQYPtPv.js +1 -0
  43. package/dist/{scan-CR_xm71k.js → scan-CzdMtHnq.js} +1 -1
  44. package/dist/{schema-CnrIP94n.js → schema-BDGEVO4g.js} +1 -1
  45. package/dist/{skills-uzsVqEi9.js → skills-BIsm1n26.js} +1 -1
  46. package/dist/{skills-CY-MY_Oj.js → skills-BZxPQ_09.js} +1 -1
  47. package/dist/{supautils-DZ95xxW-.js → supautils-CGk4YpvC.js} +14 -3
  48. package/dist/unplugin/index.js +1 -1
  49. package/dist/usage-D3KJyaNS.js +1 -0
  50. package/dist/{usage-Dm2hn0pS.js → usage-DBNrpwoT.js} +1 -1
  51. package/dist/{usage-WYRc7Kqg.js → usage-e6M1Mls1.js} +2 -2
  52. package/dist/{verify-CHQgLrtS.js → verify-CgIpJRcG.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/dist/powersync-k8lUeC37.js +0 -1
  57. package/dist/pull-BY1METGf.js +0 -1
  58. package/dist/run-DGSLvGty.js +0 -1
  59. package/dist/usage-CH7oISI6.js +0 -1
  60. 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";
@@ -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-BDGEVO4g.js";
11
11
  import { existsSync } from "node:fs";
12
12
  import { resolve } from "node:path";
13
13
  //#region src/cli/openapi/openapi.ts
@@ -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-CleDZYVf.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-DK_J8tVE.js")).runOpenapiImport({
126
126
  cwd: ctx.cwd,
127
127
  doc,
128
128
  out: fileOut("openapi import", flags.out),
@@ -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-CbEyvPN2.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) {
@@ -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-CnGzxvEl.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-CnGzxvEl.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-uqYWxTjq.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-CgIpJRcG.js";
23
23
  import { resolve } from "node:path";
24
24
  //#region src/cli/powersync/names.ts
25
25
  function ident(name) {
@@ -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-BVZpQDad.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";
@@ -0,0 +1 @@
1
+ export { n as runCloudPull } from "./pull-BBmCxnqX.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 };
@@ -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-BVZpQDad.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-BBmCxnqX.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-CgIpJRcG.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-CGk4YpvC.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";
@@ -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-BwKXdVEW.js")).runRls(input);
196
196
  }
197
197
  const generate = defineLeaf({
198
198
  flags: {
@@ -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-CbEyvPN2.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-BVZpQDad.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";
@@ -0,0 +1 @@
1
+ export { t as run } from "./run-D5WIJfs7.js";
@@ -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-DLel53Oa.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-BVZpQDad.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-BZxPQ_09.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-BVZpQDad.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-CgIpJRcG.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
@@ -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
@@ -0,0 +1 @@
1
+ export { t as runUsage } from "./usage-e6M1Mls1.js";
@@ -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-D3KJyaNS.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-BWIR_EEB.js";
10
- import { r as findingOf } from "./output-3iB3_Hfg.js";
9
+ import { t as DOCS_BASE } from "./manifest-CbEyvPN2.js";
10
+ import { r as findingOf } from "./output-BVZpQDad.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 { 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-CbEyvPN2.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.15.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-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";