@undefineds.co/xpod 0.2.39 → 0.2.40

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.
@@ -4,6 +4,7 @@ export interface PodLookupResult {
4
4
  accountId: string;
5
5
  baseUrl: string;
6
6
  webId?: string;
7
+ webIds?: string[];
7
8
  nodeId?: string;
8
9
  edgeNodeId?: string;
9
10
  }
@@ -24,6 +25,7 @@ export interface PodMigrationStatus {
24
25
  export declare class PodLookupRepository {
25
26
  private readonly db;
26
27
  private readonly kvTableName;
28
+ private readonly indexedStoreTableName;
27
29
  private readonly usageTableName;
28
30
  constructor(db: IdentityDatabase, kvTableName?: string);
29
31
  /**
@@ -69,4 +71,11 @@ export declare class PodLookupRepository {
69
71
  * id/account_id/base_url columns (used by some unit tests and older schemas).
70
72
  */
71
73
  private getAllPods;
74
+ /**
75
+ * Older Xpod/CSS deployments may have used the IndexedStorage-compatible
76
+ * identity_store table instead of CSS's WrappedIndexedStorage JSON tree in
77
+ * internal_kv. Keep this as a read-only compatibility source so hosted WebID
78
+ * profile lookup still works after storage implementation changes.
79
+ */
80
+ private getPodsFromIndexedStore;
72
81
  }
@@ -14,6 +14,7 @@ class PodLookupRepository {
14
14
  constructor(db, kvTableName) {
15
15
  this.db = db;
16
16
  this.kvTableName = kvTableName ?? 'internal_kv';
17
+ this.indexedStoreTableName = 'identity_store';
17
18
  this.usageTableName = 'identity_pod_usage';
18
19
  }
19
20
  /**
@@ -51,7 +52,16 @@ class PodLookupRepository {
51
52
  return undefined;
52
53
  }
53
54
  const pods = await this.getAllPods();
54
- return pods.find((pod) => normalizeWebId(pod.webId) === normalized);
55
+ for (const pod of pods) {
56
+ const matchedWebId = getPodWebIds(pod).find((candidate) => normalizeWebId(candidate) === normalized);
57
+ if (matchedWebId) {
58
+ return {
59
+ ...pod,
60
+ webId: matchedWebId,
61
+ };
62
+ }
63
+ }
64
+ return undefined;
55
65
  }
56
66
  /**
57
67
  * List Pods for a specific account.
@@ -131,9 +141,10 @@ class PodLookupRepository {
131
141
  const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
132
142
  SELECT key, value FROM ${kvTableId}
133
143
  WHERE key LIKE 'accounts/data/%'
144
+ OR key LIKE '/.internal/accounts/data/%'
134
145
  `);
135
146
  const pods = [];
136
- for (const row of result.rows) {
147
+ for (const row of result?.rows ?? []) {
137
148
  if (row.id && row.account_id && row.base_url) {
138
149
  pods.push({
139
150
  podId: String(row.id),
@@ -148,7 +159,10 @@ class PodLookupRepository {
148
159
  continue;
149
160
  }
150
161
  try {
151
- const accountId = row.key.replace('accounts/data/', '');
162
+ const accountId = extractAccountIdFromAccountDataKey(row.key);
163
+ if (!accountId) {
164
+ continue;
165
+ }
152
166
  const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;
153
167
  const podMap = data['**pod**'] || data.pod || {};
154
168
  const webIds = extractAccountWebIds(data);
@@ -165,6 +179,7 @@ class PodLookupRepository {
165
179
  accountId,
166
180
  baseUrl: pod.baseUrl,
167
181
  webId: dedupeStrings(podWebIds)[0],
182
+ ...webIdsProperty(podWebIds),
168
183
  nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,
169
184
  edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,
170
185
  });
@@ -175,10 +190,95 @@ class PodLookupRepository {
175
190
  // Skip malformed entries.
176
191
  }
177
192
  }
193
+ return mergePodLookupResults([
194
+ ...pods,
195
+ ...await this.getPodsFromIndexedStore(),
196
+ ]);
197
+ }
198
+ /**
199
+ * Older Xpod/CSS deployments may have used the IndexedStorage-compatible
200
+ * identity_store table instead of CSS's WrappedIndexedStorage JSON tree in
201
+ * internal_kv. Keep this as a read-only compatibility source so hosted WebID
202
+ * profile lookup still works after storage implementation changes.
203
+ */
204
+ async getPodsFromIndexedStore() {
205
+ const storeTableId = drizzle_orm_1.sql.identifier([this.indexedStoreTableName]);
206
+ let result;
207
+ try {
208
+ result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
209
+ SELECT container, id, payload FROM ${storeTableId}
210
+ WHERE container IN ('pod', 'owner', 'webIdLink')
211
+ `);
212
+ }
213
+ catch {
214
+ return [];
215
+ }
216
+ const podPayloads = new Map();
217
+ const ownerWebIdsByPodId = new Map();
218
+ const webIdsByAccountId = new Map();
219
+ for (const row of result?.rows ?? []) {
220
+ if (!row.id || !row.container) {
221
+ continue;
222
+ }
223
+ const payload = parsePayloadRecord(row.payload);
224
+ if (!payload) {
225
+ continue;
226
+ }
227
+ if (row.container === 'pod') {
228
+ podPayloads.set(row.id, payload);
229
+ continue;
230
+ }
231
+ if (row.container === 'owner') {
232
+ const podId = stringValue(payload.podId);
233
+ const webId = stringValue(payload.webId);
234
+ if (podId && webId) {
235
+ appendMapValue(ownerWebIdsByPodId, podId, webId);
236
+ }
237
+ continue;
238
+ }
239
+ if (row.container === 'webIdLink') {
240
+ const accountId = stringValue(payload.accountId);
241
+ const webId = stringValue(payload.webId);
242
+ if (accountId && webId) {
243
+ appendMapValue(webIdsByAccountId, accountId, webId);
244
+ }
245
+ }
246
+ }
247
+ const pods = [];
248
+ for (const [podId, pod] of podPayloads) {
249
+ const baseUrl = stringValue(pod.baseUrl);
250
+ const accountId = stringValue(pod.accountId);
251
+ if (!baseUrl || !accountId) {
252
+ continue;
253
+ }
254
+ const podWebIds = dedupeStrings([
255
+ stringValue(pod.webId),
256
+ ...(ownerWebIdsByPodId.get(podId) ?? []),
257
+ ...(webIdsByAccountId.get(accountId) ?? []),
258
+ ].filter((value) => typeof value === 'string'));
259
+ pods.push({
260
+ podId,
261
+ accountId,
262
+ baseUrl,
263
+ webId: podWebIds[0],
264
+ ...webIdsProperty(podWebIds),
265
+ nodeId: stringValue(pod.nodeId),
266
+ edgeNodeId: stringValue(pod.edgeNodeId),
267
+ });
268
+ }
178
269
  return pods;
179
270
  }
180
271
  }
181
272
  exports.PodLookupRepository = PodLookupRepository;
273
+ function extractAccountIdFromAccountDataKey(key) {
274
+ const marker = 'accounts/data/';
275
+ const index = key.indexOf(marker);
276
+ if (index < 0) {
277
+ return undefined;
278
+ }
279
+ const accountId = key.slice(index + marker.length).replace(/\.json$/u, '');
280
+ return accountId || undefined;
281
+ }
182
282
  function extractAccountWebIds(data) {
183
283
  if (!data || typeof data !== 'object') {
184
284
  return [];
@@ -227,4 +327,61 @@ function normalizeWebId(webId) {
227
327
  function dedupeStrings(values) {
228
328
  return [...new Set(values)];
229
329
  }
330
+ function getPodWebIds(pod) {
331
+ return dedupeStrings([
332
+ pod.webId,
333
+ ...(pod.webIds ?? []),
334
+ ].filter((value) => typeof value === 'string' && value.length > 0));
335
+ }
336
+ function webIdsProperty(values) {
337
+ const webIds = dedupeStrings(values);
338
+ return webIds.length > 1 ? { webIds } : {};
339
+ }
340
+ function mergePodLookupResults(values) {
341
+ const byPodId = new Map();
342
+ for (const value of values) {
343
+ const existing = byPodId.get(value.podId);
344
+ if (!existing) {
345
+ byPodId.set(value.podId, value);
346
+ continue;
347
+ }
348
+ const webIds = dedupeStrings([
349
+ ...getPodWebIds(existing),
350
+ ...getPodWebIds(value),
351
+ ]);
352
+ byPodId.set(value.podId, {
353
+ ...existing,
354
+ baseUrl: existing.baseUrl || value.baseUrl,
355
+ accountId: existing.accountId || value.accountId,
356
+ webId: webIds[0],
357
+ ...webIdsProperty(webIds),
358
+ nodeId: existing.nodeId ?? value.nodeId,
359
+ edgeNodeId: existing.edgeNodeId ?? value.edgeNodeId,
360
+ });
361
+ }
362
+ return [...byPodId.values()];
363
+ }
364
+ function parsePayloadRecord(value) {
365
+ if (!value) {
366
+ return undefined;
367
+ }
368
+ if (typeof value === 'string') {
369
+ try {
370
+ const parsed = JSON.parse(value);
371
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
372
+ }
373
+ catch {
374
+ return undefined;
375
+ }
376
+ }
377
+ return typeof value === 'object' ? value : undefined;
378
+ }
379
+ function stringValue(value) {
380
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
381
+ }
382
+ function appendMapValue(map, key, value) {
383
+ const values = map.get(key) ?? [];
384
+ values.push(value);
385
+ map.set(key, values);
386
+ }
230
387
  //# sourceMappingURL=PodLookupRepository.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PodLookupRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/PodLookupRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { sql } from 'drizzle-orm';\nimport { type IdentityDatabase, executeQuery, executeStatement } from './db';\n\nexport interface PodLookupResult {\n podId: string;\n accountId: string;\n baseUrl: string;\n webId?: string;\n nodeId?: string;\n edgeNodeId?: string;\n}\n\nexport interface PodMigrationStatus {\n podId: string;\n nodeId?: string;\n migrationStatus?: 'syncing' | 'done' | null;\n migrationTargetNode?: string;\n migrationProgress?: number;\n}\n\ninterface InternalKvRow {\n key?: string;\n value?: string;\n id?: string;\n account_id?: string;\n base_url?: string;\n node_id?: string;\n edge_node_id?: string;\n}\n\n/**\n * Repository for Pod lookup operations.\n *\n * Reads Pod data from CSS's internal_kv table where account data is stored.\n * CSS stores account data at key \"accounts/data/{accountId}\" with Pod info\n * nested in the \"**pod**\" field.\n */\nexport class PodLookupRepository {\n private readonly kvTableName: string;\n private readonly usageTableName: string;\n\n public constructor(\n private readonly db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.kvTableName = kvTableName ?? 'internal_kv';\n this.usageTableName = 'identity_pod_usage';\n }\n\n /**\n * Find Pod by resource path (matches longest baseUrl prefix).\n */\n public async findByResourceIdentifier(resourcePath: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n\n let bestMatch: PodLookupResult | undefined;\n let bestLength = 0;\n\n for (const pod of pods) {\n if (resourcePath.startsWith(pod.baseUrl) && pod.baseUrl.length > bestLength) {\n bestMatch = pod;\n bestLength = pod.baseUrl.length;\n }\n }\n\n return bestMatch;\n }\n\n /**\n * Get Pod by ID.\n */\n public async findById(podId: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n return pods.find((p) => p.podId === podId);\n }\n\n /**\n * Find Pod by a linked WebID URL.\n *\n * CSS account data stores WebID links separately from Pod base URLs. This is\n * the precise lookup for IdP/SP split deployments where the WebID path does\n * not have to match the storage base URL.\n */\n public async findByWebId(webId: string): Promise<PodLookupResult | undefined> {\n const normalized = normalizeWebId(webId);\n if (!normalized) {\n return undefined;\n }\n\n const pods = await this.getAllPods();\n return pods.find((pod) => normalizeWebId(pod.webId) === normalized);\n }\n\n /**\n * List Pods for a specific account.\n */\n public async listByAccountId(accountId: string): Promise<PodLookupResult[]> {\n const pods = await this.getAllPods();\n return pods.filter((pod) => pod.accountId === accountId);\n }\n\n /**\n * Get migration status for a Pod from identity_pod_usage table.\n */\n public async getMigrationStatus(podId: string): Promise<PodMigrationStatus | undefined> {\n try {\n const tableId = sql.identifier([this.usageTableName]);\n const result = await executeQuery<{\n pod_id?: string;\n id?: string;\n node_id?: string | null;\n migration_status?: string | null;\n migration_target_node?: string | null;\n migration_progress?: number | null;\n }>(this.db, sql`\n SELECT pod_id, node_id, migration_status, migration_target_node, migration_progress\n FROM ${tableId}\n WHERE pod_id = ${podId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0];\n return {\n podId: row.pod_id ?? row.id ?? podId,\n nodeId: row.node_id ?? undefined,\n migrationStatus: row.migration_status as 'syncing' | 'done' | null | undefined,\n migrationTargetNode: row.migration_target_node ?? undefined,\n migrationProgress: row.migration_progress ?? undefined,\n };\n } catch {\n // Table might not exist.\n return undefined;\n }\n }\n\n /**\n * Set the nodeId for a Pod in identity_pod_usage table.\n */\n public async setNodeId(podId: string, nodeId: string): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, node_id)\n VALUES (${podId}, '', ${nodeId})\n ON CONFLICT (pod_id) DO UPDATE SET node_id = ${nodeId}\n `);\n }\n\n /**\n * Update migration status for a Pod in identity_pod_usage table.\n */\n public async setMigrationStatus(\n podId: string,\n status: 'syncing' | 'done' | null,\n targetNode?: string | null,\n progress?: number | null,\n ): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, migration_status, migration_target_node, migration_progress)\n VALUES (${podId}, '', ${status}, ${targetNode ?? null}, ${progress ?? 0})\n ON CONFLICT (pod_id) DO UPDATE SET\n migration_status = ${status},\n migration_target_node = ${targetNode ?? null},\n migration_progress = ${progress ?? 0}\n `);\n }\n\n /**\n * List all pods.\n */\n public async listAllPods(): Promise<PodLookupResult[]> {\n return this.getAllPods();\n }\n\n /**\n * Extract all pods from CSS's internal_kv storage.\n *\n * It keeps backward compatibility with legacy rows that already expose\n * id/account_id/base_url columns (used by some unit tests and older schemas).\n */\n private async getAllPods(): Promise<PodLookupResult[]> {\n const kvTableId = sql.identifier([this.kvTableName]);\n\n const result = await executeQuery<InternalKvRow>(this.db, sql`\n SELECT key, value FROM ${kvTableId}\n WHERE key LIKE 'accounts/data/%'\n `);\n\n const pods: PodLookupResult[] = [];\n\n for (const row of result.rows) {\n if (row.id && row.account_id && row.base_url) {\n pods.push({\n podId: String(row.id),\n accountId: String(row.account_id),\n baseUrl: String(row.base_url),\n nodeId: row.node_id ? String(row.node_id) : undefined,\n edgeNodeId: row.edge_node_id ? String(row.edge_node_id) : undefined,\n });\n continue;\n }\n\n if (!row.key || row.value === undefined) {\n continue;\n }\n\n try {\n const accountId = row.key.replace('accounts/data/', '');\n const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;\n\n const podMap = (data as any)['**pod**'] || (data as any).pod || {};\n const webIds = extractAccountWebIds(data);\n\n for (const [podId, podData] of Object.entries(podMap)) {\n const pod = podData as Record<string, unknown>;\n if (pod.baseUrl && typeof pod.baseUrl === 'string') {\n const podWebIds = [\n typeof pod.webId === 'string' ? pod.webId : undefined,\n ...extractPodOwnerWebIds(pod),\n ...webIds,\n ].filter((value): value is string => typeof value === 'string');\n pods.push({\n podId,\n accountId,\n baseUrl: pod.baseUrl,\n webId: dedupeStrings(podWebIds)[0],\n nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,\n edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,\n });\n }\n }\n } catch {\n // Skip malformed entries.\n }\n }\n\n return pods;\n }\n}\n\nfunction extractAccountWebIds(data: unknown): string[] {\n if (!data || typeof data !== 'object') {\n return [];\n }\n\n const record = data as Record<string, unknown>;\n const linkMap = record['**webIdLink**'] || record.webIdLink || {};\n if (!linkMap || typeof linkMap !== 'object') {\n return [];\n }\n\n return Object.values(linkMap as Record<string, unknown>)\n .map((value) => {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n const webId = (value as Record<string, unknown>).webId;\n return typeof webId === 'string' ? webId : undefined;\n })\n .filter((value): value is string => typeof value === 'string');\n}\n\nfunction extractPodOwnerWebIds(pod: Record<string, unknown>): string[] {\n const ownerMap = pod['**owner**'] || pod.owner || {};\n if (!ownerMap || typeof ownerMap !== 'object') {\n return [];\n }\n\n return Object.values(ownerMap as Record<string, unknown>)\n .map((value) => {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n const webId = (value as Record<string, unknown>).webId;\n return typeof webId === 'string' ? webId : undefined;\n })\n .filter((value): value is string => typeof value === 'string');\n}\n\nfunction normalizeWebId(webId: string | undefined): string | undefined {\n if (!webId) {\n return undefined;\n }\n try {\n return new URL(webId).toString();\n } catch {\n return webId;\n }\n}\n\nfunction dedupeStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n"]}
1
+ {"version":3,"file":"PodLookupRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/PodLookupRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { sql } from 'drizzle-orm';\nimport { type IdentityDatabase, executeQuery, executeStatement } from './db';\n\nexport interface PodLookupResult {\n podId: string;\n accountId: string;\n baseUrl: string;\n webId?: string;\n webIds?: string[];\n nodeId?: string;\n edgeNodeId?: string;\n}\n\nexport interface PodMigrationStatus {\n podId: string;\n nodeId?: string;\n migrationStatus?: 'syncing' | 'done' | null;\n migrationTargetNode?: string;\n migrationProgress?: number;\n}\n\ninterface InternalKvRow {\n key?: string;\n value?: string;\n id?: string;\n account_id?: string;\n base_url?: string;\n node_id?: string;\n edge_node_id?: string;\n}\n\n/**\n * Repository for Pod lookup operations.\n *\n * Reads Pod data from CSS's internal_kv table where account data is stored.\n * CSS stores account data at key \"accounts/data/{accountId}\" with Pod info\n * nested in the \"**pod**\" field.\n */\nexport class PodLookupRepository {\n private readonly kvTableName: string;\n private readonly indexedStoreTableName: string;\n private readonly usageTableName: string;\n\n public constructor(\n private readonly db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.kvTableName = kvTableName ?? 'internal_kv';\n this.indexedStoreTableName = 'identity_store';\n this.usageTableName = 'identity_pod_usage';\n }\n\n /**\n * Find Pod by resource path (matches longest baseUrl prefix).\n */\n public async findByResourceIdentifier(resourcePath: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n\n let bestMatch: PodLookupResult | undefined;\n let bestLength = 0;\n\n for (const pod of pods) {\n if (resourcePath.startsWith(pod.baseUrl) && pod.baseUrl.length > bestLength) {\n bestMatch = pod;\n bestLength = pod.baseUrl.length;\n }\n }\n\n return bestMatch;\n }\n\n /**\n * Get Pod by ID.\n */\n public async findById(podId: string): Promise<PodLookupResult | undefined> {\n const pods = await this.getAllPods();\n return pods.find((p) => p.podId === podId);\n }\n\n /**\n * Find Pod by a linked WebID URL.\n *\n * CSS account data stores WebID links separately from Pod base URLs. This is\n * the precise lookup for IdP/SP split deployments where the WebID path does\n * not have to match the storage base URL.\n */\n public async findByWebId(webId: string): Promise<PodLookupResult | undefined> {\n const normalized = normalizeWebId(webId);\n if (!normalized) {\n return undefined;\n }\n\n const pods = await this.getAllPods();\n for (const pod of pods) {\n const matchedWebId = getPodWebIds(pod).find((candidate) => normalizeWebId(candidate) === normalized);\n if (matchedWebId) {\n return {\n ...pod,\n webId: matchedWebId,\n };\n }\n }\n return undefined;\n }\n\n /**\n * List Pods for a specific account.\n */\n public async listByAccountId(accountId: string): Promise<PodLookupResult[]> {\n const pods = await this.getAllPods();\n return pods.filter((pod) => pod.accountId === accountId);\n }\n\n /**\n * Get migration status for a Pod from identity_pod_usage table.\n */\n public async getMigrationStatus(podId: string): Promise<PodMigrationStatus | undefined> {\n try {\n const tableId = sql.identifier([this.usageTableName]);\n const result = await executeQuery<{\n pod_id?: string;\n id?: string;\n node_id?: string | null;\n migration_status?: string | null;\n migration_target_node?: string | null;\n migration_progress?: number | null;\n }>(this.db, sql`\n SELECT pod_id, node_id, migration_status, migration_target_node, migration_progress\n FROM ${tableId}\n WHERE pod_id = ${podId}\n LIMIT 1\n `);\n\n if (result.rows.length === 0) {\n return undefined;\n }\n const row = result.rows[0];\n return {\n podId: row.pod_id ?? row.id ?? podId,\n nodeId: row.node_id ?? undefined,\n migrationStatus: row.migration_status as 'syncing' | 'done' | null | undefined,\n migrationTargetNode: row.migration_target_node ?? undefined,\n migrationProgress: row.migration_progress ?? undefined,\n };\n } catch {\n // Table might not exist.\n return undefined;\n }\n }\n\n /**\n * Set the nodeId for a Pod in identity_pod_usage table.\n */\n public async setNodeId(podId: string, nodeId: string): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, node_id)\n VALUES (${podId}, '', ${nodeId})\n ON CONFLICT (pod_id) DO UPDATE SET node_id = ${nodeId}\n `);\n }\n\n /**\n * Update migration status for a Pod in identity_pod_usage table.\n */\n public async setMigrationStatus(\n podId: string,\n status: 'syncing' | 'done' | null,\n targetNode?: string | null,\n progress?: number | null,\n ): Promise<void> {\n const tableId = sql.identifier([this.usageTableName]);\n await executeStatement(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, migration_status, migration_target_node, migration_progress)\n VALUES (${podId}, '', ${status}, ${targetNode ?? null}, ${progress ?? 0})\n ON CONFLICT (pod_id) DO UPDATE SET\n migration_status = ${status},\n migration_target_node = ${targetNode ?? null},\n migration_progress = ${progress ?? 0}\n `);\n }\n\n /**\n * List all pods.\n */\n public async listAllPods(): Promise<PodLookupResult[]> {\n return this.getAllPods();\n }\n\n /**\n * Extract all pods from CSS's internal_kv storage.\n *\n * It keeps backward compatibility with legacy rows that already expose\n * id/account_id/base_url columns (used by some unit tests and older schemas).\n */\n private async getAllPods(): Promise<PodLookupResult[]> {\n const kvTableId = sql.identifier([this.kvTableName]);\n\n const result = await executeQuery<InternalKvRow>(this.db, sql`\n SELECT key, value FROM ${kvTableId}\n WHERE key LIKE 'accounts/data/%'\n OR key LIKE '/.internal/accounts/data/%'\n `);\n\n const pods: PodLookupResult[] = [];\n\n for (const row of result?.rows ?? []) {\n if (row.id && row.account_id && row.base_url) {\n pods.push({\n podId: String(row.id),\n accountId: String(row.account_id),\n baseUrl: String(row.base_url),\n nodeId: row.node_id ? String(row.node_id) : undefined,\n edgeNodeId: row.edge_node_id ? String(row.edge_node_id) : undefined,\n });\n continue;\n }\n\n if (!row.key || row.value === undefined) {\n continue;\n }\n\n try {\n const accountId = extractAccountIdFromAccountDataKey(row.key);\n if (!accountId) {\n continue;\n }\n const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;\n\n const podMap = (data as any)['**pod**'] || (data as any).pod || {};\n const webIds = extractAccountWebIds(data);\n\n for (const [podId, podData] of Object.entries(podMap)) {\n const pod = podData as Record<string, unknown>;\n if (pod.baseUrl && typeof pod.baseUrl === 'string') {\n const podWebIds = [\n typeof pod.webId === 'string' ? pod.webId : undefined,\n ...extractPodOwnerWebIds(pod),\n ...webIds,\n ].filter((value): value is string => typeof value === 'string');\n pods.push({\n podId,\n accountId,\n baseUrl: pod.baseUrl,\n webId: dedupeStrings(podWebIds)[0],\n ...webIdsProperty(podWebIds),\n nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,\n edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,\n });\n }\n }\n } catch {\n // Skip malformed entries.\n }\n }\n\n return mergePodLookupResults([\n ...pods,\n ...await this.getPodsFromIndexedStore(),\n ]);\n }\n\n /**\n * Older Xpod/CSS deployments may have used the IndexedStorage-compatible\n * identity_store table instead of CSS's WrappedIndexedStorage JSON tree in\n * internal_kv. Keep this as a read-only compatibility source so hosted WebID\n * profile lookup still works after storage implementation changes.\n */\n private async getPodsFromIndexedStore(): Promise<PodLookupResult[]> {\n const storeTableId = sql.identifier([this.indexedStoreTableName]);\n let result: { rows?: Array<{ container?: string; id?: string; payload?: unknown }> } | undefined;\n try {\n result = await executeQuery(this.db, sql`\n SELECT container, id, payload FROM ${storeTableId}\n WHERE container IN ('pod', 'owner', 'webIdLink')\n `);\n } catch {\n return [];\n }\n\n const podPayloads = new Map<string, Record<string, unknown>>();\n const ownerWebIdsByPodId = new Map<string, string[]>();\n const webIdsByAccountId = new Map<string, string[]>();\n\n for (const row of result?.rows ?? []) {\n if (!row.id || !row.container) {\n continue;\n }\n const payload = parsePayloadRecord(row.payload);\n if (!payload) {\n continue;\n }\n\n if (row.container === 'pod') {\n podPayloads.set(row.id, payload);\n continue;\n }\n\n if (row.container === 'owner') {\n const podId = stringValue(payload.podId);\n const webId = stringValue(payload.webId);\n if (podId && webId) {\n appendMapValue(ownerWebIdsByPodId, podId, webId);\n }\n continue;\n }\n\n if (row.container === 'webIdLink') {\n const accountId = stringValue(payload.accountId);\n const webId = stringValue(payload.webId);\n if (accountId && webId) {\n appendMapValue(webIdsByAccountId, accountId, webId);\n }\n }\n }\n\n const pods: PodLookupResult[] = [];\n for (const [podId, pod] of podPayloads) {\n const baseUrl = stringValue(pod.baseUrl);\n const accountId = stringValue(pod.accountId);\n if (!baseUrl || !accountId) {\n continue;\n }\n const podWebIds = dedupeStrings([\n stringValue(pod.webId),\n ...(ownerWebIdsByPodId.get(podId) ?? []),\n ...(webIdsByAccountId.get(accountId) ?? []),\n ].filter((value): value is string => typeof value === 'string'));\n\n pods.push({\n podId,\n accountId,\n baseUrl,\n webId: podWebIds[0],\n ...webIdsProperty(podWebIds),\n nodeId: stringValue(pod.nodeId),\n edgeNodeId: stringValue(pod.edgeNodeId),\n });\n }\n\n return pods;\n }\n}\n\nfunction extractAccountIdFromAccountDataKey(key: string): string | undefined {\n const marker = 'accounts/data/';\n const index = key.indexOf(marker);\n if (index < 0) {\n return undefined;\n }\n const accountId = key.slice(index + marker.length).replace(/\\.json$/u, '');\n return accountId || undefined;\n}\n\nfunction extractAccountWebIds(data: unknown): string[] {\n if (!data || typeof data !== 'object') {\n return [];\n }\n\n const record = data as Record<string, unknown>;\n const linkMap = record['**webIdLink**'] || record.webIdLink || {};\n if (!linkMap || typeof linkMap !== 'object') {\n return [];\n }\n\n return Object.values(linkMap as Record<string, unknown>)\n .map((value) => {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n const webId = (value as Record<string, unknown>).webId;\n return typeof webId === 'string' ? webId : undefined;\n })\n .filter((value): value is string => typeof value === 'string');\n}\n\nfunction extractPodOwnerWebIds(pod: Record<string, unknown>): string[] {\n const ownerMap = pod['**owner**'] || pod.owner || {};\n if (!ownerMap || typeof ownerMap !== 'object') {\n return [];\n }\n\n return Object.values(ownerMap as Record<string, unknown>)\n .map((value) => {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n const webId = (value as Record<string, unknown>).webId;\n return typeof webId === 'string' ? webId : undefined;\n })\n .filter((value): value is string => typeof value === 'string');\n}\n\nfunction normalizeWebId(webId: string | undefined): string | undefined {\n if (!webId) {\n return undefined;\n }\n try {\n return new URL(webId).toString();\n } catch {\n return webId;\n }\n}\n\nfunction dedupeStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction getPodWebIds(pod: PodLookupResult): string[] {\n return dedupeStrings([\n pod.webId,\n ...(pod.webIds ?? []),\n ].filter((value): value is string => typeof value === 'string' && value.length > 0));\n}\n\nfunction webIdsProperty(values: string[]): Pick<PodLookupResult, 'webIds'> {\n const webIds = dedupeStrings(values);\n return webIds.length > 1 ? { webIds } : {};\n}\n\nfunction mergePodLookupResults(values: PodLookupResult[]): PodLookupResult[] {\n const byPodId = new Map<string, PodLookupResult>();\n for (const value of values) {\n const existing = byPodId.get(value.podId);\n if (!existing) {\n byPodId.set(value.podId, value);\n continue;\n }\n\n const webIds = dedupeStrings([\n ...getPodWebIds(existing),\n ...getPodWebIds(value),\n ]);\n byPodId.set(value.podId, {\n ...existing,\n baseUrl: existing.baseUrl || value.baseUrl,\n accountId: existing.accountId || value.accountId,\n webId: webIds[0],\n ...webIdsProperty(webIds),\n nodeId: existing.nodeId ?? value.nodeId,\n edgeNodeId: existing.edgeNodeId ?? value.edgeNodeId,\n });\n }\n return [...byPodId.values()];\n}\n\nfunction parsePayloadRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value) {\n return undefined;\n }\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' ? parsed as Record<string, unknown> : undefined;\n } catch {\n return undefined;\n }\n }\n return typeof value === 'object' ? value as Record<string, unknown> : undefined;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction appendMapValue(map: Map<string, string[]>, key: string, value: string): void {\n const values = map.get(key) ?? [];\n values.push(value);\n map.set(key, values);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undefineds.co/xpod",
3
- "version": "0.2.39",
3
+ "version": "0.2.40",
4
4
  "description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
5
5
  "repository": "https://github.com/undefinedsco/xpod",
6
6
  "author": "developer@undefineds.co",