@relayfile/adapter-confluence 0.1.0 → 0.1.2
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/discovery/confluence/.adapter.md +4 -4
- package/dist/__tests__/alias-slug.test.d.ts +2 -0
- package/dist/__tests__/alias-slug.test.d.ts.map +1 -0
- package/dist/__tests__/alias-slug.test.js +19 -0
- package/dist/__tests__/alias-slug.test.js.map +1 -0
- package/dist/__tests__/confluence-adapter.test.js +263 -11
- package/dist/__tests__/confluence-adapter.test.js.map +1 -1
- package/dist/__tests__/index-emission.test.d.ts +2 -0
- package/dist/__tests__/index-emission.test.d.ts.map +1 -0
- package/dist/__tests__/index-emission.test.js +67 -0
- package/dist/__tests__/index-emission.test.js.map +1 -0
- package/dist/__tests__/layout-prompt.test.d.ts +2 -0
- package/dist/__tests__/layout-prompt.test.d.ts.map +1 -0
- package/dist/__tests__/layout-prompt.test.js +23 -0
- package/dist/__tests__/layout-prompt.test.js.map +1 -0
- package/dist/__tests__/path-mapper.test.d.ts +2 -0
- package/dist/__tests__/path-mapper.test.d.ts.map +1 -0
- package/dist/__tests__/path-mapper.test.js +106 -0
- package/dist/__tests__/path-mapper.test.js.map +1 -0
- package/dist/__tests__/webhook-normalizer.test.d.ts +2 -0
- package/dist/__tests__/webhook-normalizer.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-normalizer.test.js +106 -0
- package/dist/__tests__/webhook-normalizer.test.js.map +1 -0
- package/dist/alias-slug.d.ts +9 -0
- package/dist/alias-slug.d.ts.map +1 -0
- package/dist/alias-slug.js +23 -0
- package/dist/alias-slug.js.map +1 -0
- package/dist/confluence-adapter.d.ts +37 -0
- package/dist/confluence-adapter.d.ts.map +1 -1
- package/dist/confluence-adapter.js +245 -24
- package/dist/confluence-adapter.js.map +1 -1
- package/dist/index-emitter.d.ts +10 -0
- package/dist/index-emitter.d.ts.map +1 -0
- package/dist/index-emitter.js +26 -0
- package/dist/index-emitter.js.map +1 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/layout-prompt.d.ts +7 -0
- package/dist/layout-prompt.d.ts.map +1 -0
- package/dist/layout-prompt.js +75 -0
- package/dist/layout-prompt.js.map +1 -0
- package/dist/path-mapper.d.ts +65 -2
- package/dist/path-mapper.d.ts.map +1 -1
- package/dist/path-mapper.js +186 -10
- package/dist/path-mapper.js.map +1 -1
- package/dist/queries.d.ts +22 -2
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +43 -2
- package/dist/queries.js.map +1 -1
- package/dist/resources.js +2 -2
- package/dist/resources.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/webhook-normalizer.d.ts +59 -0
- package/dist/webhook-normalizer.d.ts.map +1 -0
- package/dist/webhook-normalizer.js +469 -0
- package/dist/webhook-normalizer.js.map +1 -0
- package/dist/writeback.d.ts +1 -2
- package/dist/writeback.d.ts.map +1 -1
- package/dist/writeback.js +1 -1
- package/dist/writeback.js.map +1 -1
- package/package.json +22 -2
|
@@ -11,8 +11,8 @@ Resources:
|
|
|
11
11
|
|
|
12
12
|
| Resource | Schema | Create example | ID pattern | What it does |
|
|
13
13
|
|---|---|---|---|---|
|
|
14
|
-
| `/confluence/pages/<id>.json` | `/confluence/pages/.schema.json` | `/confluence/pages/.create.example.json` | `^(?:[A-Za-z0-9_.~-]
|
|
15
|
-
| `/confluence/spaces/{spaceIdOrKey}/pages/<id>.json` | `/confluence/spaces/{spaceIdOrKey}/pages/.schema.json` | `/confluence/spaces/{spaceIdOrKey}/pages/.create.example.json` | `^(?:[A-Za-z0-9_.~-]
|
|
14
|
+
| `/confluence/pages/<id>.json` | `/confluence/pages/.schema.json` | `/confluence/pages/.create.example.json` | `^(?:[A-Za-z0-9_.~-]+(?:--\|__))?\d+$` | Creates a Confluence page when `spaceId` is supplied in the document. |
|
|
15
|
+
| `/confluence/spaces/{spaceIdOrKey}/pages/<id>.json` | `/confluence/spaces/{spaceIdOrKey}/pages/.schema.json` | `/confluence/spaces/{spaceIdOrKey}/pages/.create.example.json` | `^(?:[A-Za-z0-9_.~-]+(?:--\|__))?\d+$` | Creates a Confluence page in the space named by the path. |
|
|
16
16
|
|
|
17
17
|
## Operations
|
|
18
18
|
|
|
@@ -24,8 +24,8 @@ Resources:
|
|
|
24
24
|
| Delete | `rm <id>.json` for canonical ids. |
|
|
25
25
|
|
|
26
26
|
## ID Patterns
|
|
27
|
-
- `/confluence/pages/<id>.json`: `^(?:[A-Za-z0-9_.~-]
|
|
28
|
-
- `/confluence/spaces/{spaceIdOrKey}/pages/<id>.json`: `^(?:[A-Za-z0-9_.~-]
|
|
27
|
+
- `/confluence/pages/<id>.json`: `^(?:[A-Za-z0-9_.~-]+(?:--\|__))?\d+$`. Filenames that do not match this pattern are treated as create drafts.
|
|
28
|
+
- `/confluence/spaces/{spaceIdOrKey}/pages/<id>.json`: `^(?:[A-Za-z0-9_.~-]+(?:--\|__))?\d+$`. Filenames that do not match this pattern are treated as create drafts.
|
|
29
29
|
|
|
30
30
|
## Write field contracts
|
|
31
31
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alias-slug.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/alias-slug.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { aliasCollisionSuffix, slugifyAlias } from '../alias-slug.js';
|
|
4
|
+
describe('confluence alias slug', () => {
|
|
5
|
+
it('slugging is deterministic, ASCII-folded, and strips traversal characters', () => {
|
|
6
|
+
assert.equal(slugifyAlias('Café ../ Roadmap'), 'cafe-roadmap');
|
|
7
|
+
assert.equal(slugifyAlias('Café ../ Roadmap'), slugifyAlias('Café ../ Roadmap'));
|
|
8
|
+
});
|
|
9
|
+
it('falls back to "untitled" when the input slugs to nothing', () => {
|
|
10
|
+
assert.equal(slugifyAlias('🚀🔥'), 'untitled');
|
|
11
|
+
assert.equal(slugifyAlias(' '), 'untitled');
|
|
12
|
+
});
|
|
13
|
+
it('emits an 8-char hex collision suffix from the id', () => {
|
|
14
|
+
const suffix = aliasCollisionSuffix('page-id-1');
|
|
15
|
+
assert.match(suffix, /^[0-9a-f]{8}$/u);
|
|
16
|
+
assert.equal(suffix, aliasCollisionSuffix('page-id-1'));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=alias-slug.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alias-slug.test.js","sourceRoot":"","sources":["../../src/__tests__/alias-slug.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEtE,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,cAAc,CAAC,CAAC;QAC/D,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,MAAM,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { ConfluenceAdapter, } from '../confluence-adapter.js';
|
|
4
|
-
import { computeConfluencePath, confluencePagePath, confluenceSpacePath, } from '../path-mapper.js';
|
|
4
|
+
import { computeConfluencePath, confluencePageByIdAliasPath, confluencePageByParentAliasPath, confluencePageBySpaceAliasPath, confluencePageByStatePath, confluencePageByTitleAliasPath, confluencePagePath, confluenceSpaceByIdAliasPath, confluenceSpaceByKeyAliasPath, confluenceSpaceByTitleAliasPath, confluenceSpacePath, } from '../path-mapper.js';
|
|
5
5
|
import { resolveConfluenceReadRequest } from '../queries.js';
|
|
6
6
|
import { ReadOnlyFieldError, resolveConfluenceDeleteRequest, resolveConfluenceWritebackRequest, } from '../writeback.js';
|
|
7
7
|
function createClient() {
|
|
@@ -31,13 +31,13 @@ function createAdapter(client = createClient()) {
|
|
|
31
31
|
}
|
|
32
32
|
describe('ConfluenceAdapter', () => {
|
|
33
33
|
it('computes deterministic Confluence paths', () => {
|
|
34
|
-
assert.equal(computeConfluencePath('space', '12345', { title: 'Engineering Docs' }), '/confluence/spaces/engineering-
|
|
35
|
-
assert.equal(computeConfluencePath('page', '98765', { title: 'Release Plan', spaceId: '12345' }), '/confluence/spaces/12345/pages/release-
|
|
34
|
+
assert.equal(computeConfluencePath('space', '12345', { title: 'Engineering Docs' }), '/confluence/spaces/engineering-docs__12345.json');
|
|
35
|
+
assert.equal(computeConfluencePath('page', '98765', { title: 'Release Plan', spaceId: '12345' }), '/confluence/spaces/12345/pages/release-plan__98765.json');
|
|
36
36
|
});
|
|
37
37
|
it('materializes spaces and pages from Nango-style sync records', () => {
|
|
38
38
|
const adapter = createAdapter();
|
|
39
39
|
assert.deepEqual(adapter.materializeSpace({ id: '12345', key: 'ENG', name: 'Engineering' }), {
|
|
40
|
-
path: '/confluence/spaces/
|
|
40
|
+
path: '/confluence/spaces/engineering__12345.json',
|
|
41
41
|
payload: {
|
|
42
42
|
provider: 'confluence',
|
|
43
43
|
objectType: 'space',
|
|
@@ -45,9 +45,9 @@ describe('ConfluenceAdapter', () => {
|
|
|
45
45
|
payload: { id: '12345', key: 'ENG', name: 'Engineering' },
|
|
46
46
|
},
|
|
47
47
|
});
|
|
48
|
-
assert.equal(adapter.materializePage({ id: '98765', title: 'Release Plan', spaceId: '12345' }).path, '/confluence/spaces/12345/pages/release-
|
|
48
|
+
assert.equal(adapter.materializePage({ id: '98765', title: 'Release Plan', spaceId: '12345' }).path, '/confluence/spaces/12345/pages/release-plan__98765.json');
|
|
49
49
|
});
|
|
50
|
-
it('ingests page events with space relations and
|
|
50
|
+
it('ingests page events with space relations, body comments, and by-* alias fan-out', async () => {
|
|
51
51
|
const client = createClient();
|
|
52
52
|
const adapter = createAdapter(client);
|
|
53
53
|
const result = await adapter.ingestWebhook('workspace-1', {
|
|
@@ -60,16 +60,268 @@ describe('ConfluenceAdapter', () => {
|
|
|
60
60
|
title: 'Release Plan',
|
|
61
61
|
status: 'current',
|
|
62
62
|
spaceId: '12345',
|
|
63
|
+
parentId: '44444',
|
|
63
64
|
body: { storage: { value: '<p>Ship carefully.</p>', representation: 'storage' } },
|
|
64
65
|
},
|
|
65
66
|
});
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
const canonicalPath = '/confluence/spaces/12345/pages/release-plan__98765.json';
|
|
68
|
+
const expectedAliasPaths = [
|
|
69
|
+
confluencePageByIdAliasPath('98765'),
|
|
70
|
+
confluencePageByTitleAliasPath('Release Plan', '98765'),
|
|
71
|
+
confluencePageByStatePath('current', '98765'),
|
|
72
|
+
confluencePageBySpaceAliasPath('12345', '98765'),
|
|
73
|
+
confluencePageByParentAliasPath('44444', '98765'),
|
|
74
|
+
];
|
|
75
|
+
// Canonical record + every applicable alias path are written with identical
|
|
76
|
+
// bytes — readers can pick whichever lookup primitive they have.
|
|
77
|
+
assert.equal(result.filesWritten, 1 + expectedAliasPaths.length);
|
|
78
|
+
assert.deepEqual(result.paths, [canonicalPath, ...expectedAliasPaths]);
|
|
79
|
+
const canonicalContent = client.writes.find((write) => write.path === canonicalPath)?.content;
|
|
80
|
+
assert.ok(canonicalContent, 'canonical content must be present');
|
|
81
|
+
for (const aliasPath of expectedAliasPaths) {
|
|
82
|
+
const aliasWrite = client.writes.find((write) => write.path === aliasPath);
|
|
83
|
+
assert.ok(aliasWrite, `alias ${aliasPath} must be written`);
|
|
84
|
+
assert.equal(aliasWrite?.content, canonicalContent, `alias ${aliasPath} bytes must match canonical`);
|
|
85
|
+
}
|
|
68
86
|
assert.deepEqual(client.writes[0]?.semantics?.relations, [
|
|
69
87
|
confluenceSpacePath('12345'),
|
|
88
|
+
confluencePagePath('44444'),
|
|
70
89
|
]);
|
|
71
90
|
assert.equal(client.writes[0]?.semantics?.comments?.[0], 'Ship carefully.');
|
|
72
91
|
});
|
|
92
|
+
it('skips by-title alias when the page title slugs to nothing', async () => {
|
|
93
|
+
const client = createClient();
|
|
94
|
+
const adapter = createAdapter(client);
|
|
95
|
+
await adapter.ingestWebhook('workspace-1', {
|
|
96
|
+
provider: 'confluence',
|
|
97
|
+
eventType: 'page.created',
|
|
98
|
+
objectType: 'page',
|
|
99
|
+
objectId: '11111',
|
|
100
|
+
payload: { id: '11111', title: '🚀🔥', status: 'current', spaceId: '12345' },
|
|
101
|
+
});
|
|
102
|
+
// by-id, by-state, by-space — no by-title, no by-parent.
|
|
103
|
+
const aliasPaths = client.writes.map((write) => write.path).filter((path) => path.includes('/by-'));
|
|
104
|
+
assert.deepEqual(aliasPaths, [
|
|
105
|
+
confluencePageByIdAliasPath('11111'),
|
|
106
|
+
confluencePageByStatePath('current', '11111'),
|
|
107
|
+
confluencePageBySpaceAliasPath('12345', '11111'),
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
it('emits space by-id, by-title, and by-key aliases on space ingest', async () => {
|
|
111
|
+
const client = createClient();
|
|
112
|
+
const adapter = createAdapter(client);
|
|
113
|
+
await adapter.ingestWebhook('workspace-1', {
|
|
114
|
+
provider: 'confluence',
|
|
115
|
+
eventType: 'space.updated',
|
|
116
|
+
objectType: 'space',
|
|
117
|
+
objectId: '12345',
|
|
118
|
+
payload: { id: '12345', key: 'ENG', name: 'Engineering' },
|
|
119
|
+
});
|
|
120
|
+
const canonicalPath = '/confluence/spaces/engineering__12345.json';
|
|
121
|
+
const expectedAliasPaths = [
|
|
122
|
+
confluenceSpaceByIdAliasPath('12345'),
|
|
123
|
+
confluenceSpaceByTitleAliasPath('Engineering', '12345'),
|
|
124
|
+
confluenceSpaceByKeyAliasPath('ENG'),
|
|
125
|
+
];
|
|
126
|
+
assert.deepEqual(client.writes.map((write) => write.path), [canonicalPath, ...expectedAliasPaths]);
|
|
127
|
+
});
|
|
128
|
+
it('deletes the canonical path and every alias on a delete event', async () => {
|
|
129
|
+
const client = createClient();
|
|
130
|
+
const adapter = createAdapter(client);
|
|
131
|
+
const result = await adapter.ingestWebhook('workspace-1', {
|
|
132
|
+
provider: 'confluence',
|
|
133
|
+
eventType: 'page.deleted',
|
|
134
|
+
objectType: 'page',
|
|
135
|
+
objectId: '98765',
|
|
136
|
+
payload: { id: '98765', title: 'Release Plan', status: 'current', spaceId: '12345' },
|
|
137
|
+
});
|
|
138
|
+
const canonicalPath = '/confluence/spaces/12345/pages/release-plan__98765.json';
|
|
139
|
+
const expectedAliasPaths = [
|
|
140
|
+
confluencePageByIdAliasPath('98765'),
|
|
141
|
+
confluencePageByTitleAliasPath('Release Plan', '98765'),
|
|
142
|
+
confluencePageByStatePath('current', '98765'),
|
|
143
|
+
confluencePageBySpaceAliasPath('12345', '98765'),
|
|
144
|
+
];
|
|
145
|
+
assert.equal(result.filesDeleted, 1 + expectedAliasPaths.length);
|
|
146
|
+
assert.deepEqual(client.deletes.map((d) => d.path), [canonicalPath, ...expectedAliasPaths]);
|
|
147
|
+
});
|
|
148
|
+
it('materializePageFiles returns the canonical path plus every applicable alias', () => {
|
|
149
|
+
const adapter = createAdapter();
|
|
150
|
+
const { paths } = adapter.materializePageFiles({
|
|
151
|
+
id: '98765',
|
|
152
|
+
title: 'Release Plan',
|
|
153
|
+
status: 'current',
|
|
154
|
+
spaceId: '12345',
|
|
155
|
+
parentId: '44444',
|
|
156
|
+
});
|
|
157
|
+
assert.deepEqual(paths, [
|
|
158
|
+
'/confluence/spaces/12345/pages/release-plan__98765.json',
|
|
159
|
+
confluencePageByIdAliasPath('98765'),
|
|
160
|
+
confluencePageByTitleAliasPath('Release Plan', '98765'),
|
|
161
|
+
confluencePageByStatePath('current', '98765'),
|
|
162
|
+
confluencePageBySpaceAliasPath('12345', '98765'),
|
|
163
|
+
confluencePageByParentAliasPath('44444', '98765'),
|
|
164
|
+
]);
|
|
165
|
+
});
|
|
166
|
+
it('materializeSpaceFiles returns the canonical path plus every applicable alias', () => {
|
|
167
|
+
const adapter = createAdapter();
|
|
168
|
+
const { paths } = adapter.materializeSpaceFiles({
|
|
169
|
+
id: '12345',
|
|
170
|
+
key: 'ENG',
|
|
171
|
+
name: 'Engineering',
|
|
172
|
+
});
|
|
173
|
+
assert.deepEqual(paths, [
|
|
174
|
+
'/confluence/spaces/engineering__12345.json',
|
|
175
|
+
confluenceSpaceByIdAliasPath('12345'),
|
|
176
|
+
confluenceSpaceByTitleAliasPath('Engineering', '12345'),
|
|
177
|
+
confluenceSpaceByKeyAliasPath('ENG'),
|
|
178
|
+
]);
|
|
179
|
+
});
|
|
180
|
+
it('continues fanning out aliases when one writeFile throws and reports the failed path', async () => {
|
|
181
|
+
// A mid-loop throw used to abandon the rest of the fan-out, leaving the
|
|
182
|
+
// canonical record and earlier aliases written but later aliases missing.
|
|
183
|
+
// Now each write is independent: the canonical + the surviving aliases
|
|
184
|
+
// land, the bad path is reported in `errors`, and the caller can retry
|
|
185
|
+
// just that path.
|
|
186
|
+
const failingPath = confluencePageByTitleAliasPath('Release Plan', '98765');
|
|
187
|
+
const client = {
|
|
188
|
+
writes: [],
|
|
189
|
+
deletes: [],
|
|
190
|
+
async writeFile(input) {
|
|
191
|
+
if (input.path === failingPath) {
|
|
192
|
+
throw new Error('synthetic backend error');
|
|
193
|
+
}
|
|
194
|
+
this.writes.push(input);
|
|
195
|
+
return { created: true };
|
|
196
|
+
},
|
|
197
|
+
async deleteFile(input) {
|
|
198
|
+
this.deletes.push(input);
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
const adapter = createAdapter(client);
|
|
202
|
+
const result = await adapter.ingestWebhook('workspace-1', {
|
|
203
|
+
provider: 'confluence',
|
|
204
|
+
eventType: 'page.updated',
|
|
205
|
+
objectType: 'page',
|
|
206
|
+
objectId: '98765',
|
|
207
|
+
payload: { id: '98765', title: 'Release Plan', status: 'current', spaceId: '12345' },
|
|
208
|
+
});
|
|
209
|
+
assert.equal(result.errors.length, 1);
|
|
210
|
+
assert.equal(result.errors[0].path, failingPath);
|
|
211
|
+
assert.match(result.errors[0].error, /synthetic backend error/);
|
|
212
|
+
const expectedWrittenPaths = [
|
|
213
|
+
'/confluence/spaces/12345/pages/release-plan__98765.json',
|
|
214
|
+
confluencePageByIdAliasPath('98765'),
|
|
215
|
+
confluencePageByStatePath('current', '98765'),
|
|
216
|
+
confluencePageBySpaceAliasPath('12345', '98765'),
|
|
217
|
+
];
|
|
218
|
+
assert.deepEqual(client.writes.map((w) => w.path), expectedWrittenPaths);
|
|
219
|
+
assert.deepEqual(result.paths, expectedWrittenPaths);
|
|
220
|
+
});
|
|
221
|
+
it('deletes stale aliases on rename via the by-id reconciliation path', async () => {
|
|
222
|
+
// When an alias-driving field changes (here: title), the prior alias
|
|
223
|
+
// paths (`by-title/release-plan__98765.json` and the old canonical
|
|
224
|
+
// `release-plan__98765.json`) become orphans. Reconciliation reads the
|
|
225
|
+
// stable by-id alias to recover the prior payload, computes the prior
|
|
226
|
+
// alias set, and deletes the diff before writing the new alias set.
|
|
227
|
+
const priorPayload = { id: '98765', title: 'Release Plan', status: 'current', spaceId: '12345' };
|
|
228
|
+
const priorWrapper = {
|
|
229
|
+
provider: 'confluence',
|
|
230
|
+
workspaceId: 'workspace-1',
|
|
231
|
+
objectType: 'page',
|
|
232
|
+
objectId: '98765',
|
|
233
|
+
deleted: false,
|
|
234
|
+
payload: priorPayload,
|
|
235
|
+
connectionId: 'conn-confluence',
|
|
236
|
+
};
|
|
237
|
+
const reads = [];
|
|
238
|
+
const client = {
|
|
239
|
+
writes: [],
|
|
240
|
+
deletes: [],
|
|
241
|
+
async writeFile(input) {
|
|
242
|
+
this.writes.push(input);
|
|
243
|
+
return { updated: true };
|
|
244
|
+
},
|
|
245
|
+
async deleteFile(input) {
|
|
246
|
+
this.deletes.push(input);
|
|
247
|
+
},
|
|
248
|
+
async readFile(input) {
|
|
249
|
+
reads.push(input);
|
|
250
|
+
if (input.path === confluencePageByIdAliasPath('98765')) {
|
|
251
|
+
return { content: JSON.stringify(priorWrapper, null, 2) };
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
const adapter = createAdapter(client);
|
|
257
|
+
await adapter.ingestWebhook('workspace-1', {
|
|
258
|
+
provider: 'confluence',
|
|
259
|
+
eventType: 'page.updated',
|
|
260
|
+
objectType: 'page',
|
|
261
|
+
objectId: '98765',
|
|
262
|
+
payload: { id: '98765', title: 'Launch Plan', status: 'current', spaceId: '12345' },
|
|
263
|
+
});
|
|
264
|
+
// The reconciliation read targeted the stable by-id path.
|
|
265
|
+
assert.deepEqual(reads, [{ workspaceId: 'workspace-1', path: confluencePageByIdAliasPath('98765') }]);
|
|
266
|
+
// Stale paths from the prior title were deleted: old canonical + old by-title.
|
|
267
|
+
const deletedPaths = new Set(client.deletes.map((d) => d.path));
|
|
268
|
+
assert.ok(deletedPaths.has('/confluence/spaces/12345/pages/release-plan__98765.json'), 'expected old canonical path to be deleted');
|
|
269
|
+
assert.ok(deletedPaths.has(confluencePageByTitleAliasPath('Release Plan', '98765')), 'expected old by-title alias to be deleted');
|
|
270
|
+
// The stable by-id, by-state, by-space aliases are unchanged → not deleted.
|
|
271
|
+
assert.ok(!deletedPaths.has(confluencePageByIdAliasPath('98765')));
|
|
272
|
+
assert.ok(!deletedPaths.has(confluencePageByStatePath('current', '98765')));
|
|
273
|
+
assert.ok(!deletedPaths.has(confluencePageBySpaceAliasPath('12345', '98765')));
|
|
274
|
+
// New canonical + new by-title written.
|
|
275
|
+
const writtenPaths = new Set(client.writes.map((w) => w.path));
|
|
276
|
+
assert.ok(writtenPaths.has('/confluence/spaces/12345/pages/launch-plan__98765.json'));
|
|
277
|
+
assert.ok(writtenPaths.has(confluencePageByTitleAliasPath('Launch Plan', '98765')));
|
|
278
|
+
});
|
|
279
|
+
it('skips reconciliation when client has no readFile (legacy adapters keep working)', async () => {
|
|
280
|
+
// Pre-#69 behavior: clients without readFile see no reconciliation. The
|
|
281
|
+
// adapter must still write the new alias set; it just can't clean up
|
|
282
|
+
// stale aliases. This is the "degrade gracefully" contract.
|
|
283
|
+
const client = createClient(); // no readFile defined
|
|
284
|
+
const adapter = createAdapter(client);
|
|
285
|
+
const result = await adapter.ingestWebhook('workspace-1', {
|
|
286
|
+
provider: 'confluence',
|
|
287
|
+
eventType: 'page.updated',
|
|
288
|
+
objectType: 'page',
|
|
289
|
+
objectId: '98765',
|
|
290
|
+
payload: { id: '98765', title: 'Launch Plan', status: 'current', spaceId: '12345' },
|
|
291
|
+
});
|
|
292
|
+
assert.equal(result.errors.length, 0);
|
|
293
|
+
assert.deepEqual(client.deletes, []);
|
|
294
|
+
assert.ok(client.writes.length > 0);
|
|
295
|
+
});
|
|
296
|
+
it('treats readFile errors as non-fatal and proceeds with the write fan-out', async () => {
|
|
297
|
+
// A 5xx on the reconciliation read must not block the canonical write —
|
|
298
|
+
// stale aliases are a maintenance concern, not a correctness blocker.
|
|
299
|
+
const client = {
|
|
300
|
+
writes: [],
|
|
301
|
+
deletes: [],
|
|
302
|
+
async writeFile(input) {
|
|
303
|
+
this.writes.push(input);
|
|
304
|
+
return { created: true };
|
|
305
|
+
},
|
|
306
|
+
async deleteFile(input) {
|
|
307
|
+
this.deletes.push(input);
|
|
308
|
+
},
|
|
309
|
+
async readFile() {
|
|
310
|
+
throw new Error('synthetic 503 from relayfile');
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
const adapter = createAdapter(client);
|
|
314
|
+
const result = await adapter.ingestWebhook('workspace-1', {
|
|
315
|
+
provider: 'confluence',
|
|
316
|
+
eventType: 'page.updated',
|
|
317
|
+
objectType: 'page',
|
|
318
|
+
objectId: '98765',
|
|
319
|
+
payload: { id: '98765', title: 'Launch Plan', status: 'current', spaceId: '12345' },
|
|
320
|
+
});
|
|
321
|
+
assert.equal(result.errors.length, 0);
|
|
322
|
+
assert.deepEqual(client.deletes, []); // no reconciliation deletes attempted
|
|
323
|
+
assert.ok(client.writes.length > 0); // canonical write still landed
|
|
324
|
+
});
|
|
73
325
|
it('resolves Confluence read requests to Cloud REST API v2 paths', () => {
|
|
74
326
|
assert.deepEqual(resolveConfluenceReadRequest('/confluence/pages'), {
|
|
75
327
|
action: 'list_pages',
|
|
@@ -83,7 +335,7 @@ describe('ConfluenceAdapter', () => {
|
|
|
83
335
|
endpoint: '/wiki/api/v2/pages',
|
|
84
336
|
query: { limit: '100', 'body-format': 'storage', 'space-id': '12345' },
|
|
85
337
|
});
|
|
86
|
-
assert.deepEqual(resolveConfluenceReadRequest('/confluence/pages/release-
|
|
338
|
+
assert.deepEqual(resolveConfluenceReadRequest('/confluence/pages/release-plan__98765.json'), {
|
|
87
339
|
action: 'get_page',
|
|
88
340
|
method: 'GET',
|
|
89
341
|
endpoint: '/wiki/api/v2/pages/98765',
|
|
@@ -107,7 +359,7 @@ describe('ConfluenceAdapter', () => {
|
|
|
107
359
|
});
|
|
108
360
|
});
|
|
109
361
|
it('resolves page updates and increments synced version numbers', () => {
|
|
110
|
-
assert.deepEqual(resolveConfluenceWritebackRequest('/confluence/pages/release-
|
|
362
|
+
assert.deepEqual(resolveConfluenceWritebackRequest('/confluence/pages/release-plan__98765.json', JSON.stringify({
|
|
111
363
|
provider: 'confluence',
|
|
112
364
|
objectType: 'page',
|
|
113
365
|
objectId: '98765',
|
|
@@ -134,7 +386,7 @@ describe('ConfluenceAdapter', () => {
|
|
|
134
386
|
});
|
|
135
387
|
});
|
|
136
388
|
it('resolves page deletes and rejects read-only fields', () => {
|
|
137
|
-
assert.deepEqual(resolveConfluenceDeleteRequest('/confluence/pages/release-
|
|
389
|
+
assert.deepEqual(resolveConfluenceDeleteRequest('/confluence/pages/release-plan__98765.json'), {
|
|
138
390
|
action: 'delete_page',
|
|
139
391
|
method: 'DELETE',
|
|
140
392
|
endpoint: '/wiki/api/v2/pages/98765',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confluence-adapter.test.js","sourceRoot":"","sources":["../../src/__tests__/confluence-adapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAGxC,OAAO,EACL,iBAAiB,GAGlB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,iBAAiB,CAAC;AAOzB,SAAS,YAAY;IACnB,OAAO;QACL,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,KAAK,CAAC,SAAS,CAAC,KAAK;YACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,KAAK;YACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,MAAM,GAAG,YAAY,EAAE;IAC5C,MAAM,QAAQ,GAAuB;QACnC,IAAI,EAAE,YAAY;QAClB,KAAK,CAAC,KAAK;YACT,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAO,EAAE,CAAC;QACrD,CAAC;QACD,KAAK,CAAC,WAAW;YACf,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;IACF,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACtF,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,KAAK,CACV,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,EACtE,iDAAiD,CAClD,CAAC;QACF,MAAM,CAAC,KAAK,CACV,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EACnF,yDAAyD,CAC1D,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;QAChC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE;YAC3F,IAAI,EAAE,4CAA4C;YAClD,OAAO,EAAE;gBACP,QAAQ,EAAE,YAAY;gBACtB,UAAU,EAAE,OAAO;gBACnB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE;aAC1D;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CACV,OAAO,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EACtF,yDAAyD,CAC1D,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAEtC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,aAAa,EAAE;YACxD,QAAQ,EAAE,YAAY;YACtB,SAAS,EAAE,cAAc;YACzB,UAAU,EAAE,MAAM;YAClB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE;gBACP,EAAE,EAAE,OAAO;gBACX,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE;aAClF;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,yDAAyD,CAAC,CAAC;QAChG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE;YACvD,mBAAmB,CAAC,OAAO,CAAC;SAC7B,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,SAAS,CAAC,4BAA4B,CAAC,mBAAmB,CAAC,EAAE;YAClE,MAAM,EAAE,YAAY;YACpB,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,oBAAoB;YAC9B,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE;SAClD,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,4BAA4B,CAAC,gCAAgC,CAAC,EAAE;YAC/E,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,oBAAoB;YAC9B,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE;SACvE,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,4BAA4B,CAAC,4CAA4C,CAAC,EAAE;YAC3F,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,0BAA0B;YACpC,KAAK,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE;SACzD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,SAAS,CACd,iCAAiC,CAC/B,2CAA2C,EAC3C,IAAI,CAAC,SAAS,CAAC;YACb,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,wBAAwB;SAC/B,CAAC,CACH,EACD;YACE,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,oBAAoB;YAC9B,IAAI,EAAE;gBACJ,OAAO,EAAE,OAAO;gBAChB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,wBAAwB,EAAE;aACrE;SACF,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,SAAS,CACd,iCAAiC,CAC/B,4CAA4C,EAC5C,IAAI,CAAC,SAAS,CAAC;YACb,QAAQ,EAAE,YAAY;YACtB,UAAU,EAAE,MAAM;YAClB,QAAQ,EAAE,OAAO;YACjB,WAAW,EAAE,aAAa;YAC1B,OAAO,EAAE;gBACP,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,OAAO;gBAChB,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE;gBAC/D,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE;aAC3E;SACF,CAAC,CACH,EACD;YACE,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE;gBACJ,EAAE,EAAE,OAAO;gBACX,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE;gBAC7D,OAAO,EAAE,OAAO;gBAChB,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE;aAChE;SACF,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,4CAA4C,CAAC,EAAE;YAC7F,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,0BAA0B;SACrC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CACH,iCAAiC,CAC/B,8BAA8B,EAC9B,IAAI,CAAC,SAAS,CAAC;YACb,EAAE,EAAE,OAAO;YACX,KAAK,EAAE,cAAc;YACrB,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,wBAAwB;SAC/B,CAAC,CACH,EACH,kBAAkB,CACnB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"confluence-adapter.test.js","sourceRoot":"","sources":["../../src/__tests__/confluence-adapter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-emission.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index-emission.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { buildConfluenceIndexFile } from '../index-emitter.js';
|
|
4
|
+
import { confluencePagePath, confluenceSpacePath, } from '../path-mapper.js';
|
|
5
|
+
import { confluencePageIndexRow, confluenceSpaceIndexRow } from '../queries.js';
|
|
6
|
+
describe('confluence index emission', () => {
|
|
7
|
+
it('emits deterministic page and space indexes sorted by updated DESC, then id ASC', () => {
|
|
8
|
+
const pageRows = [
|
|
9
|
+
confluencePageIndexRow({
|
|
10
|
+
id: '98765',
|
|
11
|
+
title: 'Release Plan',
|
|
12
|
+
spaceId: '12345',
|
|
13
|
+
status: 'current',
|
|
14
|
+
createdAt: '2026-04-01T08:00:00.000Z',
|
|
15
|
+
}),
|
|
16
|
+
confluencePageIndexRow({
|
|
17
|
+
id: '99999',
|
|
18
|
+
title: 'Draft notes',
|
|
19
|
+
spaceId: '12345',
|
|
20
|
+
status: 'draft',
|
|
21
|
+
createdAt: '2026-04-03T10:00:00.000Z',
|
|
22
|
+
}),
|
|
23
|
+
];
|
|
24
|
+
const spaceRows = [
|
|
25
|
+
confluenceSpaceIndexRow({
|
|
26
|
+
id: '12345',
|
|
27
|
+
key: 'ENG',
|
|
28
|
+
name: 'Engineering',
|
|
29
|
+
createdAt: '2026-04-01T08:00:00.000Z',
|
|
30
|
+
}),
|
|
31
|
+
confluenceSpaceIndexRow({
|
|
32
|
+
id: '67890',
|
|
33
|
+
key: 'OPS',
|
|
34
|
+
name: 'Operations',
|
|
35
|
+
createdAt: '2026-04-03T10:00:00.000Z',
|
|
36
|
+
}),
|
|
37
|
+
];
|
|
38
|
+
const pageIndex = buildConfluenceIndexFile('pages', pageRows);
|
|
39
|
+
const pageIndexAgain = buildConfluenceIndexFile('pages', [...pageRows].reverse());
|
|
40
|
+
const spaceIndex = buildConfluenceIndexFile('spaces', spaceRows);
|
|
41
|
+
assert.deepEqual(pageIndex, pageIndexAgain);
|
|
42
|
+
assert.equal(pageIndex.path, '/confluence/pages/_index.json');
|
|
43
|
+
assert.equal(spaceIndex.path, '/confluence/spaces/_index.json');
|
|
44
|
+
assert.equal(pageIndex.contentType, 'application/json; charset=utf-8');
|
|
45
|
+
assert.deepEqual(JSON.parse(pageIndex.content), [
|
|
46
|
+
{ id: '99999', title: 'Draft notes', updated: '2026-04-03T10:00:00.000Z', spaceId: '12345', status: 'draft' },
|
|
47
|
+
{ id: '98765', title: 'Release Plan', updated: '2026-04-01T08:00:00.000Z', spaceId: '12345', status: 'current' },
|
|
48
|
+
]);
|
|
49
|
+
assert.deepEqual(JSON.parse(spaceIndex.content), [
|
|
50
|
+
{ id: '67890', title: 'Operations', updated: '2026-04-03T10:00:00.000Z', key: 'OPS' },
|
|
51
|
+
{ id: '12345', title: 'Engineering', updated: '2026-04-01T08:00:00.000Z', key: 'ENG' },
|
|
52
|
+
]);
|
|
53
|
+
assert.equal(confluencePagePath('98765'), '/confluence/pages/98765.json');
|
|
54
|
+
assert.equal(confluenceSpacePath('12345'), '/confluence/spaces/12345.json');
|
|
55
|
+
});
|
|
56
|
+
it('emits an empty index when a Confluence bucket has no records', () => {
|
|
57
|
+
const file = buildConfluenceIndexFile('pages', []);
|
|
58
|
+
assert.equal(file.path, '/confluence/pages/_index.json');
|
|
59
|
+
assert.deepEqual(JSON.parse(file.content), []);
|
|
60
|
+
});
|
|
61
|
+
it('re-exports the index and layout helpers from the barrel', async () => {
|
|
62
|
+
const barrel = await import('../index.js');
|
|
63
|
+
assert.equal(barrel.buildConfluenceIndexFile, buildConfluenceIndexFile);
|
|
64
|
+
assert.equal(typeof barrel.confluenceLayoutPromptFile, 'function');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=index-emission.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-emission.test.js","sourceRoot":"","sources":["../../src/__tests__/index-emission.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-prompt.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/layout-prompt.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { confluenceLayoutPromptFile } from '../layout-prompt.js';
|
|
4
|
+
describe('confluence layout prompt', () => {
|
|
5
|
+
it('emits the integration layout guide at the confluence root', () => {
|
|
6
|
+
const file = confluenceLayoutPromptFile();
|
|
7
|
+
assert.equal(file.path, '/confluence/LAYOUT.md');
|
|
8
|
+
assert.equal(file.contentType, 'text/markdown; charset=utf-8');
|
|
9
|
+
assert.match(file.content, /\bls\b/u);
|
|
10
|
+
assert.match(file.content, /__/u);
|
|
11
|
+
assert.match(file.content, /_index\.json/u);
|
|
12
|
+
assert.match(file.content, /\/confluence\/pages\//u);
|
|
13
|
+
assert.match(file.content, /\/confluence\/spaces\//u);
|
|
14
|
+
assert.match(file.content, /by-title/u);
|
|
15
|
+
assert.match(file.content, /by-id/u);
|
|
16
|
+
assert.match(file.content, /by-state/u);
|
|
17
|
+
assert.match(file.content, /by-space/u);
|
|
18
|
+
assert.match(file.content, /by-parent/u);
|
|
19
|
+
assert.match(file.content, /by-key/u);
|
|
20
|
+
assert.match(file.content, /jq /u);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=layout-prompt.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-prompt.test.js","sourceRoot":"","sources":["../../src/__tests__/layout-prompt.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEjE,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,IAAI,GAAG,0BAA0B,EAAE,CAAC;QAE1C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,8BAA8B,CAAC,CAAC;QAC/D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,yBAAyB,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-mapper.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/path-mapper.test.ts"],"names":[],"mappings":""}
|