Skip to content

Commit 783412f

Browse files
committed
fix: make parseLink handle stream bases (adds regression test)
1 parent 8bdf98c commit 783412f

File tree

2 files changed

+88
-8
lines changed

2 files changed

+88
-8
lines changed

packages/runner/src/link-utils.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,18 +236,27 @@ export function parseLink(
236236

237237
if (isCell(value) || isStream(value)) return value.getAsNormalizedFullLink();
238238

239+
let baseLink: NormalizedLink | undefined;
240+
if (base) {
241+
if (isCell(base) || isStream(base)) {
242+
baseLink = base.getAsNormalizedFullLink();
243+
} else {
244+
baseLink = base as NormalizedLink;
245+
}
246+
}
247+
239248
// Handle new sigil format
240249
if (isSigilLink(value)) {
241250
const link = value["/"][LINK_V1_TAG];
242251

243252
// Resolve relative references
244253
let id = link.id;
245254
const path = link.path || [];
246-
const resolvedSpace = link.space || base?.space;
255+
const resolvedSpace = link.space || baseLink?.space;
247256

248257
// If no id provided, use base cell's document
249-
if (!id && base) {
250-
id = isAnyCell(base) ? toURI(base.entityId) : base.id;
258+
if (!id && baseLink?.id) {
259+
id = baseLink.id;
251260
}
252261

253262
return {
@@ -266,7 +275,7 @@ export function parseLink(
266275
return {
267276
id: toURI(value.cell["/"]),
268277
path: value.path.map((p) => p.toString()),
269-
...(base?.space && { space: base.space }),
278+
...(baseLink?.space && { space: baseLink.space }),
270279
type: "application/json",
271280
};
272281
}
@@ -275,7 +284,7 @@ export function parseLink(
275284
return {
276285
id: toURI(value["/"]),
277286
path: [],
278-
...(base?.space && { space: base.space }), // Space must come from context for JSON links
287+
...(baseLink?.space && { space: baseLink.space }), // Space must come from context for JSON links
279288
type: "application/json",
280289
};
281290
}
@@ -293,16 +302,16 @@ export function parseLink(
293302
}
294303

295304
// If no cell provided, use base cell's document
296-
if (!id && base) {
297-
id = isAnyCell(base) ? toURI(base.entityId) : base.id;
305+
if (!id && baseLink?.id) {
306+
id = baseLink.id;
298307
}
299308

300309
return {
301310
...(id && { id }),
302311
path: Array.isArray(alias.path)
303312
? alias.path.map((p) => p.toString())
304313
: [],
305-
...(base?.space && { space: base.space }),
314+
...(baseLink?.space && { space: baseLink.space }),
306315
type: "application/json",
307316
...(alias.schema && { schema: alias.schema }),
308317
...(alias.rootSchema && { rootSchema: alias.rootSchema }),
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { afterEach, beforeEach, describe, it } from "@std/testing/bdd";
2+
import { expect } from "@std/expect";
3+
import { Identity } from "@commontools/identity";
4+
import { StorageManager } from "@commontools/runner/storage/cache.deno";
5+
import { Runtime } from "../src/runtime.ts";
6+
import { parseLink } from "../src/link-utils.ts";
7+
import { LINK_V1_TAG, type SigilLink } from "../src/sigil-types.ts";
8+
import { isStream } from "../src/cell.ts";
9+
10+
const signer = await Identity.fromPassphrase("parse-link stream base regression");
11+
const space = signer.did();
12+
13+
describe("parseLink stream base", () => {
14+
let storageManager: ReturnType<typeof StorageManager.emulate>;
15+
let runtime: Runtime;
16+
17+
beforeEach(() => {
18+
storageManager = StorageManager.emulate({ as: signer });
19+
runtime = new Runtime({
20+
apiUrl: new URL(import.meta.url),
21+
storageManager,
22+
});
23+
});
24+
25+
afterEach(async () => {
26+
await runtime?.storageManager.synced();
27+
await runtime?.dispose();
28+
await storageManager?.close();
29+
});
30+
31+
it("should safely resolve links when a Stream is provided as the base", async () => {
32+
const tx = runtime.edit();
33+
const backingCell = runtime.getCell(
34+
space,
35+
"regression-parseLink-stream-base",
36+
undefined,
37+
tx,
38+
);
39+
backingCell.set({
40+
events: { $stream: true },
41+
});
42+
await tx.commit();
43+
44+
const schema = {
45+
type: "object",
46+
properties: {
47+
events: {
48+
type: "object",
49+
asStream: true,
50+
},
51+
},
52+
required: ["events"],
53+
} as const;
54+
55+
const typedCell = backingCell.asSchema(schema);
56+
const eventsStream = typedCell.key("events");
57+
expect(isStream(eventsStream)).toBe(true);
58+
59+
const sigil: SigilLink = {
60+
"/": {
61+
[LINK_V1_TAG]: {
62+
path: ["payload"],
63+
},
64+
},
65+
};
66+
67+
const call = () => parseLink(sigil, eventsStream);
68+
69+
expect(call).not.toThrow();
70+
});
71+
});

0 commit comments

Comments
 (0)