mapples 0.1.0
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/README.md +42 -0
- package/dist/adopt/mappingTable.d.ts +1 -0
- package/dist/adopt/mappingTable.js +18 -0
- package/dist/adopt/mappingTable.js.map +1 -0
- package/dist/adopt/scan.d.ts +4 -0
- package/dist/adopt/scan.js +51 -0
- package/dist/adopt/scan.js.map +1 -0
- package/dist/api/canonical.d.ts +12 -0
- package/dist/api/canonical.js +65 -0
- package/dist/api/canonical.js.map +1 -0
- package/dist/api/client.d.ts +37 -0
- package/dist/api/client.js +79 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/commit.d.ts +18 -0
- package/dist/api/commit.js +47 -0
- package/dist/api/commit.js.map +1 -0
- package/dist/api/errors.d.ts +30 -0
- package/dist/api/errors.js +74 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/objects.d.ts +55 -0
- package/dist/api/objects.js +48 -0
- package/dist/api/objects.js.map +1 -0
- package/dist/api/poll.d.ts +10 -0
- package/dist/api/poll.js +33 -0
- package/dist/api/poll.js.map +1 -0
- package/dist/assets/assets.d.ts +15 -0
- package/dist/assets/assets.js +110 -0
- package/dist/assets/assets.js.map +1 -0
- package/dist/codegen/emit.d.ts +8 -0
- package/dist/codegen/emit.js +33 -0
- package/dist/codegen/emit.js.map +1 -0
- package/dist/codegen/format.d.ts +8 -0
- package/dist/codegen/format.js +24 -0
- package/dist/codegen/format.js.map +1 -0
- package/dist/codegen/imports.d.ts +15 -0
- package/dist/codegen/imports.js +79 -0
- package/dist/codegen/imports.js.map +1 -0
- package/dist/commands/adopt.d.ts +2 -0
- package/dist/commands/adopt.js +11 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.js +13 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +11 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/impl/adoptImpl.d.ts +1 -0
- package/dist/commands/impl/adoptImpl.js +24 -0
- package/dist/commands/impl/adoptImpl.js.map +1 -0
- package/dist/commands/impl/createImpl.d.ts +2 -0
- package/dist/commands/impl/createImpl.js +65 -0
- package/dist/commands/impl/createImpl.js.map +1 -0
- package/dist/commands/impl/doctorImpl.d.ts +1 -0
- package/dist/commands/impl/doctorImpl.js +25 -0
- package/dist/commands/impl/doctorImpl.js.map +1 -0
- package/dist/commands/impl/initImpl.d.ts +1 -0
- package/dist/commands/impl/initImpl.js +65 -0
- package/dist/commands/impl/initImpl.js.map +1 -0
- package/dist/commands/impl/syncImpl.d.ts +1 -0
- package/dist/commands/impl/syncImpl.js +34 -0
- package/dist/commands/impl/syncImpl.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +13 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +13 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/merge/applyOps.d.ts +11 -0
- package/dist/merge/applyOps.js +134 -0
- package/dist/merge/applyOps.js.map +1 -0
- package/dist/merge/deprecate.d.ts +3 -0
- package/dist/merge/deprecate.js +28 -0
- package/dist/merge/deprecate.js.map +1 -0
- package/dist/merge/diff.d.ts +15 -0
- package/dist/merge/diff.js +124 -0
- package/dist/merge/diff.js.map +1 -0
- package/dist/merge/mergePatch.d.ts +8 -0
- package/dist/merge/mergePatch.js +63 -0
- package/dist/merge/mergePatch.js.map +1 -0
- package/dist/merge/threeWay.d.ts +15 -0
- package/dist/merge/threeWay.js +242 -0
- package/dist/merge/threeWay.js.map +1 -0
- package/dist/merge/toEdits.d.ts +3 -0
- package/dist/merge/toEdits.js +92 -0
- package/dist/merge/toEdits.js.map +1 -0
- package/dist/nav/routeTree.d.ts +11 -0
- package/dist/nav/routeTree.js +182 -0
- package/dist/nav/routeTree.js.map +1 -0
- package/dist/project/baseStore.d.ts +19 -0
- package/dist/project/baseStore.js +78 -0
- package/dist/project/baseStore.js.map +1 -0
- package/dist/project/config.d.ts +13 -0
- package/dist/project/config.js +28 -0
- package/dist/project/config.js.map +1 -0
- package/dist/project/head.d.ts +6 -0
- package/dist/project/head.js +22 -0
- package/dist/project/head.js.map +1 -0
- package/dist/project/outbox.d.ts +14 -0
- package/dist/project/outbox.js +31 -0
- package/dist/project/outbox.js.map +1 -0
- package/dist/project/pagesMap.d.ts +8 -0
- package/dist/project/pagesMap.js +20 -0
- package/dist/project/pagesMap.js.map +1 -0
- package/dist/services/adoptService.d.ts +11 -0
- package/dist/services/adoptService.js +197 -0
- package/dist/services/adoptService.js.map +1 -0
- package/dist/services/doctorService.d.ts +11 -0
- package/dist/services/doctorService.js +135 -0
- package/dist/services/doctorService.js.map +1 -0
- package/dist/services/scaffold.d.ts +7 -0
- package/dist/services/scaffold.js +56 -0
- package/dist/services/scaffold.js.map +1 -0
- package/dist/services/syncEngine.d.ts +11 -0
- package/dist/services/syncEngine.js +618 -0
- package/dist/services/syncEngine.js.map +1 -0
- package/dist/style/theme.d.ts +12 -0
- package/dist/style/theme.js +125 -0
- package/dist/style/theme.js.map +1 -0
- package/dist/types.d.ts +136 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/report.d.ts +24 -0
- package/dist/ui/report.js +50 -0
- package/dist/ui/report.js.map +1 -0
- package/package.json +38 -0
- package/templates/app/_layout.tsx +5 -0
- package/templates/app.json +15 -0
- package/templates/babel.config.js +8 -0
- package/templates/gitignore +7 -0
- package/templates/mapples/sid.d.ts +13 -0
- package/templates/package.json +30 -0
- package/templates/tsconfig.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# mapples
|
|
2
|
+
|
|
3
|
+
Two-way code sync between **Mapples Creator** and your Expo project (RFC-0002, over the JPATCH/1
|
|
4
|
+
protocol from RFC-0001). Screens live as generated, editable TSX in your repo — `$sid`-tagged JSX
|
|
5
|
+
is the code-side truth, extracted back into RenderDom and merged 3-way against the canvas.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx mapples create my-app --project <projectId> # scaffold a connected Expo app
|
|
9
|
+
npx mapples init --project <projectId> # link an existing Expo app
|
|
10
|
+
npx mapples sync # pull + push in one run
|
|
11
|
+
npx mapples adopt app/settings.tsx # force-adopt a plain RN screen
|
|
12
|
+
npx mapples doctor # $sid lint + drift check
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What sync does
|
|
16
|
+
|
|
17
|
+
1. Extracts every `*Mapplet.tsx` (`$sid` JSX → RenderDom; hooks/handlers/wrappers are invisible).
|
|
18
|
+
2. Pulls the delta since the last synced HEAD (`GET ?since`, cold bootstrap when too far behind).
|
|
19
|
+
3. 3-way merges per `$sid` node, per prop key: disjoint edits auto-merge; same-prop or
|
|
20
|
+
delete-vs-edit conflicts **deprecate** the file (`_depr_<ts>_<name>.tsx.old` — your version is
|
|
21
|
+
never overwritten or deleted) and regenerate it from Creator.
|
|
22
|
+
4. Writes remote changes into your files **surgically** — user code is preserved byte-for-byte.
|
|
23
|
+
5. Pushes your edits as one JPATCH transaction; 409s rebase and retry with the same txId; going
|
|
24
|
+
offline parks ops in `.mapples/outbox/` and drains them next run.
|
|
25
|
+
6. Rewrites client-minted `c_` ids to server ids from the response `idMap`.
|
|
26
|
+
|
|
27
|
+
Also synced: navigation ↔ `app/(mapples)/` route tree (layouts CLI-owned, wrappers create-once —
|
|
28
|
+
rename a wrapper file to rename the route), style ↔ `mapples/theme.ts`, and assets ↔
|
|
29
|
+
`mapples/assets/` + generated `assetMap.ts`.
|
|
30
|
+
|
|
31
|
+
## Project state
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
.mapples/
|
|
35
|
+
config.json # projectId, apiUrl, appDir, routesDir
|
|
36
|
+
head # last synced HEAD + seq
|
|
37
|
+
pages.json # pageId <-> file mapping
|
|
38
|
+
base/ # canonical JSON per entity at the last synced HEAD
|
|
39
|
+
outbox/ # parked transactions (offline)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Auth: set `MAPPLES_TOKEN` in the environment.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PRIMITIVE_MAPPING: Record<string, string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// THE primitive mapping table (RFC-0002 §2) — react-native element name ->
|
|
2
|
+
// Mapples registered component type. Target names verified against the
|
|
3
|
+
// @mapples/ui schematics registry in mapples-open-kit (canonical), 2026-09-01:
|
|
4
|
+
// the registered types are View, Text, Image, ScrollView, Button, TextInput,
|
|
5
|
+
// FlatList (no "Styled*"/"Input"/"List" variants exist).
|
|
6
|
+
// Swappable + snapshot-tested, same pattern as the figma-connector heuristics.
|
|
7
|
+
export const PRIMITIVE_MAPPING = {
|
|
8
|
+
View: 'View',
|
|
9
|
+
Text: 'Text',
|
|
10
|
+
Image: 'Image',
|
|
11
|
+
ScrollView: 'ScrollView',
|
|
12
|
+
SafeAreaView: 'SafeAreaView',
|
|
13
|
+
Pressable: 'Button',
|
|
14
|
+
TouchableOpacity: 'Button',
|
|
15
|
+
TextInput: 'TextInput',
|
|
16
|
+
FlatList: 'FlatList',
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=mappingTable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mappingTable.js","sourceRoot":"","sources":["../../src/adopt/mappingTable.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,uEAAuE;AACvE,+EAA+E;AAC/E,6EAA6E;AAC7E,yDAAyD;AACzD,+EAA+E;AAE/E,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,QAAQ;IACnB,gBAAgB,EAAE,QAAQ;IAC1B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;CACrB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PagesMap } from '../project/pagesMap.js';
|
|
2
|
+
import type { AdoptCandidate } from '../types.js';
|
|
3
|
+
export declare function scanForCandidates(root: string, pagesMap: PagesMap): AdoptCandidate[];
|
|
4
|
+
export declare function toRelative(root: string, file: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Adoption scan: exported React components under app/ + mapples/ with no $sid
|
|
2
|
+
// and absent from the pages manifest. Unadopted files are never touched.
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { join, relative, sep } from 'node:path';
|
|
5
|
+
import { scanComponents } from '@mapples/sid-ast';
|
|
6
|
+
const SCAN_DIRS = ['app', 'mapples'];
|
|
7
|
+
export function scanForCandidates(root, pagesMap) {
|
|
8
|
+
const managed = new Set(Object.values(pagesMap).map((m) => m.file));
|
|
9
|
+
const candidates = [];
|
|
10
|
+
const walk = (dir) => {
|
|
11
|
+
const abs = join(root, dir);
|
|
12
|
+
if (!existsSync(abs))
|
|
13
|
+
return;
|
|
14
|
+
for (const entry of readdirSync(abs, { withFileTypes: true })) {
|
|
15
|
+
const rel = join(dir, entry.name);
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.'))
|
|
18
|
+
continue;
|
|
19
|
+
walk(rel);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (!entry.name.endsWith('.tsx'))
|
|
23
|
+
continue;
|
|
24
|
+
if (entry.name === '_layout.tsx' || entry.name.startsWith('_depr_'))
|
|
25
|
+
continue;
|
|
26
|
+
if (managed.has(rel))
|
|
27
|
+
continue;
|
|
28
|
+
const source = readFileSync(join(root, rel), 'utf8');
|
|
29
|
+
const info = scanComponents(source);
|
|
30
|
+
if (info.hasSid || !info.hasJsx)
|
|
31
|
+
continue;
|
|
32
|
+
if (info.markers.length > 0)
|
|
33
|
+
continue; // route wrappers / generated files
|
|
34
|
+
const componentName = info.exportedComponents[0];
|
|
35
|
+
if (!componentName)
|
|
36
|
+
continue;
|
|
37
|
+
candidates.push({
|
|
38
|
+
file: rel,
|
|
39
|
+
componentName,
|
|
40
|
+
kind: rel.split(sep)[0] === 'app' ? 'page' : 'component',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
for (const dir of SCAN_DIRS)
|
|
45
|
+
walk(dir);
|
|
46
|
+
return candidates.sort((a, b) => (a.file < b.file ? -1 : 1));
|
|
47
|
+
}
|
|
48
|
+
export function toRelative(root, file) {
|
|
49
|
+
return relative(root, file);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/adopt/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const HASH_PREFIX = "b3:";
|
|
2
|
+
export declare const PREFIX_HEX = 20;
|
|
3
|
+
/**
|
|
4
|
+
* Compare two strings by their UTF-8 encoded bytes. UTF-8 byte order equals
|
|
5
|
+
* Unicode code-point order, so we compare code points — NOT UTF-16 code units
|
|
6
|
+
* (naive `sort()`), which diverge above the BMP (e.g. U+FF61 vs U+1F341).
|
|
7
|
+
*/
|
|
8
|
+
export declare function compareUtf8(a: string, b: string): number;
|
|
9
|
+
export declare function canonicalJson(obj: unknown): string;
|
|
10
|
+
export declare function canonicalBytes(obj: unknown): Uint8Array;
|
|
11
|
+
/** `"b3:" + hex(blake3(canonical_bytes(obj)))[:20]` — the 80-bit wire prefix. */
|
|
12
|
+
export declare function contentHash(obj: unknown): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Canonical JSON + blake3 content hashing — RFC-0001 §4.2/§4.3, byte-identical
|
|
2
|
+
// with the Python reference in rfc-0001-implementation-guide §3. Conformance is
|
|
3
|
+
// pinned by the shared vectors in test/fixtures/vectors/canonical.json.
|
|
4
|
+
import { blake3 } from '@noble/hashes/blake3';
|
|
5
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
6
|
+
export const HASH_PREFIX = 'b3:';
|
|
7
|
+
export const PREFIX_HEX = 20; // 80 bits on the wire
|
|
8
|
+
/**
|
|
9
|
+
* Compare two strings by their UTF-8 encoded bytes. UTF-8 byte order equals
|
|
10
|
+
* Unicode code-point order, so we compare code points — NOT UTF-16 code units
|
|
11
|
+
* (naive `sort()`), which diverge above the BMP (e.g. U+FF61 vs U+1F341).
|
|
12
|
+
*/
|
|
13
|
+
export function compareUtf8(a, b) {
|
|
14
|
+
let i = 0;
|
|
15
|
+
let j = 0;
|
|
16
|
+
while (i < a.length && j < b.length) {
|
|
17
|
+
const ca = a.codePointAt(i); // reason: i < a.length guarantees a value
|
|
18
|
+
const cb = b.codePointAt(j); // reason: j < b.length guarantees a value
|
|
19
|
+
if (ca !== cb)
|
|
20
|
+
return ca < cb ? -1 : 1;
|
|
21
|
+
i += ca > 0xffff ? 2 : 1;
|
|
22
|
+
j += cb > 0xffff ? 2 : 1;
|
|
23
|
+
}
|
|
24
|
+
return a.length - i === 0 && b.length - j === 0 ? 0 : a.length - i === 0 ? -1 : 1;
|
|
25
|
+
}
|
|
26
|
+
function emit(v) {
|
|
27
|
+
if (v === null)
|
|
28
|
+
return 'null';
|
|
29
|
+
const t = typeof v;
|
|
30
|
+
if (t === 'boolean')
|
|
31
|
+
return v ? 'true' : 'false';
|
|
32
|
+
if (t === 'number') {
|
|
33
|
+
const n = v;
|
|
34
|
+
if (!Number.isFinite(n))
|
|
35
|
+
throw new Error('NaN/Inf not JSON');
|
|
36
|
+
// JS numbers are doubles; JSON.stringify already emits shortest round-trip
|
|
37
|
+
// form and collapses integral floats (1.0 -> "1").
|
|
38
|
+
return JSON.stringify(n);
|
|
39
|
+
}
|
|
40
|
+
if (t === 'string') {
|
|
41
|
+
// JSON.stringify escapes `"`, `\` and control chars, and emits non-ASCII
|
|
42
|
+
// raw — matching Python json.dumps(ensure_ascii=False).
|
|
43
|
+
return JSON.stringify(v);
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(v))
|
|
46
|
+
return '[' + v.map(emit).join(',') + ']';
|
|
47
|
+
if (t === 'object') {
|
|
48
|
+
const entries = Object.entries(v).filter(([, x]) => x !== undefined);
|
|
49
|
+
entries.sort(([a], [b]) => compareUtf8(a, b));
|
|
50
|
+
return '{' + entries.map(([k, x]) => JSON.stringify(k) + ':' + emit(x)).join(',') + '}';
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`non-JSON type: ${t}`);
|
|
53
|
+
}
|
|
54
|
+
export function canonicalJson(obj) {
|
|
55
|
+
return emit(obj);
|
|
56
|
+
}
|
|
57
|
+
export function canonicalBytes(obj) {
|
|
58
|
+
return new TextEncoder().encode(emit(obj));
|
|
59
|
+
}
|
|
60
|
+
/** `"b3:" + hex(blake3(canonical_bytes(obj)))[:20]` — the 80-bit wire prefix. */
|
|
61
|
+
export function contentHash(obj) {
|
|
62
|
+
const digest = blake3(canonicalBytes(obj));
|
|
63
|
+
return HASH_PREFIX + bytesToHex(digest).slice(0, PREFIX_HEX);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=canonical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../src/api/canonical.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type AxiosInstance } from 'axios';
|
|
2
|
+
import type { Bootstrap, CommitOk, Delta, Envelope } from '../types.js';
|
|
3
|
+
export interface JpatchClientOptions {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
projectId: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
actor?: string;
|
|
8
|
+
origin?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Thin, faithful JPATCH/1 transport (RFC-0001 §5–§8). No retry logic here —
|
|
12
|
+
* commit.ts owns the retry-same-txId contract, poll.ts owns delta paging.
|
|
13
|
+
*/
|
|
14
|
+
export declare class JpatchClient {
|
|
15
|
+
readonly http: AxiosInstance;
|
|
16
|
+
readonly projectId: string;
|
|
17
|
+
readonly actor: string | undefined;
|
|
18
|
+
readonly origin: string | undefined;
|
|
19
|
+
constructor(opts: JpatchClientOptions);
|
|
20
|
+
private path;
|
|
21
|
+
commitRaw(envelope: Envelope): Promise<CommitOk>;
|
|
22
|
+
pullOnce(since: string): Promise<Delta>;
|
|
23
|
+
bootstrap(): Promise<Bootstrap>;
|
|
24
|
+
getObject(hash: string): Promise<unknown>;
|
|
25
|
+
/** Binary blob fetch (asset bodies, addressed as `sha256:<hex>`). */
|
|
26
|
+
getObjectBytes(hash: string): Promise<Uint8Array>;
|
|
27
|
+
/** Which of these content hashes is the store missing? Returns hash -> signed PUT URL. */
|
|
28
|
+
have(hashes: string[]): Promise<Record<string, string>>;
|
|
29
|
+
history(limit?: number): Promise<{
|
|
30
|
+
seq: number;
|
|
31
|
+
head: string;
|
|
32
|
+
actor: string;
|
|
33
|
+
ts: string;
|
|
34
|
+
summary: string;
|
|
35
|
+
}[]>;
|
|
36
|
+
}
|
|
37
|
+
export declare function isNetworkError(err: unknown): boolean;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import axios, { AxiosError } from 'axios';
|
|
2
|
+
import { errorFromBody } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Thin, faithful JPATCH/1 transport (RFC-0001 §5–§8). No retry logic here —
|
|
5
|
+
* commit.ts owns the retry-same-txId contract, poll.ts owns delta paging.
|
|
6
|
+
*/
|
|
7
|
+
export class JpatchClient {
|
|
8
|
+
http;
|
|
9
|
+
projectId;
|
|
10
|
+
actor;
|
|
11
|
+
origin;
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
this.projectId = opts.projectId;
|
|
14
|
+
this.actor = opts.actor;
|
|
15
|
+
this.origin = opts.origin;
|
|
16
|
+
this.http = axios.create({
|
|
17
|
+
baseURL: opts.baseUrl.replace(/\/+$/, ''),
|
|
18
|
+
headers: opts.token ? { Authorization: `Bearer ${opts.token}` } : {},
|
|
19
|
+
// We map error bodies ourselves; axios must hand them to us.
|
|
20
|
+
validateStatus: () => true,
|
|
21
|
+
timeout: 30_000,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
path(suffix = '') {
|
|
25
|
+
return `/${this.projectId}/canvas${suffix}`;
|
|
26
|
+
}
|
|
27
|
+
async commitRaw(envelope) {
|
|
28
|
+
const res = await this.http.post(this.path(), envelope);
|
|
29
|
+
if (res.status === 200 || res.status === 201)
|
|
30
|
+
return res.data;
|
|
31
|
+
throw errorFromBody(res.status, res.data);
|
|
32
|
+
}
|
|
33
|
+
async pullOnce(since) {
|
|
34
|
+
const res = await this.http.get(this.path(), { params: { since } });
|
|
35
|
+
if (res.status === 200)
|
|
36
|
+
return res.data;
|
|
37
|
+
throw errorFromBody(res.status, res.data);
|
|
38
|
+
}
|
|
39
|
+
async bootstrap() {
|
|
40
|
+
const res = await this.http.get(this.path());
|
|
41
|
+
if (res.status === 200)
|
|
42
|
+
return res.data;
|
|
43
|
+
throw errorFromBody(res.status, res.data);
|
|
44
|
+
}
|
|
45
|
+
async getObject(hash) {
|
|
46
|
+
const res = await this.http.get(this.path(`/objects/${encodeURIComponent(hash)}`));
|
|
47
|
+
if (res.status === 200)
|
|
48
|
+
return res.data;
|
|
49
|
+
throw errorFromBody(res.status, res.data);
|
|
50
|
+
}
|
|
51
|
+
/** Binary blob fetch (asset bodies, addressed as `sha256:<hex>`). */
|
|
52
|
+
async getObjectBytes(hash) {
|
|
53
|
+
const res = await this.http.get(this.path(`/objects/${encodeURIComponent(hash)}`), {
|
|
54
|
+
responseType: 'arraybuffer',
|
|
55
|
+
});
|
|
56
|
+
if (res.status === 200)
|
|
57
|
+
return new Uint8Array(res.data);
|
|
58
|
+
throw errorFromBody(res.status, JSON.parse(Buffer.from(res.data).toString('utf8')));
|
|
59
|
+
}
|
|
60
|
+
/** Which of these content hashes is the store missing? Returns hash -> signed PUT URL. */
|
|
61
|
+
async have(hashes) {
|
|
62
|
+
if (hashes.length > 500)
|
|
63
|
+
throw new Error('objects/have accepts at most 500 hashes per call');
|
|
64
|
+
const res = await this.http.post(this.path('/objects/have'), hashes);
|
|
65
|
+
if (res.status === 200)
|
|
66
|
+
return res.data;
|
|
67
|
+
throw errorFromBody(res.status, res.data);
|
|
68
|
+
}
|
|
69
|
+
async history(limit = 50) {
|
|
70
|
+
const res = await this.http.get(this.path('/history'), { params: { limit } });
|
|
71
|
+
if (res.status === 200)
|
|
72
|
+
return res.data;
|
|
73
|
+
throw errorFromBody(res.status, res.data);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function isNetworkError(err) {
|
|
77
|
+
return err instanceof AxiosError && !err.response;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CommitOk, Envelope, JpatchOp } from '../types.js';
|
|
2
|
+
import type { JpatchClient } from './client.js';
|
|
3
|
+
export declare function mintTxId(): string;
|
|
4
|
+
export declare function mintCid(): string;
|
|
5
|
+
export interface CommitParams {
|
|
6
|
+
base: string;
|
|
7
|
+
ops: JpatchOp[];
|
|
8
|
+
txId?: string;
|
|
9
|
+
revertTo?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function buildEnvelope(client: JpatchClient, params: CommitParams): Envelope;
|
|
12
|
+
/**
|
|
13
|
+
* POST the transaction, retrying the SAME txId on transport failures (the
|
|
14
|
+
* server's txId tombstone makes this safe — a replay returns the original
|
|
15
|
+
* result). RFC-0001 §7.3. 409 HEAD_MISMATCH is NOT retried here: the caller
|
|
16
|
+
* must rebase and call again with the same txId (merge/rebase.ts owns that loop).
|
|
17
|
+
*/
|
|
18
|
+
export declare function commitWithTransportRetry(client: JpatchClient, envelope: Envelope, maxAttempts?: number): Promise<CommitOk>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ulid } from 'ulid';
|
|
2
|
+
import { isNetworkError } from './client.js';
|
|
3
|
+
export function mintTxId() {
|
|
4
|
+
return `tx_${ulid()}`;
|
|
5
|
+
}
|
|
6
|
+
export function mintCid() {
|
|
7
|
+
return `c_${ulid()}`;
|
|
8
|
+
}
|
|
9
|
+
export function buildEnvelope(client, params) {
|
|
10
|
+
const envelope = {
|
|
11
|
+
protocol: 'jpatch/1',
|
|
12
|
+
schema: 1,
|
|
13
|
+
txId: params.txId ?? mintTxId(),
|
|
14
|
+
base: params.base,
|
|
15
|
+
ops: params.ops,
|
|
16
|
+
meta: {
|
|
17
|
+
...(client.actor ? { actor: client.actor } : {}),
|
|
18
|
+
...(client.origin ? { origin: client.origin } : {}),
|
|
19
|
+
ts: new Date().toISOString(),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
if (params.revertTo !== undefined)
|
|
23
|
+
envelope.revertTo = params.revertTo;
|
|
24
|
+
return envelope;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* POST the transaction, retrying the SAME txId on transport failures (the
|
|
28
|
+
* server's txId tombstone makes this safe — a replay returns the original
|
|
29
|
+
* result). RFC-0001 §7.3. 409 HEAD_MISMATCH is NOT retried here: the caller
|
|
30
|
+
* must rebase and call again with the same txId (merge/rebase.ts owns that loop).
|
|
31
|
+
*/
|
|
32
|
+
export async function commitWithTransportRetry(client, envelope, maxAttempts = 5) {
|
|
33
|
+
let lastErr;
|
|
34
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
35
|
+
try {
|
|
36
|
+
return await client.commitRaw(envelope);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (!isNetworkError(err))
|
|
40
|
+
throw err;
|
|
41
|
+
lastErr = err;
|
|
42
|
+
await new Promise((r) => setTimeout(r, Math.min(2 ** attempt * 250, 4_000)));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw lastErr;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit.js","sourceRoot":"","sources":["../../src/api/commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,UAAU,QAAQ;IACtB,OAAO,MAAM,IAAI,EAAE,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,KAAK,IAAI,EAAE,EAAE,CAAC;AACvB,CAAC;AASD,MAAM,UAAU,aAAa,CAAC,MAAoB,EAAE,MAAoB;IACtE,MAAM,QAAQ,GAAa;QACzB,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC;QACT,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,QAAQ,EAAE;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,IAAI,EAAE;YACJ,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnD,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAC7B;KACF,CAAC;IACF,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;QAAE,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACvE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAoB,EACpB,QAAkB,EAClB,WAAW,GAAG,CAAC;IAEf,IAAI,OAAgB,CAAC;IACrB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAC;YACpC,OAAO,GAAG,GAAG,CAAC;YACd,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IACD,MAAM,OAAO,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Delta, JpatchOp } from '../types.js';
|
|
2
|
+
export type JpatchErrorCode = 'HEAD_MISMATCH' | 'HASH_MISMATCH' | 'CID_UNRESOLVED' | 'UNKNOWN_ENTITY' | 'PATCH_INVALID' | 'CHILDREN_PRESENT' | 'OBJECT_MISSING' | 'TX_TOO_LARGE' | 'PROJECT_LOCKED' | 'FORBIDDEN' | 'SNAPSHOT_REQUIRED';
|
|
3
|
+
export declare class JpatchError extends Error {
|
|
4
|
+
readonly code: JpatchErrorCode;
|
|
5
|
+
readonly http: number;
|
|
6
|
+
readonly body: Record<string, unknown>;
|
|
7
|
+
constructor(code: JpatchErrorCode, message: string, body?: Record<string, unknown>);
|
|
8
|
+
}
|
|
9
|
+
/** 409 HEAD_MISMATCH carries the current head and the delta from the stale base. */
|
|
10
|
+
export declare class HeadMismatchError extends JpatchError {
|
|
11
|
+
readonly head: string;
|
|
12
|
+
readonly delta: Delta;
|
|
13
|
+
constructor(head: string, delta: Delta);
|
|
14
|
+
}
|
|
15
|
+
/** A rebase dropped ops whose target was deleted/re-typed under us — the only user-visible conflict (RFC §7.1). */
|
|
16
|
+
export declare class OpsDroppedNotice {
|
|
17
|
+
readonly dropped: JpatchOp[];
|
|
18
|
+
constructor(dropped: JpatchOp[]);
|
|
19
|
+
}
|
|
20
|
+
export declare function isJpatchErrorBody(body: unknown): body is {
|
|
21
|
+
code: JpatchErrorCode;
|
|
22
|
+
message?: string;
|
|
23
|
+
} & Record<string, unknown>;
|
|
24
|
+
/** A response that is neither a success nor a recognized RFC-0001 error body. Never mapped, never retried. */
|
|
25
|
+
export declare class UnexpectedResponseError extends Error {
|
|
26
|
+
readonly status: number;
|
|
27
|
+
readonly responseBody: unknown;
|
|
28
|
+
constructor(status: number, responseBody: unknown);
|
|
29
|
+
}
|
|
30
|
+
export declare function errorFromBody(status: number, body: unknown): Error;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Typed errors — the full RFC-0001 §8 table plus SNAPSHOT_REQUIRED (§6 delta
|
|
2
|
+
// paging: a client too far behind, or a pruned base, must cold-bootstrap).
|
|
3
|
+
const HTTP_BY_CODE = {
|
|
4
|
+
HEAD_MISMATCH: 409,
|
|
5
|
+
HASH_MISMATCH: 409,
|
|
6
|
+
CID_UNRESOLVED: 422,
|
|
7
|
+
UNKNOWN_ENTITY: 422,
|
|
8
|
+
PATCH_INVALID: 422,
|
|
9
|
+
CHILDREN_PRESENT: 409,
|
|
10
|
+
OBJECT_MISSING: 424,
|
|
11
|
+
TX_TOO_LARGE: 413,
|
|
12
|
+
PROJECT_LOCKED: 423,
|
|
13
|
+
FORBIDDEN: 403,
|
|
14
|
+
SNAPSHOT_REQUIRED: 409,
|
|
15
|
+
};
|
|
16
|
+
export class JpatchError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
http;
|
|
19
|
+
body;
|
|
20
|
+
constructor(code, message, body = {}) {
|
|
21
|
+
super(`${code}: ${message}`);
|
|
22
|
+
this.name = 'JpatchError';
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.http = HTTP_BY_CODE[code];
|
|
25
|
+
this.body = body;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** 409 HEAD_MISMATCH carries the current head and the delta from the stale base. */
|
|
29
|
+
export class HeadMismatchError extends JpatchError {
|
|
30
|
+
head;
|
|
31
|
+
delta;
|
|
32
|
+
constructor(head, delta) {
|
|
33
|
+
super('HEAD_MISMATCH', `base is not current HEAD (now ${head})`, { head, delta });
|
|
34
|
+
this.head = head;
|
|
35
|
+
this.delta = delta;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** A rebase dropped ops whose target was deleted/re-typed under us — the only user-visible conflict (RFC §7.1). */
|
|
39
|
+
export class OpsDroppedNotice {
|
|
40
|
+
dropped;
|
|
41
|
+
constructor(dropped) {
|
|
42
|
+
this.dropped = dropped;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function isJpatchErrorBody(body) {
|
|
46
|
+
if (typeof body !== 'object' || body === null)
|
|
47
|
+
return false;
|
|
48
|
+
const code = body.code;
|
|
49
|
+
return typeof code === 'string' && Object.prototype.hasOwnProperty.call(HTTP_BY_CODE, code);
|
|
50
|
+
}
|
|
51
|
+
/** A response that is neither a success nor a recognized RFC-0001 error body. Never mapped, never retried. */
|
|
52
|
+
export class UnexpectedResponseError extends Error {
|
|
53
|
+
status;
|
|
54
|
+
responseBody;
|
|
55
|
+
constructor(status, responseBody) {
|
|
56
|
+
super(`unexpected response (HTTP ${status}): ${JSON.stringify(responseBody)}`);
|
|
57
|
+
this.status = status;
|
|
58
|
+
this.responseBody = responseBody;
|
|
59
|
+
this.name = 'UnexpectedResponseError';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function errorFromBody(status, body) {
|
|
63
|
+
if (isJpatchErrorBody(body)) {
|
|
64
|
+
const { code, message, ...rest } = body;
|
|
65
|
+
if (code === 'HEAD_MISMATCH') {
|
|
66
|
+
const head = rest.head;
|
|
67
|
+
const delta = rest.delta;
|
|
68
|
+
return new HeadMismatchError(head, delta);
|
|
69
|
+
}
|
|
70
|
+
return new JpatchError(code, typeof message === 'string' ? message : '', rest);
|
|
71
|
+
}
|
|
72
|
+
return new UnexpectedResponseError(status, body);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Bootstrap, RenderDomNode } from '../types.js';
|
|
2
|
+
import type { JpatchClient } from './client.js';
|
|
3
|
+
export interface PageEntity {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
route: string;
|
|
7
|
+
root: RenderDomNode;
|
|
8
|
+
}
|
|
9
|
+
export interface ComponentEntity {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
root: RenderDomNode;
|
|
13
|
+
}
|
|
14
|
+
export interface NavigationDocument {
|
|
15
|
+
routes: NavigationRoute[];
|
|
16
|
+
layouts: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface NavigationRoute {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
path: string;
|
|
22
|
+
pageId?: string;
|
|
23
|
+
[k: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
export interface StyleConfig {
|
|
26
|
+
theme: Record<string, unknown>;
|
|
27
|
+
typography: Record<string, unknown>;
|
|
28
|
+
sizing: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export type AssetIndex = Record<string, {
|
|
31
|
+
sha256: string;
|
|
32
|
+
mime: string;
|
|
33
|
+
w?: number;
|
|
34
|
+
h?: number;
|
|
35
|
+
name?: string;
|
|
36
|
+
}>;
|
|
37
|
+
export interface ProjectState {
|
|
38
|
+
head: string;
|
|
39
|
+
seq: number;
|
|
40
|
+
pages: Map<string, PageEntity>;
|
|
41
|
+
components: Map<string, ComponentEntity>;
|
|
42
|
+
navigation: NavigationDocument;
|
|
43
|
+
style: StyleConfig;
|
|
44
|
+
assets: AssetIndex;
|
|
45
|
+
}
|
|
46
|
+
/** Cold bootstrap: walk the manifest and fetch every blob (RFC-0001 §6). */
|
|
47
|
+
export declare function fetchProjectState(client: JpatchClient, boot: Bootstrap): Promise<ProjectState>;
|
|
48
|
+
/**
|
|
49
|
+
* Upload binary asset bodies: `objects/have` filters to genuinely missing
|
|
50
|
+
* hashes, then PUT bytes to each signed URL (RFC-0001 §6, binary assets).
|
|
51
|
+
*/
|
|
52
|
+
export declare function uploadMissingAssets(client: JpatchClient, bodies: Map<string, {
|
|
53
|
+
bytes: Uint8Array;
|
|
54
|
+
mime: string;
|
|
55
|
+
}>): Promise<string[]>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
/** Cold bootstrap: walk the manifest and fetch every blob (RFC-0001 §6). */
|
|
3
|
+
export async function fetchProjectState(client, boot) {
|
|
4
|
+
const [pagesIndex, componentsIndex, navigation, style, assets] = await Promise.all([
|
|
5
|
+
client.getObject(boot.manifest.pages),
|
|
6
|
+
client.getObject(boot.manifest.components),
|
|
7
|
+
client.getObject(boot.manifest.navigation),
|
|
8
|
+
client.getObject(boot.manifest.style),
|
|
9
|
+
client.getObject(boot.manifest.assets),
|
|
10
|
+
]);
|
|
11
|
+
const pages = new Map();
|
|
12
|
+
await Promise.all(Object.entries(pagesIndex).map(async ([id, hash]) => {
|
|
13
|
+
const blob = (await client.getObject(hash));
|
|
14
|
+
pages.set(id, { id, ...blob });
|
|
15
|
+
}));
|
|
16
|
+
const components = new Map();
|
|
17
|
+
await Promise.all(Object.entries(componentsIndex).map(async ([id, hash]) => {
|
|
18
|
+
const blob = (await client.getObject(hash));
|
|
19
|
+
components.set(id, { id, ...blob });
|
|
20
|
+
}));
|
|
21
|
+
return { head: boot.head, seq: boot.seq, pages, components, navigation, style, assets };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Upload binary asset bodies: `objects/have` filters to genuinely missing
|
|
25
|
+
* hashes, then PUT bytes to each signed URL (RFC-0001 §6, binary assets).
|
|
26
|
+
*/
|
|
27
|
+
export async function uploadMissingAssets(client, bodies) {
|
|
28
|
+
const hashes = [...bodies.keys()];
|
|
29
|
+
const uploaded = [];
|
|
30
|
+
for (let i = 0; i < hashes.length; i += 500) {
|
|
31
|
+
const missing = await client.have(hashes.slice(i, i + 500));
|
|
32
|
+
for (const [hash, putUrl] of Object.entries(missing)) {
|
|
33
|
+
const body = bodies.get(hash);
|
|
34
|
+
if (!body)
|
|
35
|
+
throw new Error(`objects/have returned a hash we never asked about: ${hash}`);
|
|
36
|
+
const res = await axios.put(putUrl, body.bytes, {
|
|
37
|
+
headers: { 'Content-Type': body.mime },
|
|
38
|
+
validateStatus: () => true,
|
|
39
|
+
});
|
|
40
|
+
if (res.status < 200 || res.status >= 300) {
|
|
41
|
+
throw new Error(`asset upload failed (HTTP ${res.status}) for ${hash}`);
|
|
42
|
+
}
|
|
43
|
+
uploaded.push(hash);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return uploaded;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=objects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objects.js","sourceRoot":"","sources":["../../src/api/objects.ts"],"names":[],"mappings":"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"}
|