@cogenta/import 0.1.4 → 0.2.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/dist/csv-import.d.ts +11 -0
- package/dist/csv-import.d.ts.map +1 -0
- package/dist/csv-import.js +14 -0
- package/dist/csv-import.js.map +1 -0
- package/dist/csv.d.ts +15 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/csv.js +100 -0
- package/dist/csv.js.map +1 -0
- package/dist/feed.d.ts +4 -0
- package/dist/feed.d.ts.map +1 -0
- package/dist/feed.js +50 -0
- package/dist/feed.js.map +1 -0
- package/dist/generic-import.d.ts +56 -0
- package/dist/generic-import.d.ts.map +1 -0
- package/dist/generic-import.js +64 -0
- package/dist/generic-import.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/json-import.d.ts +52 -0
- package/dist/json-import.d.ts.map +1 -0
- package/dist/json-import.js +110 -0
- package/dist/json-import.js.map +1 -0
- package/dist/mapping.d.ts +23 -0
- package/dist/mapping.d.ts.map +1 -0
- package/dist/mapping.js +41 -0
- package/dist/mapping.js.map +1 -0
- package/dist/ssrf.d.ts +12 -0
- package/dist/ssrf.d.ts.map +1 -0
- package/dist/ssrf.js +106 -0
- package/dist/ssrf.js.map +1 -0
- package/dist/tracking.d.ts +82 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +200 -0
- package/dist/tracking.js.map +1 -0
- package/dist/undo.d.ts +27 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/undo.js +47 -0
- package/dist/undo.js.map +1 -0
- package/dist/wordpress/analyze.d.ts +40 -0
- package/dist/wordpress/analyze.d.ts.map +1 -0
- package/dist/wordpress/analyze.js +111 -0
- package/dist/wordpress/analyze.js.map +1 -0
- package/dist/wordpress/import.d.ts +31 -0
- package/dist/wordpress/import.d.ts.map +1 -1
- package/dist/wordpress/import.js +199 -6
- package/dist/wordpress/import.js.map +1 -1
- package/dist/wordpress/media.d.ts +3 -0
- package/dist/wordpress/media.d.ts.map +1 -1
- package/dist/wordpress/media.js +39 -2
- package/dist/wordpress/media.js.map +1 -1
- package/package.json +6 -5
package/dist/mapping.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
/** Proposes target field names by matching source header names case-insensitively (task 2's "correspondances proposées automatiquement"). */
|
|
3
|
+
export function proposeFieldMapping(sourceFields, collection) {
|
|
4
|
+
const targetNames = new Map(Object.keys(collection.fields).map((name) => [name.toLowerCase(), name]));
|
|
5
|
+
const fields = {};
|
|
6
|
+
for (const source of sourceFields) {
|
|
7
|
+
fields[source] = targetNames.get(source.toLowerCase()) ?? null;
|
|
8
|
+
}
|
|
9
|
+
return { targetCollection: collection.name, fields };
|
|
10
|
+
}
|
|
11
|
+
export function resolveMapping(mapping, collections) {
|
|
12
|
+
const collection = collections.find((c) => c.name === mapping.targetCollection);
|
|
13
|
+
if (collection === undefined) {
|
|
14
|
+
throw new CogentaError({
|
|
15
|
+
code: 'IMPORT_MAPPING_INVALID',
|
|
16
|
+
message: `Collection "${mapping.targetCollection}" does not exist on this site.`,
|
|
17
|
+
hint: 'Pick a collection this site declares, or use the field-mapping screen to change the target.',
|
|
18
|
+
details: { collection: mapping.targetCollection },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const targetFieldNames = new Set(Object.keys(collection.fields));
|
|
22
|
+
const fields = new Map();
|
|
23
|
+
const ignored = [];
|
|
24
|
+
for (const [source, target] of Object.entries(mapping.fields)) {
|
|
25
|
+
if (target === null) {
|
|
26
|
+
ignored.push(source);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!targetFieldNames.has(target)) {
|
|
30
|
+
throw new CogentaError({
|
|
31
|
+
code: 'IMPORT_MAPPING_INVALID',
|
|
32
|
+
message: `Collection "${collection.name}" has no field "${target}".`,
|
|
33
|
+
hint: 'Point this source column at an existing field, or map it to "ignore".',
|
|
34
|
+
details: { collection: collection.name, field: target },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
fields.set(source, target);
|
|
38
|
+
}
|
|
39
|
+
return { collection, fields, ignored };
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAuB5C,6IAA6I;AAC7I,MAAM,UAAU,mBAAmB,CACjC,YAA+B,EAC/B,UAAgC;IAEhC,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAU,CAAC,CAClF,CAAA;IACD,MAAM,MAAM,GAAkC,EAAE,CAAA;IAChD,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAA;IAChE,CAAC;IACD,OAAO,EAAE,gBAAgB,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,OAAqB,EACrB,WAA4C;IAE5C,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC/E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,wBAAwB;YAC9B,OAAO,EAAE,eAAe,OAAO,CAAC,gBAAgB,gCAAgC;YAChF,IAAI,EAAE,6FAA6F;YACnG,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,gBAAgB,EAAE;SAClD,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YACpB,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,YAAY,CAAC;gBACrB,IAAI,EAAE,wBAAwB;gBAC9B,OAAO,EAAE,eAAe,UAAU,CAAC,IAAI,mBAAmB,MAAM,IAAI;gBACpE,IAAI,EAAE,uEAAuE;gBAC7E,OAAO,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;aACxD,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AACxC,CAAC"}
|
package/dist/ssrf.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { lookup } from 'node:dns/promises';
|
|
2
|
+
export declare function isPrivateAddress(ip: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the URL's host and throws if it (or any of its resolved
|
|
5
|
+
* addresses) is a loopback, link-local or otherwise private address —
|
|
6
|
+
* checked on the resolved IP, not the hostname, so `http://a.b.c` that
|
|
7
|
+
* resolves to `127.0.0.1` (DNS rebinding) is refused just the same.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertPublicUrl(url: string, options?: {
|
|
10
|
+
readonly lookupImpl?: typeof lookup;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=ssrf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf.d.ts","sourceRoot":"","sources":["../src/ssrf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAqD1C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAQpD;AAWD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CAmCf"}
|
package/dist/ssrf.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { lookup } from 'node:dns/promises';
|
|
2
|
+
import { isIP } from 'node:net';
|
|
3
|
+
import { CogentaError } from '@cogenta/core';
|
|
4
|
+
/**
|
|
5
|
+
* Guards against a remote-media download becoming a request an attacker
|
|
6
|
+
* chose the destination of (fiche 25, pièges connus: "les médias distants
|
|
7
|
+
* sont des requêtes sortantes vers des URL fournies par un fichier").
|
|
8
|
+
*
|
|
9
|
+
* A WXR, an RSS feed or a CSV cell is untrusted input (rule R8) that this
|
|
10
|
+
* import turns into an outbound `fetch`. Without this check, an attacker who
|
|
11
|
+
* can get their export imported can make the server fetch
|
|
12
|
+
* `http://169.254.169.254/latest/meta-data/` (cloud instance metadata) or
|
|
13
|
+
* `http://localhost:5432` (a database port) and, through the image that gets
|
|
14
|
+
* stored, read the response back.
|
|
15
|
+
*/
|
|
16
|
+
const PRIVATE_IPV4_RANGES = [
|
|
17
|
+
[ipToInt('0.0.0.0'), ipToInt('0.255.255.255')],
|
|
18
|
+
[ipToInt('10.0.0.0'), ipToInt('10.255.255.255')],
|
|
19
|
+
[ipToInt('100.64.0.0'), ipToInt('100.127.255.255')], // CGNAT
|
|
20
|
+
[ipToInt('127.0.0.0'), ipToInt('127.255.255.255')], // loopback
|
|
21
|
+
[ipToInt('169.254.0.0'), ipToInt('169.254.255.255')], // link-local, cloud metadata
|
|
22
|
+
[ipToInt('172.16.0.0'), ipToInt('172.31.255.255')],
|
|
23
|
+
[ipToInt('192.0.0.0'), ipToInt('192.0.0.255')],
|
|
24
|
+
[ipToInt('192.168.0.0'), ipToInt('192.168.255.255')],
|
|
25
|
+
[ipToInt('198.18.0.0'), ipToInt('198.19.255.255')], // benchmarking
|
|
26
|
+
[ipToInt('224.0.0.0'), ipToInt('255.255.255.255')], // multicast/reserved
|
|
27
|
+
];
|
|
28
|
+
function ipToInt(ip) {
|
|
29
|
+
const parts = ip.split('.').map(Number);
|
|
30
|
+
return (((parts[0] ?? 0) << 24) + ((parts[1] ?? 0) << 16) + ((parts[2] ?? 0) << 8) + (parts[3] ?? 0));
|
|
31
|
+
}
|
|
32
|
+
function isPrivateIpv4(ip) {
|
|
33
|
+
const value = ipToInt(ip) >>> 0;
|
|
34
|
+
return PRIVATE_IPV4_RANGES.some(([start, end]) => value >= start >>> 0 && value <= end >>> 0);
|
|
35
|
+
}
|
|
36
|
+
function isPrivateIpv6(ip) {
|
|
37
|
+
const lower = ip.toLowerCase();
|
|
38
|
+
return (lower === '::1' || // loopback
|
|
39
|
+
lower.startsWith('fe80:') || // link-local
|
|
40
|
+
lower.startsWith('fc') || // unique local fc00::/7
|
|
41
|
+
lower.startsWith('fd') ||
|
|
42
|
+
lower.startsWith('::ffff:') // IPv4-mapped — check the embedded address too
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
export function isPrivateAddress(ip) {
|
|
46
|
+
if (isIP(ip) === 4)
|
|
47
|
+
return isPrivateIpv4(ip);
|
|
48
|
+
if (isIP(ip) === 6) {
|
|
49
|
+
if (isPrivateIpv6(ip))
|
|
50
|
+
return true;
|
|
51
|
+
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i.exec(ip);
|
|
52
|
+
return mapped?.[1] !== undefined && isPrivateIpv4(mapped[1]);
|
|
53
|
+
}
|
|
54
|
+
return true; // not a recognisable literal — refuse rather than guess
|
|
55
|
+
}
|
|
56
|
+
function unsafeUrl(url, reason) {
|
|
57
|
+
return new CogentaError({
|
|
58
|
+
code: 'IMPORT_MEDIA_URL_UNSAFE',
|
|
59
|
+
message: `"${url}" cannot be fetched: ${reason}.`,
|
|
60
|
+
hint: 'Only public http(s) URLs are downloaded during import. Host the file publicly, or upload it directly.',
|
|
61
|
+
details: { url },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the URL's host and throws if it (or any of its resolved
|
|
66
|
+
* addresses) is a loopback, link-local or otherwise private address —
|
|
67
|
+
* checked on the resolved IP, not the hostname, so `http://a.b.c` that
|
|
68
|
+
* resolves to `127.0.0.1` (DNS rebinding) is refused just the same.
|
|
69
|
+
*/
|
|
70
|
+
export async function assertPublicUrl(url, options = {}) {
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = new URL(url);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
throw unsafeUrl(url, 'not a valid URL');
|
|
77
|
+
}
|
|
78
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
79
|
+
throw unsafeUrl(url, `protocol "${parsed.protocol}" is not allowed`);
|
|
80
|
+
}
|
|
81
|
+
const hostname = parsed.hostname;
|
|
82
|
+
if (hostname === 'localhost' || hostname.endsWith('.localhost')) {
|
|
83
|
+
throw unsafeUrl(url, 'localhost is not a public address');
|
|
84
|
+
}
|
|
85
|
+
if (isIP(hostname) !== 0) {
|
|
86
|
+
if (isPrivateAddress(hostname))
|
|
87
|
+
throw unsafeUrl(url, 'resolves to a private address');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const doLookup = options.lookupImpl ?? lookup;
|
|
91
|
+
let addresses;
|
|
92
|
+
try {
|
|
93
|
+
const result = await doLookup(hostname, { all: true });
|
|
94
|
+
addresses = Array.isArray(result) ? result : [result];
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw unsafeUrl(url, 'the host name could not be resolved');
|
|
98
|
+
}
|
|
99
|
+
if (addresses.length === 0)
|
|
100
|
+
throw unsafeUrl(url, 'the host name resolved to nothing');
|
|
101
|
+
for (const { address } of addresses) {
|
|
102
|
+
if (isPrivateAddress(address))
|
|
103
|
+
throw unsafeUrl(url, 'resolves to a private address');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=ssrf.js.map
|
package/dist/ssrf.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../src/ssrf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* The import bookkeeping tables (fiche 25, tasks 1, 3 and 4).
|
|
4
|
+
*
|
|
5
|
+
* Two tables, both owned by `@cogenta/import` alone — never a field on
|
|
6
|
+
* contract A. `provenance`/`provenanceDetail` were the plan's first idea
|
|
7
|
+
* (task 4's "décision à prendre"), but `provenanceDetail` is a fixed,
|
|
8
|
+
* `strictObject` shape (`{ agent, model, at, prompt? }`, contract A) with no
|
|
9
|
+
* room for an import identifier, and stuffing one into `agent` would misuse a
|
|
10
|
+
* field that means "which AI agent wrote this" for content that is not
|
|
11
|
+
* AI-generated at all. A side table costs nothing on the figé contract and is
|
|
12
|
+
* exactly the shape `createRedirectStore` already uses for the same reason —
|
|
13
|
+
* bookkeeping that is about *how* content arrived, not what it is.
|
|
14
|
+
*
|
|
15
|
+
* `cogenta_import_runs` is one row per analyze/apply cycle: its `status`
|
|
16
|
+
* tracks the two-phase flow the lot asks for ('analyzed' → nothing written
|
|
17
|
+
* yet; 'running'/'done'/'failed' → apply is or was in progress; 'cancelled'
|
|
18
|
+
* → undone). `cogenta_import_items` is one row per entry actually created,
|
|
19
|
+
* which is what makes both resume (task 3: skip a `sourceId` already
|
|
20
|
+
* recorded) and undo (task 4: trash everything recorded for a run) possible
|
|
21
|
+
* without touching the entries themselves.
|
|
22
|
+
*/
|
|
23
|
+
export declare const IMPORT_RUNS_TABLE = "cogenta_import_runs";
|
|
24
|
+
export declare const IMPORT_ITEMS_TABLE = "cogenta_import_items";
|
|
25
|
+
export declare const IMPORT_SOURCES: readonly ['wordpress', 'csv', 'json', 'rss'];
|
|
26
|
+
export type ImportSource = (typeof IMPORT_SOURCES)[number];
|
|
27
|
+
export declare const IMPORT_RUN_STATUSES: readonly ['analyzed', 'queued', 'running', 'done', 'failed', 'cancelled'];
|
|
28
|
+
export type ImportRunStatus = (typeof IMPORT_RUN_STATUSES)[number];
|
|
29
|
+
export interface ImportRun {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly source: ImportSource;
|
|
32
|
+
readonly status: ImportRunStatus;
|
|
33
|
+
readonly createdBy: string | null;
|
|
34
|
+
readonly createdAt: string;
|
|
35
|
+
readonly updatedAt: string;
|
|
36
|
+
/** The analyzed preview report — what `analyze*` produced, unchanged by apply. */
|
|
37
|
+
readonly analysis: unknown;
|
|
38
|
+
/** Caller-supplied field/collection mapping (task 2), stored so a resumed apply reuses the same one. */
|
|
39
|
+
readonly mapping: unknown;
|
|
40
|
+
/** Progress counters, updated as apply runs — `{ processed, total }`. */
|
|
41
|
+
readonly progress: {
|
|
42
|
+
readonly processed: number;
|
|
43
|
+
readonly total: number;
|
|
44
|
+
};
|
|
45
|
+
/** The final report, present once `status` is `'done'` or `'failed'`. */
|
|
46
|
+
readonly report: unknown;
|
|
47
|
+
readonly error: string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface CreateImportRunInput {
|
|
50
|
+
readonly source: ImportSource;
|
|
51
|
+
readonly createdBy: string | null;
|
|
52
|
+
readonly analysis: unknown;
|
|
53
|
+
readonly mapping?: unknown;
|
|
54
|
+
readonly total?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ImportItem {
|
|
57
|
+
readonly runId: string;
|
|
58
|
+
readonly sourceId: string;
|
|
59
|
+
readonly collection: string;
|
|
60
|
+
readonly entryId: string;
|
|
61
|
+
readonly createdAt: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ImportTrackingStore {
|
|
64
|
+
ensureTables(): Promise<void>;
|
|
65
|
+
createRun(input: CreateImportRunInput): Promise<ImportRun>;
|
|
66
|
+
getRun(id: string): Promise<ImportRun | null>;
|
|
67
|
+
listRuns(options?: {
|
|
68
|
+
readonly limit?: number;
|
|
69
|
+
}): Promise<ImportRun[]>;
|
|
70
|
+
updateRun(id: string, patch: Partial<Pick<ImportRun, 'status' | 'progress' | 'report' | 'error' | 'mapping'>>): Promise<ImportRun>;
|
|
71
|
+
/** Records one written entry. Idempotent: re-recording the same `(runId, sourceId)` is a no-op. */
|
|
72
|
+
recordItem(item: Omit<ImportItem, 'createdAt'>): Promise<void>;
|
|
73
|
+
/** The `sourceId`s already recorded for a run — what a resumed apply must skip. */
|
|
74
|
+
doneSourceIds(runId: string): Promise<Set<string>>;
|
|
75
|
+
listItems(runId: string): Promise<ImportItem[]>;
|
|
76
|
+
}
|
|
77
|
+
export interface CreateImportTrackingStoreOptions {
|
|
78
|
+
readonly db: DatabaseHandle;
|
|
79
|
+
readonly now?: () => number;
|
|
80
|
+
}
|
|
81
|
+
export declare function createImportTrackingStore(options: CreateImportTrackingStoreOptions): ImportTrackingStore;
|
|
82
|
+
//# sourceMappingURL=tracking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"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"}
|
package/dist/tracking.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { CogentaError, identifier, limit, newId, sql } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* The import bookkeeping tables (fiche 25, tasks 1, 3 and 4).
|
|
4
|
+
*
|
|
5
|
+
* Two tables, both owned by `@cogenta/import` alone — never a field on
|
|
6
|
+
* contract A. `provenance`/`provenanceDetail` were the plan's first idea
|
|
7
|
+
* (task 4's "décision à prendre"), but `provenanceDetail` is a fixed,
|
|
8
|
+
* `strictObject` shape (`{ agent, model, at, prompt? }`, contract A) with no
|
|
9
|
+
* room for an import identifier, and stuffing one into `agent` would misuse a
|
|
10
|
+
* field that means "which AI agent wrote this" for content that is not
|
|
11
|
+
* AI-generated at all. A side table costs nothing on the figé contract and is
|
|
12
|
+
* exactly the shape `createRedirectStore` already uses for the same reason —
|
|
13
|
+
* bookkeeping that is about *how* content arrived, not what it is.
|
|
14
|
+
*
|
|
15
|
+
* `cogenta_import_runs` is one row per analyze/apply cycle: its `status`
|
|
16
|
+
* tracks the two-phase flow the lot asks for ('analyzed' → nothing written
|
|
17
|
+
* yet; 'running'/'done'/'failed' → apply is or was in progress; 'cancelled'
|
|
18
|
+
* → undone). `cogenta_import_items` is one row per entry actually created,
|
|
19
|
+
* which is what makes both resume (task 3: skip a `sourceId` already
|
|
20
|
+
* recorded) and undo (task 4: trash everything recorded for a run) possible
|
|
21
|
+
* without touching the entries themselves.
|
|
22
|
+
*/
|
|
23
|
+
export const IMPORT_RUNS_TABLE = 'cogenta_import_runs';
|
|
24
|
+
export const IMPORT_ITEMS_TABLE = 'cogenta_import_items';
|
|
25
|
+
export const IMPORT_SOURCES = ['wordpress', 'csv', 'json', 'rss'];
|
|
26
|
+
export const IMPORT_RUN_STATUSES = [
|
|
27
|
+
'analyzed',
|
|
28
|
+
'queued',
|
|
29
|
+
'running',
|
|
30
|
+
'done',
|
|
31
|
+
'failed',
|
|
32
|
+
'cancelled',
|
|
33
|
+
];
|
|
34
|
+
function parseJson(raw) {
|
|
35
|
+
if (raw === null)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function toRun(row) {
|
|
45
|
+
return {
|
|
46
|
+
id: row.id,
|
|
47
|
+
source: row.source,
|
|
48
|
+
status: row.status,
|
|
49
|
+
createdBy: row.created_by,
|
|
50
|
+
createdAt: row.created_at,
|
|
51
|
+
updatedAt: row.updated_at,
|
|
52
|
+
analysis: parseJson(row.analysis),
|
|
53
|
+
mapping: parseJson(row.mapping),
|
|
54
|
+
progress: { processed: Number(row.processed), total: Number(row.total) },
|
|
55
|
+
report: parseJson(row.report),
|
|
56
|
+
error: row.error,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function notFound(id) {
|
|
60
|
+
return new CogentaError({
|
|
61
|
+
code: 'IMPORT_RUN_NOT_FOUND',
|
|
62
|
+
message: `No import run "${id}" exists.`,
|
|
63
|
+
hint: 'Analyze a source first — apply, status and cancel all operate on the run id that returns.',
|
|
64
|
+
details: { id },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function createImportTrackingStore(options) {
|
|
68
|
+
const { db } = options;
|
|
69
|
+
const now = options.now ?? Date.now;
|
|
70
|
+
const runsTable = identifier(IMPORT_RUNS_TABLE, db.dialect);
|
|
71
|
+
const itemsTable = identifier(IMPORT_ITEMS_TABLE, db.dialect);
|
|
72
|
+
let ready = false;
|
|
73
|
+
async function ensureTables() {
|
|
74
|
+
if (ready)
|
|
75
|
+
return;
|
|
76
|
+
await db.query(sql `
|
|
77
|
+
create table if not exists ${runsTable} (
|
|
78
|
+
id varchar(64) not null primary key,
|
|
79
|
+
source varchar(32) not null,
|
|
80
|
+
status varchar(16) not null,
|
|
81
|
+
created_by varchar(64),
|
|
82
|
+
created_at varchar(40) not null,
|
|
83
|
+
updated_at varchar(40) not null,
|
|
84
|
+
analysis text,
|
|
85
|
+
mapping text,
|
|
86
|
+
processed integer not null default 0,
|
|
87
|
+
total integer not null default 0,
|
|
88
|
+
report text,
|
|
89
|
+
error text
|
|
90
|
+
)`);
|
|
91
|
+
await db.query(sql `
|
|
92
|
+
create table if not exists ${itemsTable} (
|
|
93
|
+
run_id varchar(64) not null,
|
|
94
|
+
source_id varchar(255) not null,
|
|
95
|
+
collection varchar(255) not null,
|
|
96
|
+
entry_id varchar(64) not null,
|
|
97
|
+
created_at varchar(40) not null
|
|
98
|
+
)`);
|
|
99
|
+
await db
|
|
100
|
+
.query(sql `create unique index ${identifier(`${IMPORT_ITEMS_TABLE}_run_source`, db.dialect)}
|
|
101
|
+
on ${itemsTable} (run_id, source_id)`)
|
|
102
|
+
.catch(() => undefined); // already there — no dialect spells "if not exists" the same way
|
|
103
|
+
ready = true;
|
|
104
|
+
}
|
|
105
|
+
async function createRun(input) {
|
|
106
|
+
await ensureTables();
|
|
107
|
+
const id = newId();
|
|
108
|
+
const timestamp = new Date(now()).toISOString();
|
|
109
|
+
await db.query(sql `
|
|
110
|
+
insert into ${runsTable}
|
|
111
|
+
(id, source, status, created_by, created_at, updated_at, analysis, mapping, processed, total, report, error)
|
|
112
|
+
values (
|
|
113
|
+
${id}, ${input.source}, ${'analyzed'}, ${input.createdBy}, ${timestamp}, ${timestamp},
|
|
114
|
+
${JSON.stringify(input.analysis)},
|
|
115
|
+
${input.mapping === undefined ? null : JSON.stringify(input.mapping)},
|
|
116
|
+
${0}, ${input.total ?? 0}, ${null}, ${null}
|
|
117
|
+
)`);
|
|
118
|
+
const created = await getRun(id);
|
|
119
|
+
if (created === null)
|
|
120
|
+
throw notFound(id);
|
|
121
|
+
return created;
|
|
122
|
+
}
|
|
123
|
+
async function getRun(id) {
|
|
124
|
+
await ensureTables();
|
|
125
|
+
const found = await db.query(sql `
|
|
126
|
+
select * from ${runsTable} where id = ${id} limit ${limit(1)}`);
|
|
127
|
+
const row = found.rows[0];
|
|
128
|
+
return row === undefined ? null : toRun(row);
|
|
129
|
+
}
|
|
130
|
+
async function listRuns(listOptions = {}) {
|
|
131
|
+
await ensureTables();
|
|
132
|
+
const found = await db.query(sql `
|
|
133
|
+
select * from ${runsTable} order by created_at desc limit ${limit(listOptions.limit ?? 50)}`);
|
|
134
|
+
return found.rows.map(toRun);
|
|
135
|
+
}
|
|
136
|
+
async function updateRun(id, patch) {
|
|
137
|
+
await ensureTables();
|
|
138
|
+
const current = await getRun(id);
|
|
139
|
+
if (current === null)
|
|
140
|
+
throw notFound(id);
|
|
141
|
+
const next = {
|
|
142
|
+
status: patch.status ?? current.status,
|
|
143
|
+
processed: patch.progress?.processed ?? current.progress.processed,
|
|
144
|
+
total: patch.progress?.total ?? current.progress.total,
|
|
145
|
+
report: patch.report === undefined ? current.report : patch.report,
|
|
146
|
+
error: patch.error === undefined ? current.error : patch.error,
|
|
147
|
+
mapping: patch.mapping === undefined ? current.mapping : patch.mapping,
|
|
148
|
+
};
|
|
149
|
+
await db.query(sql `
|
|
150
|
+
update ${runsTable} set
|
|
151
|
+
status = ${next.status},
|
|
152
|
+
updated_at = ${new Date(now()).toISOString()},
|
|
153
|
+
processed = ${next.processed},
|
|
154
|
+
total = ${next.total},
|
|
155
|
+
report = ${next.report === null ? null : JSON.stringify(next.report)},
|
|
156
|
+
error = ${next.error},
|
|
157
|
+
mapping = ${next.mapping === null ? null : JSON.stringify(next.mapping)}
|
|
158
|
+
where id = ${id}`);
|
|
159
|
+
const updated = await getRun(id);
|
|
160
|
+
if (updated === null)
|
|
161
|
+
throw notFound(id);
|
|
162
|
+
return updated;
|
|
163
|
+
}
|
|
164
|
+
async function recordItem(item) {
|
|
165
|
+
await ensureTables();
|
|
166
|
+
await db
|
|
167
|
+
.query(sql `
|
|
168
|
+
insert into ${itemsTable} (run_id, source_id, collection, entry_id, created_at)
|
|
169
|
+
values (${item.runId}, ${item.sourceId}, ${item.collection}, ${item.entryId}, ${new Date(now()).toISOString()})`)
|
|
170
|
+
.catch(() => undefined); // unique (run_id, source_id): a retried item after a crash is a no-op, not an error
|
|
171
|
+
}
|
|
172
|
+
async function doneSourceIds(runId) {
|
|
173
|
+
await ensureTables();
|
|
174
|
+
const found = await db.query(sql `
|
|
175
|
+
select source_id from ${itemsTable} where run_id = ${runId}`);
|
|
176
|
+
return new Set(found.rows.map((row) => row.source_id));
|
|
177
|
+
}
|
|
178
|
+
async function listItems(runId) {
|
|
179
|
+
await ensureTables();
|
|
180
|
+
const found = await db.query(sql `select * from ${itemsTable} where run_id = ${runId} order by created_at asc`);
|
|
181
|
+
return found.rows.map((row) => ({
|
|
182
|
+
runId: row.run_id,
|
|
183
|
+
sourceId: row.source_id,
|
|
184
|
+
collection: row.collection,
|
|
185
|
+
entryId: row.entry_id,
|
|
186
|
+
createdAt: row.created_at,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
ensureTables,
|
|
191
|
+
createRun,
|
|
192
|
+
getRun,
|
|
193
|
+
listRuns,
|
|
194
|
+
updateRun,
|
|
195
|
+
recordItem,
|
|
196
|
+
doneSourceIds,
|
|
197
|
+
listItems,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=tracking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"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"}
|
package/dist/undo.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CollectionDefinition, ContentStore } from '@cogenta/schema';
|
|
2
|
+
import type { ImportTrackingStore } from './tracking.js';
|
|
3
|
+
/**
|
|
4
|
+
* Undoing an import (fiche 25 task 4): trash every entry recorded for a run.
|
|
5
|
+
*
|
|
6
|
+
* Trash, never `purge` — ADR-0022's whole point is that `delete()` leaves
|
|
7
|
+
* every row in place, so `untrash()` can bring an over-eager undo back. A
|
|
8
|
+
* run that has already been cancelled, or that was never applied, has no
|
|
9
|
+
* items and undoes cleanly to nothing.
|
|
10
|
+
*/
|
|
11
|
+
export interface UndoImportOptions {
|
|
12
|
+
readonly tracking: ImportTrackingStore;
|
|
13
|
+
readonly runId: string;
|
|
14
|
+
readonly storeFor: (collectionName: string) => ContentStore | undefined;
|
|
15
|
+
readonly collections?: readonly CollectionDefinition[];
|
|
16
|
+
}
|
|
17
|
+
export interface UndoImportReport {
|
|
18
|
+
readonly trashed: number;
|
|
19
|
+
readonly alreadyGone: number;
|
|
20
|
+
readonly failed: readonly {
|
|
21
|
+
readonly collection: string;
|
|
22
|
+
readonly entryId: string;
|
|
23
|
+
readonly reason: string;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
export declare function undoImport(options: UndoImportOptions): Promise<UndoImportReport>;
|
|
27
|
+
//# sourceMappingURL=undo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../src/undo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAExD;;;;;;;GAOG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,CAAA;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS;QACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;QAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KACxB,EAAE,CAAA;CACJ;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6CtF"}
|
package/dist/undo.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CogentaError } from '@cogenta/core';
|
|
2
|
+
export async function undoImport(options) {
|
|
3
|
+
const run = await options.tracking.getRun(options.runId);
|
|
4
|
+
if (run === null) {
|
|
5
|
+
throw new CogentaError({
|
|
6
|
+
code: 'IMPORT_RUN_NOT_FOUND',
|
|
7
|
+
message: `No import run "${options.runId}" exists.`,
|
|
8
|
+
hint: 'Only a run that has been analyzed and applied can be cancelled.',
|
|
9
|
+
details: { id: options.runId },
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
const items = await options.tracking.listItems(options.runId);
|
|
13
|
+
let trashed = 0;
|
|
14
|
+
let alreadyGone = 0;
|
|
15
|
+
const failed = [];
|
|
16
|
+
for (const item of items) {
|
|
17
|
+
const store = options.storeFor(item.collection);
|
|
18
|
+
if (store === undefined) {
|
|
19
|
+
failed.push({
|
|
20
|
+
collection: item.collection,
|
|
21
|
+
entryId: item.entryId,
|
|
22
|
+
reason: `Collection "${item.collection}" is no longer part of this site.`,
|
|
23
|
+
});
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const removed = await store.delete(item.entryId);
|
|
28
|
+
if (removed)
|
|
29
|
+
trashed += 1;
|
|
30
|
+
else
|
|
31
|
+
alreadyGone += 1;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
failed.push({
|
|
35
|
+
collection: item.collection,
|
|
36
|
+
entryId: item.entryId,
|
|
37
|
+
reason: error instanceof CogentaError ? error.message : String(error),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await options.tracking.updateRun(options.runId, {
|
|
42
|
+
status: 'cancelled',
|
|
43
|
+
report: { trashed, alreadyGone, failed },
|
|
44
|
+
});
|
|
45
|
+
return { trashed, alreadyGone, failed };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=undo.js.map
|
package/dist/undo.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undo.js","sourceRoot":"","sources":["../src/undo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AA8B5C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA0B;IACzD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACxD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,kBAAkB,OAAO,CAAC,KAAK,WAAW;YACnD,IAAI,EAAE,iEAAiE;YACvE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,EAAE;SAC/B,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC7D,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,MAAM,MAAM,GAA8D,EAAE,CAAA;IAE5E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC/C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC;gBACV,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,eAAe,IAAI,CAAC,UAAU,mCAAmC;aAC1E,CAAC,CAAA;YACF,SAAQ;QACV,CAAC;QACD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAChD,IAAI,OAAO;gBAAE,OAAO,IAAI,CAAC,CAAA;;gBACpB,WAAW,IAAI,CAAC,CAAA;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,KAAK,YAAY,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aACtE,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE;QAC9C,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE;KACzC,CAAC,CAAA;IAEF,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAA;AACzC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface WordPressPreviewCounts {
|
|
2
|
+
readonly posts: number;
|
|
3
|
+
readonly pages: number;
|
|
4
|
+
readonly categories: number;
|
|
5
|
+
readonly tags: number;
|
|
6
|
+
readonly comments: number;
|
|
7
|
+
readonly attachments: number;
|
|
8
|
+
}
|
|
9
|
+
export interface WordPressSlugConflict {
|
|
10
|
+
readonly kind: 'post' | 'page';
|
|
11
|
+
readonly slug: string;
|
|
12
|
+
/** WordPress ids sharing this slug — Cogenta's `unique` slug field will refuse all but the first. */
|
|
13
|
+
readonly wpIds: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export interface WordPressPreviewIgnoredItem {
|
|
16
|
+
readonly type: 'post' | 'page' | 'comment';
|
|
17
|
+
readonly wpId: string;
|
|
18
|
+
readonly title: string;
|
|
19
|
+
readonly reason: string;
|
|
20
|
+
}
|
|
21
|
+
export interface WordPressPreviewReport {
|
|
22
|
+
readonly counts: WordPressPreviewCounts;
|
|
23
|
+
/** Fixed target collections this importer writes to — see `wordpress/collections.ts`'s header note on why they are not the site's own. */
|
|
24
|
+
readonly collectionMapping: {
|
|
25
|
+
readonly post: string;
|
|
26
|
+
readonly page: string;
|
|
27
|
+
};
|
|
28
|
+
readonly authors: readonly {
|
|
29
|
+
readonly login: string;
|
|
30
|
+
readonly email: string;
|
|
31
|
+
}[];
|
|
32
|
+
readonly mediaUrls: readonly string[];
|
|
33
|
+
/** `content-length` is unknown before the apply phase actually fetches each URL — this counts distinct URLs, not bytes. */
|
|
34
|
+
readonly mediaCount: number;
|
|
35
|
+
readonly slugConflicts: readonly WordPressSlugConflict[];
|
|
36
|
+
readonly ignored: readonly WordPressPreviewIgnoredItem[];
|
|
37
|
+
readonly warnings: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function analyzeWordPress(xml: string): WordPressPreviewReport;
|
|
40
|
+
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/wordpress/analyze.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,qGAAqG;IACrG,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAA;IACvC,0IAA0I;IAC1I,QAAQ,CAAC,iBAAiB,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/E,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,2HAA2H;IAC3H,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,aAAa,EAAE,SAAS,qBAAqB,EAAE,CAAA;IACxD,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAmBD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,CA2FpE"}
|