@object-ui/app-shell 6.0.2 → 6.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @object-ui/app-shell — Changelog
2
2
 
3
+ ## 6.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 58f0af6: Fix marketplace install dialog showing "No environments found" even when the
8
+ signed-in user has cloud environments. Cloud's data API returns rows under
9
+ `records`, not `data`/`items`; the dialog now reads the correct key. As a
10
+ hardening pass, also filter `sys_member` rows by the caller's session
11
+ `user_id` so a leaky data endpoint cannot widen the install target list to
12
+ other tenants' organizations.
13
+ - @object-ui/types@6.0.3
14
+ - @object-ui/core@6.0.3
15
+ - @object-ui/i18n@6.0.3
16
+ - @object-ui/react@6.0.3
17
+ - @object-ui/components@6.0.3
18
+ - @object-ui/fields@6.0.3
19
+ - @object-ui/layout@6.0.3
20
+ - @object-ui/data-objectstack@6.0.3
21
+ - @object-ui/auth@6.0.3
22
+ - @object-ui/permissions@6.0.3
23
+ - @object-ui/collaboration@6.0.3
24
+ - @object-ui/providers@6.0.3
25
+
3
26
  ## 6.0.2
4
27
 
5
28
  ### Patch Changes
@@ -108,6 +108,11 @@ export declare function listCloudEnvironments(): Promise<CloudEnvironment[]>;
108
108
  *
109
109
  * Returns an empty set on 401 / network failure so the install dialog
110
110
  * can render a clean "no installable environments" state.
111
+ *
112
+ * Hardening: we *always* re-filter rows by the caller's session
113
+ * `user_id` because the data API currently returns sys_member rows
114
+ * without per-caller scoping. Without this, the dialog would pick up
115
+ * every org in the system and offer their envs as install targets.
111
116
  */
112
117
  export declare function listInstallableOrgIds(): Promise<Set<string>>;
113
118
  export declare function cloudInstallDeepLink(packageId: string): string;
@@ -94,7 +94,9 @@ export async function listCloudEnvironments() {
94
94
  throw err;
95
95
  }
96
96
  const payload = await res.json().catch(() => ({}));
97
- const rows = payload?.data ?? payload?.items ?? payload ?? [];
97
+ // Cloud's data API returns `{ object, records, total, hasMore }`.
98
+ // We keep `data` / `items` as fallbacks for older builds.
99
+ const rows = payload?.records ?? payload?.data ?? payload?.items ?? payload ?? [];
98
100
  return Array.isArray(rows) ? rows : [];
99
101
  }
100
102
  /**
@@ -105,10 +107,30 @@ export async function listCloudEnvironments() {
105
107
  *
106
108
  * Returns an empty set on 401 / network failure so the install dialog
107
109
  * can render a clean "no installable environments" state.
110
+ *
111
+ * Hardening: we *always* re-filter rows by the caller's session
112
+ * `user_id` because the data API currently returns sys_member rows
113
+ * without per-caller scoping. Without this, the dialog would pick up
114
+ * every org in the system and offer their envs as install targets.
108
115
  */
109
116
  export async function listInstallableOrgIds() {
110
117
  const base = getCloudBase() || SERVER_URL;
111
- // sys_member rows are scoped to the caller; better-auth-managed table.
118
+ let meId = null;
119
+ try {
120
+ const meRes = await fetch(`${base}/api/v1/auth/get-session`, {
121
+ credentials: 'include',
122
+ headers: { 'Accept': 'application/json' },
123
+ });
124
+ if (meRes.ok) {
125
+ const meBody = await meRes.json().catch(() => ({}));
126
+ meId = meBody?.user?.id ?? null;
127
+ }
128
+ }
129
+ catch {
130
+ /* fall through — meId stays null and we return empty set */
131
+ }
132
+ if (!meId)
133
+ return new Set();
112
134
  const url = `${base}/api/v1/data/sys_member?limit=200`;
113
135
  let payload = null;
114
136
  try {
@@ -123,11 +145,14 @@ export async function listInstallableOrgIds() {
123
145
  catch {
124
146
  return new Set();
125
147
  }
126
- const rows = payload?.data ?? payload?.items ?? payload ?? [];
148
+ const rows = payload?.records ?? payload?.data ?? payload?.items ?? payload ?? [];
127
149
  if (!Array.isArray(rows))
128
150
  return new Set();
129
151
  const ids = new Set();
130
152
  for (const row of rows) {
153
+ const rowUserId = String(row?.user_id ?? row?.userId ?? '');
154
+ if (rowUserId !== meId)
155
+ continue;
131
156
  const role = String(row?.role ?? '').toLowerCase();
132
157
  const orgId = row?.organization_id ?? row?.organizationId;
133
158
  if (orgId && (role === 'owner' || role === 'admin')) {
@@ -2,18 +2,17 @@
2
2
  * Resolve the absolute URL of the console "home" route used after switching
3
3
  * the active organization.
4
4
  *
5
- * Earlier versions concatenated `import.meta.env.BASE_URL` to
6
- * `window.location.origin` ("`${origin}${base}home`"). That was correct for
7
- * apps built with an absolute Vite base (e.g. `base: '/_console/'`), but
8
- * silently broken for portable builds that ship with `base: './'` — the
9
- * resulting URL `https://host./home` has a trailing-dot host (`host.` is a
10
- * fully-qualified-domain marker the browser keeps) AND drops the mount
11
- * prefix, landing the user on a 404.
5
+ * History:
6
+ * - `${origin}${import.meta.env.BASE_URL}home` broke portable builds
7
+ * (`base: './'`), producing `https://host./home` — trailing-dot host.
8
+ * - `new URL('home', document.baseURI)` fixed that, but `document.baseURI`
9
+ * falls back to the current page URL when no `<base>` tag is present.
10
+ * From `/home/home/` that resolves to `/home/home/home`, and each
11
+ * subsequent navigation appended another `/home` segment.
12
12
  *
13
- * The fix is to resolve `home` against `document.baseURI`, which already
14
- * accounts for any `<base href="...">` injected by the host page. This works
15
- * for both `<base href="/_console/">` (tenant deployments) and
16
- * `<base href="/">` (root-mount deployments), and also for hosts that omit
17
- * `<base>` entirely (falls back to the current document URL's directory).
13
+ * The robust resolution: read `<base href>` explicitly. When present it
14
+ * carries the deployment mount (`/_console/`, `/`, or `./`); when absent
15
+ * we resolve against the document origin root, which is independent of the
16
+ * current SPA route.
18
17
  */
19
18
  export declare function resolveHomeUrl(baseURI?: string): string;
@@ -2,20 +2,26 @@
2
2
  * Resolve the absolute URL of the console "home" route used after switching
3
3
  * the active organization.
4
4
  *
5
- * Earlier versions concatenated `import.meta.env.BASE_URL` to
6
- * `window.location.origin` ("`${origin}${base}home`"). That was correct for
7
- * apps built with an absolute Vite base (e.g. `base: '/_console/'`), but
8
- * silently broken for portable builds that ship with `base: './'` — the
9
- * resulting URL `https://host./home` has a trailing-dot host (`host.` is a
10
- * fully-qualified-domain marker the browser keeps) AND drops the mount
11
- * prefix, landing the user on a 404.
5
+ * History:
6
+ * - `${origin}${import.meta.env.BASE_URL}home` broke portable builds
7
+ * (`base: './'`), producing `https://host./home` — trailing-dot host.
8
+ * - `new URL('home', document.baseURI)` fixed that, but `document.baseURI`
9
+ * falls back to the current page URL when no `<base>` tag is present.
10
+ * From `/home/home/` that resolves to `/home/home/home`, and each
11
+ * subsequent navigation appended another `/home` segment.
12
12
  *
13
- * The fix is to resolve `home` against `document.baseURI`, which already
14
- * accounts for any `<base href="...">` injected by the host page. This works
15
- * for both `<base href="/_console/">` (tenant deployments) and
16
- * `<base href="/">` (root-mount deployments), and also for hosts that omit
17
- * `<base>` entirely (falls back to the current document URL's directory).
13
+ * The robust resolution: read `<base href>` explicitly. When present it
14
+ * carries the deployment mount (`/_console/`, `/`, or `./`); when absent
15
+ * we resolve against the document origin root, which is independent of the
16
+ * current SPA route.
18
17
  */
19
- export function resolveHomeUrl(baseURI = document.baseURI) {
20
- return new URL('home', baseURI).toString();
18
+ export function resolveHomeUrl(baseURI) {
19
+ if (baseURI !== undefined) {
20
+ return new URL('home', baseURI).toString();
21
+ }
22
+ const baseHref = document.querySelector('base')?.getAttribute('href');
23
+ const root = baseHref
24
+ ? new URL(baseHref, window.location.origin)
25
+ : new URL('/', window.location.origin);
26
+ return new URL('home', root).toString();
21
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/app-shell",
3
- "version": "6.0.2",
3
+ "version": "6.0.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Minimal application shell for ObjectUI - framework-agnostic rendering engine",
@@ -28,35 +28,35 @@
28
28
  "@sentry/react": "^8.55.2",
29
29
  "lucide-react": "^1.16.0",
30
30
  "sonner": "^2.0.7",
31
- "@object-ui/auth": "6.0.2",
32
- "@object-ui/collaboration": "6.0.2",
33
- "@object-ui/components": "6.0.2",
34
- "@object-ui/core": "6.0.2",
35
- "@object-ui/data-objectstack": "6.0.2",
36
- "@object-ui/fields": "6.0.2",
37
- "@object-ui/i18n": "6.0.2",
38
- "@object-ui/layout": "6.0.2",
39
- "@object-ui/permissions": "6.0.2",
40
- "@object-ui/providers": "6.0.2",
41
- "@object-ui/react": "6.0.2",
42
- "@object-ui/types": "6.0.2"
31
+ "@object-ui/auth": "6.0.3",
32
+ "@object-ui/collaboration": "6.0.3",
33
+ "@object-ui/components": "6.0.3",
34
+ "@object-ui/core": "6.0.3",
35
+ "@object-ui/data-objectstack": "6.0.3",
36
+ "@object-ui/fields": "6.0.3",
37
+ "@object-ui/i18n": "6.0.3",
38
+ "@object-ui/layout": "6.0.3",
39
+ "@object-ui/permissions": "6.0.3",
40
+ "@object-ui/providers": "6.0.3",
41
+ "@object-ui/react": "6.0.3",
42
+ "@object-ui/types": "6.0.3"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "react": "^18.0.0 || ^19.0.0",
46
46
  "react-dom": "^18.0.0 || ^19.0.0",
47
47
  "react-router-dom": "^6.0.0 || ^7.0.0",
48
- "@object-ui/plugin-calendar": "^6.0.2",
49
- "@object-ui/plugin-charts": "^6.0.2",
50
- "@object-ui/plugin-chatbot": "^6.0.2",
51
- "@object-ui/plugin-dashboard": "^6.0.2",
52
- "@object-ui/plugin-designer": "^6.0.2",
53
- "@object-ui/plugin-detail": "^6.0.2",
54
- "@object-ui/plugin-form": "^6.0.2",
55
- "@object-ui/plugin-grid": "^6.0.2",
56
- "@object-ui/plugin-kanban": "^6.0.2",
57
- "@object-ui/plugin-list": "^6.0.2",
58
- "@object-ui/plugin-report": "^6.0.2",
59
- "@object-ui/plugin-view": "^6.0.2"
48
+ "@object-ui/plugin-calendar": "^6.0.3",
49
+ "@object-ui/plugin-charts": "^6.0.3",
50
+ "@object-ui/plugin-chatbot": "^6.0.3",
51
+ "@object-ui/plugin-dashboard": "^6.0.3",
52
+ "@object-ui/plugin-designer": "^6.0.3",
53
+ "@object-ui/plugin-detail": "^6.0.3",
54
+ "@object-ui/plugin-form": "^6.0.3",
55
+ "@object-ui/plugin-grid": "^6.0.3",
56
+ "@object-ui/plugin-kanban": "^6.0.3",
57
+ "@object-ui/plugin-list": "^6.0.3",
58
+ "@object-ui/plugin-report": "^6.0.3",
59
+ "@object-ui/plugin-view": "^6.0.3"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^25.9.0",