@cogenta/export 0.2.3
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/LICENSE +373 -0
- package/dist/backup.d.ts +64 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +79 -0
- package/dist/backup.js.map +1 -0
- package/dist/content-export.d.ts +49 -0
- package/dist/content-export.d.ts.map +1 -0
- package/dist/content-export.js +197 -0
- package/dist/content-export.js.map +1 -0
- package/dist/content-import.d.ts +44 -0
- package/dist/content-import.d.ts.map +1 -0
- package/dist/content-import.js +221 -0
- package/dist/content-import.js.map +1 -0
- package/dist/crypto.d.ts +18 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +151 -0
- package/dist/crypto.js.map +1 -0
- package/dist/format.d.ts +133 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +71 -0
- package/dist/format.js.map +1 -0
- package/dist/gdpr.d.ts +79 -0
- package/dist/gdpr.d.ts.map +1 -0
- package/dist/gdpr.js +84 -0
- package/dist/gdpr.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/media-export.d.ts +33 -0
- package/dist/media-export.d.ts.map +1 -0
- package/dist/media-export.js +64 -0
- package/dist/media-export.js.map +1 -0
- package/dist/restore.d.ts +53 -0
- package/dist/restore.d.ts.map +1 -0
- package/dist/restore.js +173 -0
- package/dist/restore.js.map +1 -0
- package/dist/tables.d.ts +28 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +26 -0
- package/dist/tables.js.map +1 -0
- package/dist/zip-reader.d.ts +13 -0
- package/dist/zip-reader.d.ts.map +1 -0
- package/dist/zip-reader.js +97 -0
- package/dist/zip-reader.js.map +1 -0
- package/dist/zip-writer.d.ts +16 -0
- package/dist/zip-writer.d.ts.map +1 -0
- package/dist/zip-writer.js +143 -0
- package/dist/zip-writer.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { BlockZones, ContentStatus, ContentValues, Provenance, SystemFields } from '@cogenta/schema';
|
|
2
|
+
/**
|
|
3
|
+
* The export format — `export@1.0`.
|
|
4
|
+
*
|
|
5
|
+
* **A public format from the first version.** It will be read by scripts,
|
|
6
|
+
* other sites and migrations, so it is versioned and documented
|
|
7
|
+
* (`docs/04-contrats.md`) exactly like contracts A-D, even though the plan
|
|
8
|
+
* that asked for this package did not require an ADR for it (only "a format
|
|
9
|
+
* decision"). NDJSON for content — one entry, term, menu item or redirect per
|
|
10
|
+
* line, so a multi-gigabyte export never has to be held in memory to be read
|
|
11
|
+
* or written — with the manifest as the stream's own first line, so the whole
|
|
12
|
+
* export is one file rather than two that can drift apart.
|
|
13
|
+
*/
|
|
14
|
+
export declare const EXPORT_FORMAT: 'cogenta-export';
|
|
15
|
+
export declare const EXPORT_FORMAT_VERSION: '1.0';
|
|
16
|
+
export interface ExportSelection {
|
|
17
|
+
/** Collection names included. Absent means every collection the actor may read. */
|
|
18
|
+
readonly collections?: readonly string[];
|
|
19
|
+
readonly statuses?: readonly ContentStatus[];
|
|
20
|
+
readonly locales?: readonly string[];
|
|
21
|
+
/** ISO 8601. Filters on `updatedAt`. */
|
|
22
|
+
readonly from?: string;
|
|
23
|
+
readonly to?: string;
|
|
24
|
+
readonly includeTrashed?: boolean;
|
|
25
|
+
readonly includeHistory?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface ExportManifestRecord {
|
|
28
|
+
readonly kind: 'manifest';
|
|
29
|
+
readonly format: typeof EXPORT_FORMAT;
|
|
30
|
+
readonly version: typeof EXPORT_FORMAT_VERSION;
|
|
31
|
+
readonly createdAt: string;
|
|
32
|
+
readonly site: {
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly url: string;
|
|
35
|
+
};
|
|
36
|
+
readonly selection: ExportSelection;
|
|
37
|
+
readonly counts: {
|
|
38
|
+
readonly entries: number;
|
|
39
|
+
readonly terms: number;
|
|
40
|
+
readonly menus: number;
|
|
41
|
+
readonly menuItems: number;
|
|
42
|
+
readonly redirects: number;
|
|
43
|
+
readonly mediaRefs: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface ExportEntryRecord {
|
|
47
|
+
readonly kind: 'entry';
|
|
48
|
+
readonly collection: string;
|
|
49
|
+
readonly id: string;
|
|
50
|
+
readonly locale: string;
|
|
51
|
+
readonly translationOf: string | null;
|
|
52
|
+
readonly status: ContentStatus;
|
|
53
|
+
readonly deletedAt: string | null;
|
|
54
|
+
readonly version: number;
|
|
55
|
+
readonly provenance: Provenance;
|
|
56
|
+
readonly provenanceDetail: SystemFields['provenanceDetail'];
|
|
57
|
+
readonly createdAt: string;
|
|
58
|
+
readonly updatedAt: string;
|
|
59
|
+
readonly createdBy: string | null;
|
|
60
|
+
readonly updatedBy: string | null;
|
|
61
|
+
readonly publishedAt: string | null;
|
|
62
|
+
readonly values: ContentValues;
|
|
63
|
+
readonly blocks: BlockZones;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* One version of an entry's history — only emitted when `includeHistory` is
|
|
67
|
+
* set. Metadata only: `ContentStore.history()` itself does not return each
|
|
68
|
+
* version's field snapshot, only who changed what and when, which is exactly
|
|
69
|
+
* what a history *listing* needs and all this record carries.
|
|
70
|
+
*/
|
|
71
|
+
export interface ExportVersionRecord {
|
|
72
|
+
readonly kind: 'version';
|
|
73
|
+
readonly collection: string;
|
|
74
|
+
readonly entryId: string;
|
|
75
|
+
readonly version: number;
|
|
76
|
+
readonly status: ContentStatus;
|
|
77
|
+
readonly createdAt: string;
|
|
78
|
+
readonly createdBy: string | null;
|
|
79
|
+
}
|
|
80
|
+
export interface ExportTermRecord {
|
|
81
|
+
readonly kind: 'term';
|
|
82
|
+
readonly taxonomy: string;
|
|
83
|
+
readonly id: string;
|
|
84
|
+
readonly slug: string;
|
|
85
|
+
readonly parent: string | null;
|
|
86
|
+
readonly position: number;
|
|
87
|
+
readonly labels: Readonly<Record<string, string>>;
|
|
88
|
+
}
|
|
89
|
+
export interface ExportMenuRecord {
|
|
90
|
+
readonly kind: 'menu';
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
readonly locale: string;
|
|
94
|
+
readonly label: string;
|
|
95
|
+
}
|
|
96
|
+
export interface ExportMenuItemRecord {
|
|
97
|
+
readonly kind: 'menu-item';
|
|
98
|
+
readonly id: string;
|
|
99
|
+
readonly menuId: string;
|
|
100
|
+
readonly parent: string | null;
|
|
101
|
+
readonly position: number;
|
|
102
|
+
readonly label: string;
|
|
103
|
+
readonly itemKind: string;
|
|
104
|
+
readonly url: string | null;
|
|
105
|
+
readonly targetCollection: string | null;
|
|
106
|
+
readonly targetEntryId: string | null;
|
|
107
|
+
readonly openInNewTab: boolean;
|
|
108
|
+
}
|
|
109
|
+
export interface ExportRedirectRecord {
|
|
110
|
+
readonly kind: 'redirect';
|
|
111
|
+
readonly from: string;
|
|
112
|
+
readonly to: string;
|
|
113
|
+
readonly status: number;
|
|
114
|
+
readonly collection: string | null;
|
|
115
|
+
readonly entryId: string | null;
|
|
116
|
+
readonly locale: string | null;
|
|
117
|
+
readonly reason: string;
|
|
118
|
+
}
|
|
119
|
+
/** A reference to a medium used by an exported entry — never the bytes (task 1 vs task 2). */
|
|
120
|
+
export interface ExportMediaRefRecord {
|
|
121
|
+
readonly kind: 'media-ref';
|
|
122
|
+
readonly id: string;
|
|
123
|
+
readonly filename: string;
|
|
124
|
+
readonly mimeType: string;
|
|
125
|
+
readonly size: number;
|
|
126
|
+
readonly storageKey: string;
|
|
127
|
+
}
|
|
128
|
+
export type ExportRecord = ExportManifestRecord | ExportEntryRecord | ExportVersionRecord | ExportTermRecord | ExportMenuRecord | ExportMenuItemRecord | ExportRedirectRecord | ExportMediaRefRecord;
|
|
129
|
+
export declare function encodeRecord(record: ExportRecord): string;
|
|
130
|
+
/** Reads NDJSON, one `ExportRecord` per non-blank line. Throws on the first malformed line, naming it. */
|
|
131
|
+
export declare function decodeRecord(line: string, lineNumber: number): ExportRecord;
|
|
132
|
+
export declare function assertManifest(record: ExportRecord | undefined): asserts record is ExportManifestRecord;
|
|
133
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,UAAU,EACV,YAAY,EACb,MAAM,iBAAiB,CAAA;AAExB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAG,gBAAyB,CAAA;AACtD,eAAO,MAAM,qBAAqB,EAAG,KAAc,CAAA;AAEnD,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,wCAAwC;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,aAAa,CAAA;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,qBAAqB,CAAA;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9D,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAC,kBAAkB,CAAC,CAAA;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,8FAA8F;AAC9F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,mBAAmB,GACnB,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,GACpB,oBAAoB,CAAA;AAExB,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEzD;AAED,0GAA0G;AAC1G,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CA0B3E;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,GAAG,SAAS,GAC/B,OAAO,CAAC,MAAM,IAAI,oBAAoB,CAwBxC"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* The export format — `export@1.0`.
|
|
4
|
+
*
|
|
5
|
+
* **A public format from the first version.** It will be read by scripts,
|
|
6
|
+
* other sites and migrations, so it is versioned and documented
|
|
7
|
+
* (`docs/04-contrats.md`) exactly like contracts A-D, even though the plan
|
|
8
|
+
* that asked for this package did not require an ADR for it (only "a format
|
|
9
|
+
* decision"). NDJSON for content — one entry, term, menu item or redirect per
|
|
10
|
+
* line, so a multi-gigabyte export never has to be held in memory to be read
|
|
11
|
+
* or written — with the manifest as the stream's own first line, so the whole
|
|
12
|
+
* export is one file rather than two that can drift apart.
|
|
13
|
+
*/
|
|
14
|
+
export const EXPORT_FORMAT = 'cogenta-export';
|
|
15
|
+
export const EXPORT_FORMAT_VERSION = '1.0';
|
|
16
|
+
export function encodeRecord(record) {
|
|
17
|
+
return `${JSON.stringify(record)}\n`;
|
|
18
|
+
}
|
|
19
|
+
/** Reads NDJSON, one `ExportRecord` per non-blank line. Throws on the first malformed line, naming it. */
|
|
20
|
+
export function decodeRecord(line, lineNumber) {
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(line);
|
|
24
|
+
}
|
|
25
|
+
catch (cause) {
|
|
26
|
+
throw new CogentaError({
|
|
27
|
+
code: 'EXPORT_FORMAT_INVALID',
|
|
28
|
+
message: `Line ${lineNumber} of the export is not valid JSON.`,
|
|
29
|
+
hint: 'The file is corrupt or was not produced by `cogenta export`/`@cogenta/export`.',
|
|
30
|
+
cause,
|
|
31
|
+
details: { lineNumber },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (typeof parsed !== 'object' ||
|
|
35
|
+
parsed === null ||
|
|
36
|
+
typeof parsed.kind !== 'string') {
|
|
37
|
+
throw new CogentaError({
|
|
38
|
+
code: 'EXPORT_FORMAT_INVALID',
|
|
39
|
+
message: `Line ${lineNumber} of the export has no "kind".`,
|
|
40
|
+
hint: 'The file is corrupt or was not produced by `cogenta export`/`@cogenta/export`.',
|
|
41
|
+
details: { lineNumber },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
export function assertManifest(record) {
|
|
47
|
+
if (record === undefined || record.kind !== 'manifest') {
|
|
48
|
+
throw new CogentaError({
|
|
49
|
+
code: 'EXPORT_FORMAT_INVALID',
|
|
50
|
+
message: 'The export does not start with a manifest record.',
|
|
51
|
+
hint: 'The first line of a Cogenta export must be `{"kind":"manifest",…}`.',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (record.format !== EXPORT_FORMAT) {
|
|
55
|
+
throw new CogentaError({
|
|
56
|
+
code: 'EXPORT_FORMAT_INVALID',
|
|
57
|
+
message: `Unknown export format "${record.format}".`,
|
|
58
|
+
hint: `This reader only understands "${EXPORT_FORMAT}".`,
|
|
59
|
+
details: { format: record.format },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (record.version.split('.')[0] !== EXPORT_FORMAT_VERSION.split('.')[0]) {
|
|
63
|
+
throw new CogentaError({
|
|
64
|
+
code: 'BACKUP_VERSION_UNSUPPORTED',
|
|
65
|
+
message: `Export format ${record.version} is not compatible with this reader (${EXPORT_FORMAT_VERSION}).`,
|
|
66
|
+
hint: 'A major version bump of the export format is a breaking change; use a matching version of @cogenta/export.',
|
|
67
|
+
details: { found: record.version, supported: EXPORT_FORMAT_VERSION },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAS5C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAyB,CAAA;AACtD,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAc,CAAA;AAkInD,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAA;AACtC,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,UAAkB;IAC3D,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,QAAQ,UAAU,mCAAmC;YAC9D,IAAI,EAAE,gFAAgF;YACtF,KAAK;YACL,OAAO,EAAE,EAAE,UAAU,EAAE;SACxB,CAAC,CAAA;IACJ,CAAC;IACD,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,OAAQ,MAA6B,CAAC,IAAI,KAAK,QAAQ,EACvD,CAAC;QACD,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,QAAQ,UAAU,+BAA+B;YAC1D,IAAI,EAAE,gFAAgF;YACtF,OAAO,EAAE,EAAE,UAAU,EAAE;SACxB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,MAAsB,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAgC;IAEhC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACvD,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,mDAAmD;YAC5D,IAAI,EAAE,qEAAqE;SAC5E,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACpC,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,0BAA0B,MAAM,CAAC,MAAM,IAAI;YACpD,IAAI,EAAE,iCAAiC,aAAa,IAAI;YACxD,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;SACnC,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,4BAA4B;YAClC,OAAO,EAAE,iBAAiB,MAAM,CAAC,OAAO,wCAAwC,qBAAqB,IAAI;YACzG,IAAI,EAAE,4GAA4G;YAClH,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE;SACrE,CAAC,CAAA;IACJ,CAAC;AACH,CAAC"}
|
package/dist/gdpr.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { CollectionDefinition, ContentStore } from '@cogenta/schema';
|
|
2
|
+
/**
|
|
3
|
+
* Task 6 — the export a data-protection request (GDPR/RGPD) is answered
|
|
4
|
+
* with: everything the site holds about one person, gathered by email,
|
|
5
|
+
* addressed to the concerns L15 (commerce) and the still-unbuilt fiches 15
|
|
6
|
+
* (comments) and 16 (forms) name — the account, the content they authored,
|
|
7
|
+
* and, where a caller supplies one, their orders.
|
|
8
|
+
*
|
|
9
|
+
* **What this cannot promise**: comments and form submissions (fiches 15/16)
|
|
10
|
+
* have no store anywhere in this codebase yet — not merged, not being built
|
|
11
|
+
* by a sibling agent this wave — so a request that reaches this function
|
|
12
|
+
* gets an honest `notCollected` reason for each, never a silently empty
|
|
13
|
+
* array indistinguishable from "this person really has none". A future
|
|
14
|
+
* store slots into `PersonalDataSource` and stops being absent from the
|
|
15
|
+
* report — nothing here has to change.
|
|
16
|
+
*/
|
|
17
|
+
export interface PersonalDataAccount {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly email: string;
|
|
20
|
+
readonly roles: readonly string[];
|
|
21
|
+
readonly status: string;
|
|
22
|
+
readonly createdAt: string;
|
|
23
|
+
}
|
|
24
|
+
export interface PersonalDataEntryRef {
|
|
25
|
+
readonly collection: string;
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly status: string;
|
|
28
|
+
readonly createdAt: string;
|
|
29
|
+
readonly title: string | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A domain this codebase does not yet have a store for. Named rather than
|
|
33
|
+
* omitted, so the report says *why* a section is empty.
|
|
34
|
+
*/
|
|
35
|
+
export interface PersonalDataGap {
|
|
36
|
+
readonly source: string;
|
|
37
|
+
readonly reason: string;
|
|
38
|
+
}
|
|
39
|
+
export interface PersonalDataOrderRef {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly status: string;
|
|
42
|
+
readonly createdAt: string;
|
|
43
|
+
readonly totalMinorUnits: number;
|
|
44
|
+
readonly currency: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PersonalDataExport {
|
|
47
|
+
readonly generatedAt: string;
|
|
48
|
+
readonly subjectEmail: string;
|
|
49
|
+
readonly account: PersonalDataAccount | null;
|
|
50
|
+
readonly authoredContent: readonly PersonalDataEntryRef[];
|
|
51
|
+
readonly orders: readonly PersonalDataOrderRef[];
|
|
52
|
+
readonly gaps: readonly PersonalDataGap[];
|
|
53
|
+
}
|
|
54
|
+
export interface PersonalDataAccountLookup {
|
|
55
|
+
findByEmail(email: string): Promise<PersonalDataAccount | null>;
|
|
56
|
+
}
|
|
57
|
+
export interface PersonalDataOrderLookup {
|
|
58
|
+
/** Orders placed under this customer id (resolved from the email by the caller — commerce customers are matched by email at their own layer, not re-matched here). */
|
|
59
|
+
findByCustomerId(customerId: string): Promise<readonly PersonalDataOrderRef[]>;
|
|
60
|
+
findCustomerIdByEmail(email: string): Promise<string | null>;
|
|
61
|
+
}
|
|
62
|
+
export interface ExportPersonalDataOptions {
|
|
63
|
+
readonly email: string;
|
|
64
|
+
readonly accounts: PersonalDataAccountLookup;
|
|
65
|
+
readonly collections: readonly CollectionDefinition[];
|
|
66
|
+
readonly storeFor: (collection: CollectionDefinition) => ContentStore;
|
|
67
|
+
/** Absent when the site sells nothing — `@cogenta/commerce` is never a hard dependency of this package. */
|
|
68
|
+
readonly orders?: PersonalDataOrderLookup;
|
|
69
|
+
readonly now?: () => Date;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gathers everything the site holds about one email address.
|
|
73
|
+
*
|
|
74
|
+
* Authored content is found by `createdBy === account.id` — the account has
|
|
75
|
+
* to exist for this to find anything, which is correct: content is never
|
|
76
|
+
* attributed to an email directly, only to an account id (contract A).
|
|
77
|
+
*/
|
|
78
|
+
export declare function exportPersonalData(options: ExportPersonalDataOptions): Promise<PersonalDataExport>;
|
|
79
|
+
//# sourceMappingURL=gdpr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdpr.d.ts","sourceRoot":"","sources":["../src/gdpr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEzE;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;IAC5C,QAAQ,CAAC,eAAe,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACzD,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAChD,QAAQ,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,CAAA;CAC1C;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;CAChE;AAED,MAAM,WAAW,uBAAuB;IACtC,sKAAsK;IACtK,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAA;IAC9E,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAA;IAC5C,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,QAAQ,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,oBAAoB,KAAK,YAAY,CAAA;IACrE,2GAA2G;IAC3G,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAA;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAC1B;AAcD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,kBAAkB,CAAC,CAoE7B"}
|
package/dist/gdpr.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const TITLE_FIELD_CANDIDATES = ['title', 'name', 'label'];
|
|
2
|
+
function titleOf(collection, values) {
|
|
3
|
+
for (const field of TITLE_FIELD_CANDIDATES) {
|
|
4
|
+
if (field in collection.fields) {
|
|
5
|
+
const value = values[field];
|
|
6
|
+
if (typeof value === 'string' && value.length > 0)
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Gathers everything the site holds about one email address.
|
|
14
|
+
*
|
|
15
|
+
* Authored content is found by `createdBy === account.id` — the account has
|
|
16
|
+
* to exist for this to find anything, which is correct: content is never
|
|
17
|
+
* attributed to an email directly, only to an account id (contract A).
|
|
18
|
+
*/
|
|
19
|
+
export async function exportPersonalData(options) {
|
|
20
|
+
const now = options.now ?? (() => new Date());
|
|
21
|
+
const account = await options.accounts.findByEmail(options.email);
|
|
22
|
+
const authoredContent = [];
|
|
23
|
+
const gaps = [];
|
|
24
|
+
if (account !== null) {
|
|
25
|
+
for (const collection of options.collections) {
|
|
26
|
+
const store = options.storeFor(collection);
|
|
27
|
+
let cursor;
|
|
28
|
+
for (;;) {
|
|
29
|
+
// `createdBy` is a system field, not a declared one, so it cannot go
|
|
30
|
+
// through `list()`'s `where` (which only accepts columns a
|
|
31
|
+
// collection's own fields declare) — filtered here instead. A GDPR
|
|
32
|
+
// export is not a hot path, and every entry is paged through in
|
|
33
|
+
// bounded batches regardless.
|
|
34
|
+
const page = await store.list({
|
|
35
|
+
state: 'working',
|
|
36
|
+
trashed: 'include',
|
|
37
|
+
limit: 200,
|
|
38
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
39
|
+
});
|
|
40
|
+
for (const entry of page.items) {
|
|
41
|
+
if (entry.createdBy !== account.id)
|
|
42
|
+
continue;
|
|
43
|
+
authoredContent.push({
|
|
44
|
+
collection: collection.name,
|
|
45
|
+
id: entry.id,
|
|
46
|
+
status: entry.status,
|
|
47
|
+
createdAt: entry.createdAt,
|
|
48
|
+
title: titleOf(collection, entry.values),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (!page.hasMore || page.nextCursor === null)
|
|
52
|
+
break;
|
|
53
|
+
cursor = page.nextCursor;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
let orders = [];
|
|
58
|
+
if (options.orders !== undefined) {
|
|
59
|
+
const customerId = await options.orders.findCustomerIdByEmail(options.email);
|
|
60
|
+
orders = customerId === null ? [] : await options.orders.findByCustomerId(customerId);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
gaps.push({
|
|
64
|
+
source: 'commerce',
|
|
65
|
+
reason: 'This site has no commerce lookup configured, or sells nothing.',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
gaps.push({
|
|
69
|
+
source: 'comments',
|
|
70
|
+
reason: 'Comments (fiche 15) have no store in this codebase yet.',
|
|
71
|
+
}, {
|
|
72
|
+
source: 'form-submissions',
|
|
73
|
+
reason: 'Form submissions (fiche 16) have no store in this codebase yet.',
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
generatedAt: now().toISOString(),
|
|
77
|
+
subjectEmail: options.email,
|
|
78
|
+
account,
|
|
79
|
+
authoredContent,
|
|
80
|
+
orders,
|
|
81
|
+
gaps,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=gdpr.js.map
|
package/dist/gdpr.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gdpr.js","sourceRoot":"","sources":["../src/gdpr.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cogenta/export` — content export/import, site backup/restore, and the
|
|
3
|
+
* GDPR export of one person's data (fiche 26).
|
|
4
|
+
*
|
|
5
|
+
* Three formats, deliberately not one: `export@1.0` (NDJSON content, this
|
|
6
|
+
* package's own public, versioned format — task 1), a ZIP media archive
|
|
7
|
+
* (task 2), and `cogenta-backup@1.0` (a ZIP of raw table dumps — task 3).
|
|
8
|
+
* They share tooling (the ZIP writer/reader, the checksum, the optional
|
|
9
|
+
* encryption) but are not interchangeable: an export respects permissions
|
|
10
|
+
* and is safe to hand to a partner; a backup is the whole database,
|
|
11
|
+
* including every password hash, and is not.
|
|
12
|
+
*/
|
|
13
|
+
export { BACKUP_FORMAT, BACKUP_FORMAT_VERSION, type BackupManifest, type CreateBackupOptions, type CreateBackupResult, createBackup, dumpTable, } from './backup.js';
|
|
14
|
+
export { type ExportContentOptions, type ExportResult, exportContent, } from './content-export.js';
|
|
15
|
+
export { type ImportContentOptions, type ImportReport, importContent } from './content-import.js';
|
|
16
|
+
export { decryptStream, encryptStream } from './crypto.js';
|
|
17
|
+
export { assertManifest, decodeRecord, EXPORT_FORMAT, EXPORT_FORMAT_VERSION, type ExportEntryRecord, type ExportManifestRecord, type ExportMediaRefRecord, type ExportMenuItemRecord, type ExportMenuRecord, type ExportRecord, type ExportRedirectRecord, type ExportSelection, type ExportTermRecord, type ExportVersionRecord, encodeRecord, } from './format.js';
|
|
18
|
+
export { type ExportPersonalDataOptions, exportPersonalData, type PersonalDataAccount, type PersonalDataAccountLookup, type PersonalDataEntryRef, type PersonalDataExport, type PersonalDataGap, type PersonalDataOrderLookup, type PersonalDataOrderRef, } from './gdpr.js';
|
|
19
|
+
export { exportMediaArchive, exportMediaReferences, type MediaArchiveOptions, type MediaExportOptions, } from './media-export.js';
|
|
20
|
+
export { type ApplyRestoreOptions, applyRestore, previewRestore, type RestorePreviewTable, type RestoreReport, readBackupManifest, type VerifyBackupOptions, verifyBackup, } from './restore.js';
|
|
21
|
+
export { type BuildBackupTablesOptions, buildBackupTables } from './tables.js';
|
|
22
|
+
export { openZip, type ZipEntry, type ZipReader } from './zip-reader.js';
|
|
23
|
+
export { type CreateZipWriterOptions, createZipWriter, type ZipWriter } from './zip-writer.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,YAAY,EACZ,SAAS,GACV,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,aAAa,GACd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACjG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EACL,cAAc,EACd,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,KAAK,yBAAyB,EAC9B,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,KAAK,mBAAmB,EACxB,YAAY,EACZ,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,KAAK,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,KAAK,sBAAsB,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cogenta/export` — content export/import, site backup/restore, and the
|
|
3
|
+
* GDPR export of one person's data (fiche 26).
|
|
4
|
+
*
|
|
5
|
+
* Three formats, deliberately not one: `export@1.0` (NDJSON content, this
|
|
6
|
+
* package's own public, versioned format — task 1), a ZIP media archive
|
|
7
|
+
* (task 2), and `cogenta-backup@1.0` (a ZIP of raw table dumps — task 3).
|
|
8
|
+
* They share tooling (the ZIP writer/reader, the checksum, the optional
|
|
9
|
+
* encryption) but are not interchangeable: an export respects permissions
|
|
10
|
+
* and is safe to hand to a partner; a backup is the whole database,
|
|
11
|
+
* including every password hash, and is not.
|
|
12
|
+
*/
|
|
13
|
+
export { BACKUP_FORMAT, BACKUP_FORMAT_VERSION, createBackup, dumpTable, } from './backup.js';
|
|
14
|
+
export { exportContent, } from './content-export.js';
|
|
15
|
+
export { importContent } from './content-import.js';
|
|
16
|
+
export { decryptStream, encryptStream } from './crypto.js';
|
|
17
|
+
export { assertManifest, decodeRecord, EXPORT_FORMAT, EXPORT_FORMAT_VERSION, encodeRecord, } from './format.js';
|
|
18
|
+
export { exportPersonalData, } from './gdpr.js';
|
|
19
|
+
export { exportMediaArchive, exportMediaReferences, } from './media-export.js';
|
|
20
|
+
export { applyRestore, previewRestore, readBackupManifest, verifyBackup, } from './restore.js';
|
|
21
|
+
export { buildBackupTables } from './tables.js';
|
|
22
|
+
export { openZip } from './zip-reader.js';
|
|
23
|
+
export { createZipWriter } from './zip-writer.js';
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EAIrB,YAAY,EACZ,SAAS,GACV,MAAM,aAAa,CAAA;AACpB,OAAO,EAGL,aAAa,GACd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAgD,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACjG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EACL,cAAc,EACd,YAAY,EACZ,aAAa,EACb,qBAAqB,EAWrB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAEL,kBAAkB,GAQnB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,GAGtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAEL,YAAY,EACZ,cAAc,EAGd,kBAAkB,EAElB,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EAAiC,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAiC,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAA+B,eAAe,EAAkB,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { MediaAsset, MediaStore } from '@cogenta/core';
|
|
2
|
+
import { type StorageDriver } from '@cogenta/core';
|
|
3
|
+
export interface MediaExportOptions {
|
|
4
|
+
readonly media: MediaStore;
|
|
5
|
+
readonly ids: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Task 2, "references seules" mode: one NDJSON line per medium, naming its
|
|
9
|
+
* storage key rather than its bytes. Cheap, and correct whenever the target
|
|
10
|
+
* site shares (or restores) the same storage — a media archive (below) is
|
|
11
|
+
* for when it does not.
|
|
12
|
+
*/
|
|
13
|
+
export declare function exportMediaReferences(options: MediaExportOptions): AsyncGenerator<string>;
|
|
14
|
+
export interface MediaArchiveOptions {
|
|
15
|
+
readonly media: MediaStore;
|
|
16
|
+
readonly storage: StorageDriver;
|
|
17
|
+
readonly ids: readonly string[];
|
|
18
|
+
/** Receives each chunk of the ZIP as it is produced. Never buffers the whole archive (task 2's own rule). */
|
|
19
|
+
readonly write: (chunk: Buffer) => Promise<void> | void;
|
|
20
|
+
readonly onAsset?: (asset: MediaAsset) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Task 2, "archive complète" mode: every referenced medium's real bytes,
|
|
24
|
+
* streamed straight from `storage.get()` into the ZIP entry — the file is
|
|
25
|
+
* never read into a `Buffer` first, so a multi-gigabyte archive costs the
|
|
26
|
+
* process no more memory than a small one.
|
|
27
|
+
*
|
|
28
|
+
* A manifest of what went in (`manifest.json`, one JSON array) is written as
|
|
29
|
+
* the archive's own first entry, so a person who only has the `.zip` — no
|
|
30
|
+
* accompanying export stream — can still tell what each file was for.
|
|
31
|
+
*/
|
|
32
|
+
export declare function exportMediaArchive(options: MediaArchiveOptions): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=media-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-export.d.ts","sourceRoot":"","sources":["../src/media-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAIhE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC;AAED;;;;;GAKG;AACH,wBAAuB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,GAAG,cAAc,CAAC,MAAM,CAAC,CAchG;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,6GAA6G;IAC7G,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CAC/C;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BpF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
import { encodeRecord } from './format.js';
|
|
3
|
+
import { createZipWriter } from './zip-writer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Task 2, "references seules" mode: one NDJSON line per medium, naming its
|
|
6
|
+
* storage key rather than its bytes. Cheap, and correct whenever the target
|
|
7
|
+
* site shares (or restores) the same storage — a media archive (below) is
|
|
8
|
+
* for when it does not.
|
|
9
|
+
*/
|
|
10
|
+
export async function* exportMediaReferences(options) {
|
|
11
|
+
for (const id of options.ids) {
|
|
12
|
+
const asset = await options.media.get(id);
|
|
13
|
+
if (asset === null)
|
|
14
|
+
continue;
|
|
15
|
+
const record = {
|
|
16
|
+
kind: 'media-ref',
|
|
17
|
+
id: asset.id,
|
|
18
|
+
filename: asset.filename,
|
|
19
|
+
mimeType: asset.mimeType,
|
|
20
|
+
size: asset.size,
|
|
21
|
+
storageKey: asset.storageKey,
|
|
22
|
+
};
|
|
23
|
+
yield encodeRecord(record);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Task 2, "archive complète" mode: every referenced medium's real bytes,
|
|
28
|
+
* streamed straight from `storage.get()` into the ZIP entry — the file is
|
|
29
|
+
* never read into a `Buffer` first, so a multi-gigabyte archive costs the
|
|
30
|
+
* process no more memory than a small one.
|
|
31
|
+
*
|
|
32
|
+
* A manifest of what went in (`manifest.json`, one JSON array) is written as
|
|
33
|
+
* the archive's own first entry, so a person who only has the `.zip` — no
|
|
34
|
+
* accompanying export stream — can still tell what each file was for.
|
|
35
|
+
*/
|
|
36
|
+
export async function exportMediaArchive(options) {
|
|
37
|
+
const zip = createZipWriter({ write: options.write });
|
|
38
|
+
const manifest = [];
|
|
39
|
+
for (const id of options.ids) {
|
|
40
|
+
const asset = await options.media.get(id);
|
|
41
|
+
if (asset === null) {
|
|
42
|
+
throw new CogentaError({
|
|
43
|
+
code: 'EXPORT_MEDIA_NOT_FOUND',
|
|
44
|
+
message: `Media asset "${id}" referenced by the export no longer exists.`,
|
|
45
|
+
hint: 'Re-run the content export, or export media references only.',
|
|
46
|
+
details: { id },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
options.onAsset?.(asset);
|
|
50
|
+
manifest.push({
|
|
51
|
+
kind: 'media-ref',
|
|
52
|
+
id: asset.id,
|
|
53
|
+
filename: asset.filename,
|
|
54
|
+
mimeType: asset.mimeType,
|
|
55
|
+
size: asset.size,
|
|
56
|
+
storageKey: asset.storageKey,
|
|
57
|
+
});
|
|
58
|
+
const body = await options.storage.get(asset.storageKey);
|
|
59
|
+
await zip.addFile(`media/${asset.id}/${asset.filename}`, body);
|
|
60
|
+
}
|
|
61
|
+
await zip.addFile('manifest.json', Buffer.from(JSON.stringify(manifest, null, 2), 'utf8'));
|
|
62
|
+
await zip.finish();
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=media-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-export.js","sourceRoot":"","sources":["../src/media-export.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAsB,MAAM,eAAe,CAAA;AAChE,OAAO,EAA6B,YAAY,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAOjD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,qBAAqB,CAAC,OAA2B;IACtE,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzC,IAAI,KAAK,KAAK,IAAI;YAAE,SAAQ;QAC5B,MAAM,MAAM,GAAyB;YACnC,IAAI,EAAE,WAAW;YACjB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAA;QACD,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC;AAWD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAA4B;IACnE,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;IACrD,MAAM,QAAQ,GAA2B,EAAE,CAAA;IAE3C,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,YAAY,CAAC;gBACrB,IAAI,EAAE,wBAAwB;gBAC9B,OAAO,EAAE,gBAAgB,EAAE,8CAA8C;gBACzE,IAAI,EAAE,6DAA6D;gBACnE,OAAO,EAAE,EAAE,EAAE,EAAE;aAChB,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;QACxB,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,WAAW;YACjB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxD,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,CAAA;IAChE,CAAC;IAED,MAAM,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IAC1F,MAAM,GAAG,CAAC,MAAM,EAAE,CAAA;AACpB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
import type { BackupManifest } from './backup.js';
|
|
3
|
+
export declare function readBackupManifest(path: string): Promise<BackupManifest>;
|
|
4
|
+
export interface VerifyBackupOptions {
|
|
5
|
+
readonly passphrase?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Recomputes the manifest's checksum over the backup's own bytes and throws
|
|
9
|
+
* if it does not match — task 3's "somme de contrôle vérifiée avant
|
|
10
|
+
* restauration", called by `applyRestore` before it writes a single row, and
|
|
11
|
+
* exposed on its own for a caller (the admin's restore screen, `cogenta
|
|
12
|
+
* restore --dry-run`) that wants to check a file without touching a database.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifyBackup(path: string, options?: VerifyBackupOptions): Promise<BackupManifest>;
|
|
15
|
+
export interface RestorePreviewTable {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly rowsInBackup: number;
|
|
18
|
+
readonly rowsExisting: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* "Ce qui sera écrasé, ce qui sera ajouté" (task 4) — a read-only pass that
|
|
22
|
+
* verifies the checksum and, for every table the target database already has
|
|
23
|
+
* created, counts what is already there. A table this database has never
|
|
24
|
+
* created (a fresh site, mid-migration) reports `rowsExisting: 0` rather than
|
|
25
|
+
* failing, since that is exactly the case an empty-site restore expects.
|
|
26
|
+
*/
|
|
27
|
+
export declare function previewRestore(path: string, db: DatabaseHandle, options?: VerifyBackupOptions): Promise<{
|
|
28
|
+
readonly manifest: BackupManifest;
|
|
29
|
+
readonly tables: readonly RestorePreviewTable[];
|
|
30
|
+
}>;
|
|
31
|
+
export interface ApplyRestoreOptions extends VerifyBackupOptions {
|
|
32
|
+
readonly db: DatabaseHandle;
|
|
33
|
+
}
|
|
34
|
+
export interface RestoreReport {
|
|
35
|
+
readonly tables: readonly {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly rows: number;
|
|
38
|
+
}[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Restores every table of a backup, in the manifest's own order — the same
|
|
42
|
+
* dependency order `buildBackupTables` produced it in, so a foreign key never
|
|
43
|
+
* meets a row it references before that row exists.
|
|
44
|
+
*
|
|
45
|
+
* **Full restore only, by design (task 4).** This function does not decide
|
|
46
|
+
* who may call it; `packages/cli`'s `cogenta restore` is the only caller
|
|
47
|
+
* this codebase gives a full restore to — the admin API restores a *content*
|
|
48
|
+
* export instead (`importContent`), never a whole-database backup, because
|
|
49
|
+
* that would let a browser session overwrite the database it is itself
|
|
50
|
+
* running against.
|
|
51
|
+
*/
|
|
52
|
+
export declare function applyRestore(path: string, options: ApplyRestoreOptions): Promise<RestoreReport>;
|
|
53
|
+
//# sourceMappingURL=restore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../src/restore.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAUjD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ9E;AAoBD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,cAAc,CAAC,CAuBzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,cAAc,EAClB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC;IAAE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAA;CAAE,CAAC,CAmBjG;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;IAC9D,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC7E;AAeD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAgDxB"}
|