Skip to content

Commit 5be4274

Browse files
authored
[ENG-1847] Define shared cross-app node content contract (#1129)
* [ENG-1847] Define shared cross-app node content contract * [ENG-1847] Trim cross-app contract comments * [ENG-1847] Simplify cross-app contract naming * [ENG-1847] Keep cross-app node examples focused * [ENG-1847] Inline single-use node example values * Document RID local ID delimiter contract
1 parent ce0ce7d commit 5be4274

10 files changed

Lines changed: 120 additions & 7 deletions

File tree

apps/obsidian/src/utils/importNodes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
getImportedNodesInfo,
1111
getLocalNodeKeyToEndpointId,
1212
} from "~/utils/relationsStore";
13-
import { spaceUriAndLocalIdToRid } from "./rid";
13+
import { spaceUriAndLocalIdToRid } from "@repo/database/lib/rid";
1414
import type { PostgrestResponse } from "@supabase/supabase-js";
1515
import type { Tables } from "@repo/database/dbTypes";
1616
import { getSpaceNameIdFromRid } from "./spaceFromRid";

apps/obsidian/src/utils/importPreview.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
fetchRelationInstancesFromSpace,
1212
type RemoteRelationInstance,
1313
} from "./importRelations";
14-
import { spaceUriAndLocalIdToRid } from "./rid";
14+
import { spaceUriAndLocalIdToRid } from "@repo/database/lib/rid";
1515

1616
export type RelationTriplet = {
1717
sourceNodeTypeName: string;

apps/obsidian/src/utils/importRelations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { DGSupabaseClient } from "@repo/database/lib/client";
33
import { uuidv7 } from "uuidv7";
44
import type DiscourseGraphPlugin from "~/index";
55
import type { DiscourseRelationType, DiscourseRelation } from "~/types";
6-
import { spaceUriAndLocalIdToRid } from "./rid";
6+
import { spaceUriAndLocalIdToRid } from "@repo/database/lib/rid";
77
import {
88
loadRelations,
99
addRelationNoCheck,

apps/obsidian/src/utils/relationsStore.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import { getVaultId, getLocalSpaceUri } from "./supabaseContext";
77
import type { RelationInstance } from "~/types";
88
import { QueryEngine, getImportedNodesRaw } from "~/services/QueryEngine";
99
import { publishNewRelation } from "./publishNode";
10-
import { ridToSpaceUriAndLocalId, spaceUriAndLocalIdToRid } from "./rid";
10+
import {
11+
ridToSpaceUriAndLocalId,
12+
spaceUriAndLocalIdToRid,
13+
} from "@repo/database/lib/rid";
1114
import { getSpaceIdsBySpaceUris } from "./spaceFromRid";
1215

1316
const RELATIONS_FILE_NAME = "relations.json";

apps/obsidian/src/utils/spaceFromRid.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { DGSupabaseClient } from "@repo/database/lib/client";
2-
import { ridToSpaceUriAndLocalId } from "./rid";
2+
import { ridToSpaceUriAndLocalId } from "@repo/database/lib/rid";
33

44
export const getSpaceNameIdFromRid = async (
55
client: DGSupabaseClient,

apps/obsidian/src/utils/typeUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type DiscourseGraphPlugin from "~/index";
22
import { DiscourseNode, DiscourseRelationType, ImportStatus } from "~/types";
3-
import { ridToSpaceUriAndLocalId } from "./rid";
3+
import { ridToSpaceUriAndLocalId } from "@repo/database/lib/rid";
44

55
export const getNodeTypeById = (
66
plugin: DiscourseGraphPlugin,

packages/database/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"default": "./src/dbDotEnv.mjs"
1212
},
1313
"./dbTypes": "./src/dbTypes.ts",
14-
"./inputTypes": "./src/inputTypes.ts"
14+
"./inputTypes": "./src/inputTypes.ts",
15+
"./crossAppNodeContract": "./src/crossAppNodeContract.ts"
1516
},
1617
"typesVersions": {
1718
"*": {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { CrossAppNode } from "./crossAppNodeContract";
2+
import { spaceUriAndLocalIdToRid } from "./lib/rid";
3+
4+
const ROAM_SOURCE_SPACE_ID = "https://roamresearch.com/#/app/MAPLab";
5+
const ROAM_SOURCE_NODE_ID = "tgWb6JozF";
6+
7+
const roamFullMarkdown = `# Sleep improves memory consolidation
8+
9+
Multiple studies show that sleep after learning strengthens memory traces.
10+
11+
- Supported by [[EVD]] - Rasch & Born 2013
12+
`;
13+
14+
export const roamOriginNodeExample: CrossAppNode = {
15+
sourceApp: "roam",
16+
sourceSpaceId: ROAM_SOURCE_SPACE_ID,
17+
sourceSpaceName: "MAPLab",
18+
sourceNodeId: ROAM_SOURCE_NODE_ID,
19+
sourceNodeRid: spaceUriAndLocalIdToRid(
20+
ROAM_SOURCE_SPACE_ID,
21+
ROAM_SOURCE_NODE_ID,
22+
),
23+
nodeType: {
24+
sourceNodeTypeId: "rCLM0schema",
25+
label: "Claim",
26+
},
27+
content: {
28+
direct: { value: "Sleep improves memory consolidation" },
29+
full: { format: "text/markdown", value: roamFullMarkdown },
30+
},
31+
sourceModifiedAt: "2026-06-12T14:00:00.000Z",
32+
};
33+
34+
const OBSIDIAN_SOURCE_SPACE_ID = "obsidian:9a8b7c6d5e4f3210";
35+
const OBSIDIAN_SOURCE_NODE_ID = "0192f1a0-7b3c-7e2a-9f10-1a2b3c4d5e6f";
36+
const OBSIDIAN_SOURCE_NODE_TYPE_ID = "evd-7c1f9a2b";
37+
38+
const obsidianFullMarkdown = `---
39+
nodeTypeId: ${OBSIDIAN_SOURCE_NODE_TYPE_ID}
40+
nodeInstanceId: ${OBSIDIAN_SOURCE_NODE_ID}
41+
---
42+
43+
# REM sleep correlates with recall
44+
45+
Participants with more REM sleep showed better next-day recall.
46+
`;
47+
48+
export const obsidianOriginNodeExample: CrossAppNode = {
49+
sourceApp: "obsidian",
50+
sourceSpaceId: OBSIDIAN_SOURCE_SPACE_ID,
51+
sourceSpaceName: "Research Vault",
52+
sourceNodeId: OBSIDIAN_SOURCE_NODE_ID,
53+
sourceNodeRid: spaceUriAndLocalIdToRid(
54+
OBSIDIAN_SOURCE_SPACE_ID,
55+
OBSIDIAN_SOURCE_NODE_ID,
56+
"note",
57+
),
58+
nodeType: {
59+
sourceNodeTypeId: OBSIDIAN_SOURCE_NODE_TYPE_ID,
60+
label: "Evidence",
61+
},
62+
content: {
63+
direct: { value: "EVD - REM sleep and recall" },
64+
full: { format: "text/markdown", value: obsidianFullMarkdown },
65+
},
66+
sourceModifiedAt: "2026-06-14T10:30:00.000Z",
67+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
export type CrossAppNode = {
2+
sourceApp: "roam" | "obsidian";
3+
/**
4+
* Stable source-space id. Maps to `Space.url`, not numeric `Space.id`.
5+
*/
6+
sourceSpaceId: string;
7+
sourceSpaceName: string;
8+
/**
9+
* Node id inside the source app/space.
10+
* Roam: page/block UID.
11+
* Obsidian: nodeInstanceId.
12+
*/
13+
sourceNodeId: string;
14+
sourceNodeRid: string;
15+
nodeType: {
16+
/**
17+
* Node type/schema id inside the source app/space.
18+
* Maps to the schema Concept's `source_local_id`.
19+
*/
20+
sourceNodeTypeId: string;
21+
label: string;
22+
};
23+
content: {
24+
direct: { value: string };
25+
full: {
26+
/**
27+
* Contract media type for `full.value`; current Content rows store the
28+
* markdown in `text`, not in a typed media column.
29+
*/
30+
format: "text/markdown";
31+
value: string;
32+
};
33+
};
34+
/**
35+
* Source modified timestamp, or latest required `Content.last_modified` when
36+
* deriving from persisted `direct` and `full` rows.
37+
*/
38+
sourceModifiedAt: string;
39+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ export const spaceUriAndLocalIdToRid = (
1010
localId: string,
1111
subtype?: string,
1212
): string => {
13+
// Both RID forms use `/` as the sourceLocalId delimiter, so callers must pass
14+
// slash-free localIds (or pre-encode them) for ridToSpaceUriAndLocalId to
15+
// round-trip the original pair.
1316
if (spaceUri.startsWith("http")) return `${spaceUri}/${localId}`;
1417
const parts = spaceUri.split(":");
1518
if (parts.length === 2)

0 commit comments

Comments
 (0)