@pramen/admin 0.0.44 → 0.0.46

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pramen/admin",
3
- "version": "0.0.44",
3
+ "version": "0.0.46",
4
4
  "description": "pramen admin dashboard — a Preact single-page browser client for the pramen admin HTTP API (tenants, schema, and generic row browse/edit).",
5
5
  "license": "MIT",
6
6
  "private": false,
package/src/api.ts CHANGED
@@ -68,8 +68,40 @@ async function call<T>(cfg: Config, path: string, init?: RequestInit): Promise<T
68
68
  return body.result as T;
69
69
  }
70
70
 
71
+ /** One registered Durable Object, the shape `GET /tenants` actually returns. Mirrors
72
+ * `DoRef` in @pramen/server, duplicated rather than imported because the admin is a
73
+ * standalone browser bundle that does not depend on the server package. */
74
+ interface TenantRef {
75
+ tenant: string;
76
+ partition: string;
77
+ }
78
+
79
+ /** Tenant names out of whatever `GET /tenants` answered with.
80
+ *
81
+ * The server returns `{tenant, partition}[]` — one entry per registered Durable Object,
82
+ * so a tenant with several partitions appears more than once. This call was typed
83
+ * `string[]` and the result fed straight into `<SelectItem>`, which put an OBJECT where
84
+ * React expected a child: it threw during render and blanked the entire dashboard for
85
+ * anyone whose deployment had even one tenant.
86
+ *
87
+ * Deduped, because the picker chooses a tenant and every partition of a tenant is the
88
+ * same choice here — the admin always talks to the default partition. Sorted so the
89
+ * order does not drift with KV listing order.
90
+ *
91
+ * A plain `string[]` is tolerated too. The admin is a client you point at arbitrary
92
+ * deployments — a freshly built bundle against a long-running Worker, say — so the
93
+ * server on the other end is not necessarily the version that produced this parser.
94
+ */
95
+ export function tenantNames(result: unknown): string[] {
96
+ if (!Array.isArray(result)) return [];
97
+ const names = result
98
+ .map((entry) => (typeof entry === "string" ? entry : (entry as Partial<TenantRef> | null)?.tenant))
99
+ .filter((name): name is string => typeof name === "string" && name.length > 0);
100
+ return [...new Set(names)].sort();
101
+ }
102
+
71
103
  export const api = {
72
- tenants: (cfg: Config) => call<string[]>(cfg, "/tenants"),
104
+ tenants: async (cfg: Config): Promise<string[]> => tenantNames(await call<unknown>(cfg, "/tenants")),
73
105
 
74
106
  schema: (cfg: Config, tenant: string) =>
75
107
  call<SchemaResult>(cfg, `/admin/schema?tenant=${encodeURIComponent(tenant)}`),