@lobb-js/lobb-ext-auth 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { onStartup, onRouteChange } from "./onStartup";
3
3
  import LoginPage from "./lib/components/pages/loginPage/index.svelte";
4
4
  import UserSettings from "./lib/components/pages/userSettings/index.svelte";
5
5
  import Settings from "./lib/components/pages/settings/index.svelte";
6
- import { isActionAllowed } from "../permissions";
6
+ import { isActionAllowed } from "./shared/permissions";
7
7
  // TODO we should export the extension object directly without a function at all. because we dont set configuration in here
8
8
  export default function extension(utils) {
9
9
  return {
@@ -57,7 +57,7 @@ export default function extension(utils) {
57
57
  {
58
58
  label: "Auth",
59
59
  icon: utils.components.Icons.Key,
60
- href: "/studio/extensions/auth/settings/users",
60
+ href: "/studio/extensions/auth/settings",
61
61
  represents: "auth_users",
62
62
  },
63
63
  ],
@@ -10,6 +10,15 @@
10
10
  const { Icons } = components;
11
11
  const auth_settings_page = $derived(props.utils.page.url.pathname.split("/")[5]);
12
12
  const isSmall = $derived(!props.utils.mediaQueries.sm.current);
13
+
14
+ // Bare /studio/extensions/auth/settings has no sub-page to render, which
15
+ // would leave the page blank. Redirect to the first sub-page (Users) so
16
+ // landing on the settings root always shows something useful.
17
+ $effect(() => {
18
+ if (!auth_settings_page) {
19
+ props.utils.goto("/studio/extensions/auth/settings/users");
20
+ }
21
+ });
13
22
  </script>
14
23
 
15
24
  <!-- TODO: add the sidebar in here to add these pages
@@ -1,2 +1,2 @@
1
- import type { CollectionPermissionActionsKeys, PermissionsConfig } from "./config/extensionConfigSchema.ts";
1
+ import type { CollectionPermissionActionsKeys, PermissionsConfig } from "../../config/extensionConfigSchema.ts";
2
2
  export declare function isActionAllowed(collection: string, action: CollectionPermissionActionsKeys, permissions: PermissionsConfig | undefined): boolean;
@@ -0,0 +1,35 @@
1
+ // Lives under studio/ for packaging reasons — `svelte-package` only bundles
2
+ // files inside its `--input` root, so anything the studio entry imports has
3
+ // to be reachable from there. The backend imports this same file via
4
+ // `../studio/shared/permissions` so there's still one source of truth.
5
+ //
6
+ // Anything in this folder must stay environment-neutral (no DOM, no Node-only
7
+ // APIs, no top-level side effects) so it runs the same way on both sides.
8
+ // Pure permission check shared by the server-side handlePolicy and the
9
+ // studio's auth.canAccess workflow. Walks the permissions tree and returns
10
+ // a yes/no — server-side then layers on guards/filters/error-throwing,
11
+ // client-side uses the answer to decide whether to render UI.
12
+ //
13
+ // An object value for action permission means "conditional access" (filter
14
+ // and/or fields restrictions). For UI gating that counts as allowed; the
15
+ // server enforces the actual restrictions on real requests.
16
+ export function isActionAllowed(collection, action, permissions) {
17
+ if (permissions === true)
18
+ return true;
19
+ if (!permissions)
20
+ return false;
21
+ const collPerm = permissions[collection];
22
+ if (collPerm === true)
23
+ return true;
24
+ if (!collPerm)
25
+ return false;
26
+ const actionPerm = collPerm[action];
27
+ if (actionPerm === true)
28
+ return true;
29
+ // A conditional grant must actually list at least one constraint
30
+ // (filter / fields / payloadGuard / mutate). Empty `{}` collapses back
31
+ // to "no grant" — explicit `true` is required for unconditional access.
32
+ return (typeof actionPerm === "object" &&
33
+ actionPerm !== null &&
34
+ Object.keys(actionPerm).length > 0);
35
+ }
@@ -4,7 +4,7 @@ import { onStartup, onRouteChange } from "./onStartup";
4
4
  import LoginPage from "./lib/components/pages/loginPage/index.svelte";
5
5
  import UserSettings from "./lib/components/pages/userSettings/index.svelte";
6
6
  import Settings from "./lib/components/pages/settings/index.svelte";
7
- import { isActionAllowed } from "../permissions";
7
+ import { isActionAllowed } from "./shared/permissions";
8
8
  import type { CollectionPermissionActionsKeys } from "../config/extensionConfigSchema";
9
9
 
10
10
  // TODO we should export the extension object directly without a function at all. because we dont set configuration in here
@@ -62,7 +62,7 @@ export default function extension(utils: ExtensionUtils): Extension {
62
62
  {
63
63
  label: "Auth",
64
64
  icon: utils.components.Icons.Key,
65
- href: "/studio/extensions/auth/settings/users",
65
+ href: "/studio/extensions/auth/settings",
66
66
  represents: "auth_users",
67
67
  },
68
68
  ],
@@ -10,6 +10,15 @@
10
10
  const { Icons } = components;
11
11
  const auth_settings_page = $derived(props.utils.page.url.pathname.split("/")[5]);
12
12
  const isSmall = $derived(!props.utils.mediaQueries.sm.current);
13
+
14
+ // Bare /studio/extensions/auth/settings has no sub-page to render, which
15
+ // would leave the page blank. Redirect to the first sub-page (Users) so
16
+ // landing on the settings root always shows something useful.
17
+ $effect(() => {
18
+ if (!auth_settings_page) {
19
+ props.utils.goto("/studio/extensions/auth/settings/users");
20
+ }
21
+ });
13
22
  </script>
14
23
 
15
24
  <!-- TODO: add the sidebar in here to add these pages
@@ -1,7 +1,15 @@
1
+ // Lives under studio/ for packaging reasons — `svelte-package` only bundles
2
+ // files inside its `--input` root, so anything the studio entry imports has
3
+ // to be reachable from there. The backend imports this same file via
4
+ // `../studio/shared/permissions` so there's still one source of truth.
5
+ //
6
+ // Anything in this folder must stay environment-neutral (no DOM, no Node-only
7
+ // APIs, no top-level side effects) so it runs the same way on both sides.
8
+
1
9
  import type {
2
10
  CollectionPermissionActionsKeys,
3
11
  PermissionsConfig,
4
- } from "./config/extensionConfigSchema.ts";
12
+ } from "../../config/extensionConfigSchema.ts";
5
13
 
6
14
  // Pure permission check shared by the server-side handlePolicy and the
7
15
  // studio's auth.canAccess workflow. Walks the permissions tree and returns
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "bun:test";
2
- import { isActionAllowed } from "../permissions.ts";
2
+ import { isActionAllowed } from "../studio/shared/permissions.ts";
3
3
 
4
4
  // Default-deny is the contract: only explicit `true` or an object that
5
5
  // actually lists at least one constraint counts as a grant. Anything empty
@@ -6,7 +6,7 @@ import type {
6
6
  PermissionsConfig,
7
7
  User,
8
8
  } from "../config/extensionConfigSchema.ts";
9
- import { isActionAllowed } from "../permissions.ts";
9
+ import { isActionAllowed } from "../studio/shared/permissions.ts";
10
10
 
11
11
  interface HandlePolicyOutput {
12
12
  filter?: Filter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobb-js/lobb-ext-auth",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -32,12 +32,12 @@
32
32
  "package": "svelte-package --input extensions/auth/studio"
33
33
  },
34
34
  "dependencies": {
35
- "@lobb-js/core": "^0.32.1",
35
+ "@lobb-js/core": "^0.33.0",
36
36
  "argon2": "^0.40.3",
37
37
  "hono": "^4.7.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@lobb-js/studio": "^0.29.1",
40
+ "@lobb-js/studio": "^0.31.0",
41
41
  "@lucide/svelte": "^0.563.1",
42
42
  "@sveltejs/adapter-node": "^5.5.4",
43
43
  "@sveltejs/kit": "^2.60.1",