@krantho/persistence 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/dist/auth.d.ts +79 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +2 -0
- package/dist/auth.js.map +1 -0
- package/dist/content-contract.d.ts +36 -0
- package/dist/content-contract.d.ts.map +1 -0
- package/dist/content-contract.js +70 -0
- package/dist/content-contract.js.map +1 -0
- package/dist/content-query.d.ts +48 -0
- package/dist/content-query.d.ts.map +1 -0
- package/dist/content-query.js +5 -0
- package/dist/content-query.js.map +1 -0
- package/dist/content-row.d.ts +59 -0
- package/dist/content-row.d.ts.map +1 -0
- package/dist/content-row.js +60 -0
- package/dist/content-row.js.map +1 -0
- package/dist/index.d.ts +503 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/live-version.d.ts +44 -0
- package/dist/live-version.d.ts.map +1 -0
- package/dist/live-version.js +74 -0
- package/dist/live-version.js.map +1 -0
- package/dist/migration-utils.d.ts +35 -0
- package/dist/migration-utils.d.ts.map +1 -0
- package/dist/migration-utils.js +50 -0
- package/dist/migration-utils.js.map +1 -0
- package/dist/migrations.d.ts +119 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +2 -0
- package/dist/migrations.js.map +1 -0
- package/dist/page-cache.d.ts +77 -0
- package/dist/page-cache.d.ts.map +1 -0
- package/dist/page-cache.js +65 -0
- package/dist/page-cache.js.map +1 -0
- package/dist/repository-content.d.ts +45 -0
- package/dist/repository-content.d.ts.map +1 -0
- package/dist/repository-content.js +94 -0
- package/dist/repository-content.js.map +1 -0
- package/dist/sort-columns.d.ts +19 -0
- package/dist/sort-columns.d.ts.map +1 -0
- package/dist/sort-columns.js +21 -0
- package/dist/sort-columns.js.map +1 -0
- package/package.json +34 -3
- package/README.md +0 -3
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live-version rules shared by the adapters.
|
|
3
|
+
*
|
|
4
|
+
* A document has a current version (its newest row, what Studio edits) and
|
|
5
|
+
* at most one live version (what visitors see). Every appended row records
|
|
6
|
+
* `live_version`: the document's live version as of that row, or null when
|
|
7
|
+
* nothing is live. Only publishing changes what visitors see — a save appends
|
|
8
|
+
* a new current version and keeps the live one — and a document's publication
|
|
9
|
+
* state is read from its live version alone (see {@link statusOf}).
|
|
10
|
+
*/
|
|
11
|
+
/** What a write does to the live version: publishing makes the new row live, unpublishing and archiving take the document offline, a save keeps it */
|
|
12
|
+
export function liveOnWrite(publication) {
|
|
13
|
+
if (publication === 'publish')
|
|
14
|
+
return 'self';
|
|
15
|
+
if (publication === 'unpublish' || publication === 'archive')
|
|
16
|
+
return 'clear';
|
|
17
|
+
return 'keep';
|
|
18
|
+
}
|
|
19
|
+
/** The publication a file import makes: the file's status says whether the document is live */
|
|
20
|
+
export function publicationOfFile(status) {
|
|
21
|
+
if (status === 'published')
|
|
22
|
+
return 'publish';
|
|
23
|
+
return status === 'archived' ? 'archive' : 'unpublish';
|
|
24
|
+
}
|
|
25
|
+
/** The live version an appended row records */
|
|
26
|
+
export function resolveLiveVersion(directive, version, previous) {
|
|
27
|
+
if (directive === 'self')
|
|
28
|
+
return version;
|
|
29
|
+
if (directive === 'clear')
|
|
30
|
+
return null;
|
|
31
|
+
return previous;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The status an appended row stores, consistent with how it is read: archived
|
|
35
|
+
* when archived (a save keeps an archived document archived), published while
|
|
36
|
+
* a version is live, draft otherwise.
|
|
37
|
+
*/
|
|
38
|
+
export function storedStatus(publication, liveVersion, previousStatus) {
|
|
39
|
+
if (publication === 'archive')
|
|
40
|
+
return 'archived';
|
|
41
|
+
if (liveVersion !== null)
|
|
42
|
+
return 'published';
|
|
43
|
+
if (publication === undefined && previousStatus === 'archived')
|
|
44
|
+
return 'archived';
|
|
45
|
+
return 'draft';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A row's publication state: a tombstone is deleted and an archived document
|
|
49
|
+
* archived; otherwise published while a version is live, draft when none is.
|
|
50
|
+
* Rows written before saves kept the live version read correctly too.
|
|
51
|
+
*/
|
|
52
|
+
export function statusOf(stored, liveVersion) {
|
|
53
|
+
if (stored === 'deleted')
|
|
54
|
+
return 'deleted';
|
|
55
|
+
if (liveVersion !== null)
|
|
56
|
+
return 'published';
|
|
57
|
+
return stored === 'archived' ? 'archived' : 'draft';
|
|
58
|
+
}
|
|
59
|
+
/** Whether visitors see a document's current version — after a save, true only where saves are live (no history) */
|
|
60
|
+
export function currentIsLive(meta) {
|
|
61
|
+
return meta.liveVersion !== null && meta.liveVersion === meta.version;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The SQL condition that selects current rows by publication state, the same
|
|
65
|
+
* on SQLite and Postgres (columns `status` and `live_version`).
|
|
66
|
+
*/
|
|
67
|
+
export function statusCondition(status) {
|
|
68
|
+
if (status === 'published')
|
|
69
|
+
return 'live_version IS NOT NULL';
|
|
70
|
+
if (status === 'archived')
|
|
71
|
+
return "live_version IS NULL AND status = 'archived'";
|
|
72
|
+
return "live_version IS NULL AND status NOT IN ('archived', 'deleted')";
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=live-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-version.js","sourceRoot":"","sources":["../src/live-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,sJAAsJ;AACtJ,MAAM,UAAU,WAAW,CAAC,WAAoC;IAC9D,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IAC5C,IAAI,WAAW,KAAK,WAAW,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,OAAO,CAAA;IAC5E,OAAO,MAAM,CAAA;AACf,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAA;IAC5C,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAA;AACxD,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,kBAAkB,CAChC,SAAwB,EACxB,OAAe,EACf,QAAuB;IAEvB,IAAI,SAAS,KAAK,MAAM;QAAE,OAAO,OAAO,CAAA;IACxC,IAAI,SAAS,KAAK,OAAO;QAAE,OAAO,IAAI,CAAA;IACtC,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,WAAoC,EACpC,WAA0B,EAC1B,cAAkC;IAElC,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,UAAU,CAAA;IAChD,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,WAAW,CAAA;IAC5C,IAAI,WAAW,KAAK,SAAS,IAAI,cAAc,KAAK,UAAU;QAAE,OAAO,UAAU,CAAA;IACjF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,WAA0B;IACjE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC1C,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,WAAW,CAAA;IAC5C,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAA;AACrD,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,aAAa,CAAC,IAAqD;IACjF,OAAO,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,OAAO,CAAA;AACvE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAA0C;IACxE,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,0BAA0B,CAAA;IAC7D,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,8CAA8C,CAAA;IAChF,OAAO,gEAAgE,CAAA;AACzE,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AppliedMigration, ContentManifest } from './migrations.js';
|
|
2
|
+
/** Minimal migration identity — version + name, shared by all adapters */
|
|
3
|
+
export interface MigrationIdentity {
|
|
4
|
+
version: number;
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Verify that already-applied migrations have not been modified.
|
|
9
|
+
* Requires a checksum function injected by the caller (since node:crypto
|
|
10
|
+
* is not available in the interface package).
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateChecksums(migrations: readonly MigrationIdentity[], appliedMap: Map<number, {
|
|
13
|
+
checksum: string;
|
|
14
|
+
}>, computeChecksum: (migration: MigrationIdentity) => string): void;
|
|
15
|
+
/**
|
|
16
|
+
* Determine the current schema version from applied and newly-applied migrations.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveCurrentVersion(appliedVersions: Iterable<number>, newlyApplied: readonly AppliedMigration[]): number;
|
|
19
|
+
/**
|
|
20
|
+
* Format a migration failure error message.
|
|
21
|
+
*/
|
|
22
|
+
export declare function migrationFailedMessage(version: number, name: string, err: unknown): string;
|
|
23
|
+
/** Content migration decision */
|
|
24
|
+
export type MigrationDecision = {
|
|
25
|
+
action: 'skip';
|
|
26
|
+
reason: string;
|
|
27
|
+
} | {
|
|
28
|
+
action: 'full-import';
|
|
29
|
+
} | {
|
|
30
|
+
action: 'migrate';
|
|
31
|
+
repoVersion: number;
|
|
32
|
+
};
|
|
33
|
+
/** Determine what content migration action to take */
|
|
34
|
+
export declare function decideMigration(repoManifest: ContentManifest | null, dbVersion: number | null): MigrationDecision;
|
|
35
|
+
//# sourceMappingURL=migration-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-utils.d.ts","sourceRoot":"","sources":["../src/migration-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAExE,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,iBAAiB,EAAE,EACxC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7C,eAAe,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,MAAM,GACxD,IAAI,CAcN;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,EACjC,YAAY,EAAE,SAAS,gBAAgB,EAAE,GACxC,MAAM,CAGR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAM1F;AAED,iCAAiC;AACjC,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GACzB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9C,sDAAsD;AACtD,wBAAgB,eAAe,CAC7B,YAAY,EAAE,eAAe,GAAG,IAAI,EACpC,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,iBAAiB,CAiBnB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify that already-applied migrations have not been modified.
|
|
3
|
+
* Requires a checksum function injected by the caller (since node:crypto
|
|
4
|
+
* is not available in the interface package).
|
|
5
|
+
*/
|
|
6
|
+
export function validateChecksums(migrations, appliedMap, computeChecksum) {
|
|
7
|
+
for (const migration of migrations) {
|
|
8
|
+
const existing = appliedMap.get(migration.version);
|
|
9
|
+
if (!existing)
|
|
10
|
+
continue;
|
|
11
|
+
const expectedChecksum = computeChecksum(migration);
|
|
12
|
+
if (existing.checksum !== expectedChecksum) {
|
|
13
|
+
throw new Error(`Migration V${migration.version} (${migration.name}) checksum mismatch. ` +
|
|
14
|
+
`Expected ${expectedChecksum}, found ${existing.checksum}. ` +
|
|
15
|
+
`Applied migrations must not be modified.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Determine the current schema version from applied and newly-applied migrations.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveCurrentVersion(appliedVersions, newlyApplied) {
|
|
23
|
+
const allVersions = [...appliedVersions, ...newlyApplied.map((m) => m.version)];
|
|
24
|
+
return allVersions.length > 0 ? Math.max(...allVersions) : 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Format a migration failure error message.
|
|
28
|
+
*/
|
|
29
|
+
export function migrationFailedMessage(version, name, err) {
|
|
30
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31
|
+
return (`Migration V${version} (${name}) failed: ${message}. ` +
|
|
32
|
+
`Database has been rolled back to the state before this migration.`);
|
|
33
|
+
}
|
|
34
|
+
/** Determine what content migration action to take */
|
|
35
|
+
export function decideMigration(repoManifest, dbVersion) {
|
|
36
|
+
if (!repoManifest) {
|
|
37
|
+
return { action: 'skip', reason: 'No content manifest found in repo' };
|
|
38
|
+
}
|
|
39
|
+
if (dbVersion === null) {
|
|
40
|
+
return { action: 'full-import' };
|
|
41
|
+
}
|
|
42
|
+
if (dbVersion >= repoManifest.version) {
|
|
43
|
+
return {
|
|
44
|
+
action: 'skip',
|
|
45
|
+
reason: `DB version ${dbVersion} >= repo version ${repoManifest.version}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { action: 'migrate', repoVersion: repoManifest.version };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=migration-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-utils.js","sourceRoot":"","sources":["../src/migration-utils.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAwC,EACxC,UAA6C,EAC7C,eAAyD;IAEzD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;QAClD,IAAI,CAAC,QAAQ;YAAE,SAAQ;QAEvB,MAAM,gBAAgB,GAAG,eAAe,CAAC,SAAS,CAAC,CAAA;QACnD,IAAI,QAAQ,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,cAAc,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,IAAI,uBAAuB;gBACvE,YAAY,gBAAgB,WAAW,QAAQ,CAAC,QAAQ,IAAI;gBAC5D,0CAA0C,CAC7C,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,eAAiC,EACjC,YAAyC;IAEzC,MAAM,WAAW,GAAG,CAAC,GAAG,eAAe,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;IAC/E,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe,EAAE,IAAY,EAAE,GAAY;IAChF,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAChE,OAAO,CACL,cAAc,OAAO,KAAK,IAAI,aAAa,OAAO,IAAI;QACtD,mEAAmE,CACpE,CAAA;AACH,CAAC;AAQD,sDAAsD;AACtD,MAAM,UAAU,eAAe,CAC7B,YAAoC,EACpC,SAAwB;IAExB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,mCAAmC,EAAE,CAAA;IACxE,CAAC;IAED,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAA;IAClC,CAAC;IAED,IAAI,SAAS,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;QACtC,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,cAAc,SAAS,oBAAoB,YAAY,CAAC,OAAO,EAAE;SAC1E,CAAA;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC,OAAO,EAAE,CAAA;AACjE,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/** A single numbered database migration */
|
|
2
|
+
export interface Migration {
|
|
3
|
+
/** Monotonic version number: 1, 2, 3, ... */
|
|
4
|
+
version: number;
|
|
5
|
+
/** Human-readable name: 'initial_tables', 'add_locale_column' */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Apply the migration. Must be idempotent within its transaction. */
|
|
8
|
+
apply: MigrationApplyFn;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* SQLite migrations receive a better-sqlite3 Database.
|
|
12
|
+
* Postgres migrations receive a postgres.js Sql within a transaction.
|
|
13
|
+
* Each adapter defines its own concrete type — this is the generic shape.
|
|
14
|
+
*/
|
|
15
|
+
export type MigrationApplyFn = (db: unknown) => void | Promise<void>;
|
|
16
|
+
/** Record of an applied migration stored in schema_migrations */
|
|
17
|
+
export interface AppliedMigration {
|
|
18
|
+
version: number;
|
|
19
|
+
name: string;
|
|
20
|
+
checksum: string;
|
|
21
|
+
appliedAt: string;
|
|
22
|
+
}
|
|
23
|
+
/** Result of running the migration process */
|
|
24
|
+
export interface MigrationResult {
|
|
25
|
+
/** Migrations that were applied in this run */
|
|
26
|
+
applied: AppliedMigration[];
|
|
27
|
+
/** Current schema version after migration */
|
|
28
|
+
currentVersion: number;
|
|
29
|
+
}
|
|
30
|
+
/** Trigger that caused a checkpoint to be created */
|
|
31
|
+
export type CheckpointTrigger = 'manual' | 'pre-migration' | 'pre-restore' | 'pre-import';
|
|
32
|
+
/** Metadata for a checkpoint (lightweight timestamp marker) */
|
|
33
|
+
export interface CheckpointInfo {
|
|
34
|
+
id: number;
|
|
35
|
+
version: number;
|
|
36
|
+
name: string | null;
|
|
37
|
+
trigger: CheckpointTrigger;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
}
|
|
40
|
+
/** Interface for checkpoint operations — point-in-time restore via is_current flags */
|
|
41
|
+
export interface ICheckpointAdapter {
|
|
42
|
+
/** Create a checkpoint (records timestamp, no data copying) */
|
|
43
|
+
createCheckpoint(name?: string | undefined, trigger?: CheckpointTrigger | undefined): Promise<CheckpointInfo>;
|
|
44
|
+
/** List all checkpoints (newest first) */
|
|
45
|
+
listCheckpoints(): Promise<CheckpointInfo[]>;
|
|
46
|
+
/** Get a single checkpoint by version */
|
|
47
|
+
getCheckpoint(version: number): Promise<CheckpointInfo | null>;
|
|
48
|
+
/** Restore to a checkpoint (rewinds is_current flags to checkpoint time) */
|
|
49
|
+
restoreCheckpoint(version: number): Promise<CheckpointInfo>;
|
|
50
|
+
/** Delete a checkpoint */
|
|
51
|
+
deleteCheckpoint(version: number): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
/** Content manifest file format (content/manifest.json) */
|
|
54
|
+
export interface ContentManifest {
|
|
55
|
+
version: number;
|
|
56
|
+
/** The git commit the content was built from, when the build knew it */
|
|
57
|
+
commit?: string | undefined;
|
|
58
|
+
generatedAt: string;
|
|
59
|
+
files: Record<string, {
|
|
60
|
+
checksum: string;
|
|
61
|
+
size: number;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/** Record of an imported content manifest version */
|
|
65
|
+
export interface ContentManifestRecord {
|
|
66
|
+
id: number;
|
|
67
|
+
repoVersion: number;
|
|
68
|
+
/** The git commit the imported content came from: the baseline a pull of production's edits starts at */
|
|
69
|
+
repoCommit: string | null;
|
|
70
|
+
importedAt: string;
|
|
71
|
+
/** Each file's checksum as the database last took it from the repository, by path */
|
|
72
|
+
fileChecksums: Record<string, string>;
|
|
73
|
+
}
|
|
74
|
+
/** A content migration conflict */
|
|
75
|
+
export interface ContentConflict {
|
|
76
|
+
id: number;
|
|
77
|
+
migrationId: number;
|
|
78
|
+
collection: string;
|
|
79
|
+
slug: string;
|
|
80
|
+
locale: string;
|
|
81
|
+
repoChecksum: string;
|
|
82
|
+
dbChecksum: string;
|
|
83
|
+
resolved: boolean;
|
|
84
|
+
resolvedAt: string | null;
|
|
85
|
+
resolution: 'keep_db' | 'use_repo' | 'merged' | null;
|
|
86
|
+
}
|
|
87
|
+
/** Resolution for a content conflict */
|
|
88
|
+
export type ConflictResolution = 'keep_db' | 'use_repo';
|
|
89
|
+
/** Interface for content migration operations */
|
|
90
|
+
export interface IContentMigrationAdapter {
|
|
91
|
+
/** Get the current content manifest version from DB */
|
|
92
|
+
getContentManifestVersion(): Promise<number | null>;
|
|
93
|
+
/** Get the latest content manifest record */
|
|
94
|
+
getContentManifest(): Promise<ContentManifestRecord | null>;
|
|
95
|
+
/** Save a new content manifest record */
|
|
96
|
+
saveContentManifest(manifest: ContentManifestRecord): Promise<void>;
|
|
97
|
+
/** List unresolved content migration conflicts */
|
|
98
|
+
listConflicts(): Promise<ContentConflict[]>;
|
|
99
|
+
/** Resolve a content migration conflict */
|
|
100
|
+
resolveConflict(conflictId: number, resolution: ConflictResolution): Promise<boolean>;
|
|
101
|
+
/** Record a content migration conflict */
|
|
102
|
+
recordConflict(conflict: Omit<ContentConflict, 'id' | 'resolved' | 'resolvedAt' | 'resolution'>): Promise<void>;
|
|
103
|
+
}
|
|
104
|
+
/** Migration status returned by the API */
|
|
105
|
+
export interface MigrationStatus {
|
|
106
|
+
/** Current physical schema version */
|
|
107
|
+
schemaVersion: number;
|
|
108
|
+
/** Whether checkpoint system is available */
|
|
109
|
+
checkpointEnabled: boolean;
|
|
110
|
+
/** Whether content migration system is available */
|
|
111
|
+
contentMigrationEnabled: boolean;
|
|
112
|
+
/** Content manifest version in repo (null if no manifest) */
|
|
113
|
+
repoContentVersion: number | null;
|
|
114
|
+
/** Content manifest version in DB (null if never imported) */
|
|
115
|
+
dbContentVersion: number | null;
|
|
116
|
+
/** Number of unresolved content conflicts */
|
|
117
|
+
unresolvedConflicts: number;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared page cache store — rendered pages and fetch results kept in the
|
|
3
|
+
* site's own database, so every instance of a multi-instance site reads and
|
|
4
|
+
* invalidates one cache.
|
|
5
|
+
*
|
|
6
|
+
* Timestamps are epoch milliseconds from the store's clock: the store stamps an
|
|
7
|
+
* entry's `lastModified` on `set` and a tag's mark on `markTags` from the same
|
|
8
|
+
* clock (the database server's clock on Postgres), so instances with skewed
|
|
9
|
+
* clocks still compare one entry against one mark consistently.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* A value the page cache stores: JSON data whose leaves may be binary.
|
|
13
|
+
*
|
|
14
|
+
* Binary leaves (`Uint8Array`, including Node `Buffer`) are stored base64-encoded
|
|
15
|
+
* inside an object `{ "__kranthoBytes": "<base64>" }` and come back as
|
|
16
|
+
* `Uint8Array`; that key is reserved. Object members that are `undefined` are
|
|
17
|
+
* dropped, as in JSON. Maps, Dates and class instances are not values — the
|
|
18
|
+
* caller converts them before `set`.
|
|
19
|
+
*/
|
|
20
|
+
export type PageCacheValue = null | boolean | number | string | Uint8Array | readonly PageCacheValue[] | {
|
|
21
|
+
readonly [key: string]: PageCacheValue | undefined;
|
|
22
|
+
};
|
|
23
|
+
/** One cached entry as the store returns it */
|
|
24
|
+
export interface PageCacheEntry {
|
|
25
|
+
/** The value handed to `set`, binary leaves restored as `Uint8Array` */
|
|
26
|
+
value: PageCacheValue;
|
|
27
|
+
/** The entry's tags as of its last `set` (deduplicated, in first-seen order) */
|
|
28
|
+
tags: string[];
|
|
29
|
+
/** When the entry was last written, epoch milliseconds on the store's clock */
|
|
30
|
+
lastModified: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* What marks on a set of tags mean for an entry written at a given time.
|
|
34
|
+
* `expired` — a tag was expired at or after that time: the entry must not be served.
|
|
35
|
+
* `stale` — a tag was marked stale (and none expired) at or after that time: the
|
|
36
|
+
* entry may be served while it is refreshed. `fresh` — no mark since.
|
|
37
|
+
*/
|
|
38
|
+
export type PageCacheTagState = 'fresh' | 'stale' | 'expired';
|
|
39
|
+
/** How `markTags` marks its tags */
|
|
40
|
+
export interface PageCacheMarkOptions {
|
|
41
|
+
/**
|
|
42
|
+
* `true` expires the tags now (entries written up to now are not served) and
|
|
43
|
+
* marks them stale; `false` only marks them stale now. A mark never moves a
|
|
44
|
+
* tag's timestamps backwards, and a stale mark never clears an earlier expiry.
|
|
45
|
+
*/
|
|
46
|
+
expire: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Shared page cache persistence: entries by key, and the latest stale and expire
|
|
50
|
+
* mark per tag. Every write is safe under concurrent instances — `set` is an
|
|
51
|
+
* upsert and `markTags` is idempotent and monotonic.
|
|
52
|
+
*/
|
|
53
|
+
export interface IPageCacheStore {
|
|
54
|
+
/** Read an entry by key, or null when none is stored */
|
|
55
|
+
get(key: string): Promise<PageCacheEntry | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Write an entry (upsert). Replaces the value and the tags of an existing entry
|
|
58
|
+
* — tags are never appended — and stamps `lastModified` with the store's clock.
|
|
59
|
+
*/
|
|
60
|
+
set(key: string, value: PageCacheValue, tags: readonly string[]): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Whether any of `tags` was expired or marked stale at or after `since` (epoch
|
|
63
|
+
* milliseconds, normally an entry's `lastModified`). Tags never marked are fresh.
|
|
64
|
+
*/
|
|
65
|
+
tagsRevalidatedSince(tags: readonly string[], since: number): Promise<PageCacheTagState>;
|
|
66
|
+
/** Mark tags stale, or expired, as of now on the store's clock */
|
|
67
|
+
markTags(tags: readonly string[], options: PageCacheMarkOptions): Promise<void>;
|
|
68
|
+
/** How many entries (pages and data) carry any of `tags` */
|
|
69
|
+
countTagged(tags: readonly string[]): Promise<number>;
|
|
70
|
+
}
|
|
71
|
+
/** Unique tags in first-seen order */
|
|
72
|
+
export declare function uniquePageCacheTags(tags: readonly string[]): string[];
|
|
73
|
+
/** Serialise a page cache value to text, binary leaves as base64 (see `PageCacheValue`) */
|
|
74
|
+
export declare function encodePageCacheValue(value: PageCacheValue): string;
|
|
75
|
+
/** Parse text written by `encodePageCacheValue`, restoring binary leaves as `Uint8Array` */
|
|
76
|
+
export declare function decodePageCacheValue(text: string): PageCacheValue;
|
|
77
|
+
//# sourceMappingURL=page-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-cache.d.ts","sourceRoot":"","sources":["../src/page-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,UAAU,GACV,SAAS,cAAc,EAAE,GACzB;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAA;CAAE,CAAA;AAE1D,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,KAAK,EAAE,cAAc,CAAA;IACrB,gFAAgF;IAChF,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAA;AAE7D,oCAAoC;AACpC,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IAChD;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/E;;;OAGG;IACH,oBAAoB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACxF,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/E,4DAA4D;IAC5D,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACtD;AAKD,sCAAsC;AACtC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAErE;AAED,2FAA2F;AAC3F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAElE;AAED,4FAA4F;AAC5F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAKjE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared page cache store — rendered pages and fetch results kept in the
|
|
3
|
+
* site's own database, so every instance of a multi-instance site reads and
|
|
4
|
+
* invalidates one cache.
|
|
5
|
+
*
|
|
6
|
+
* Timestamps are epoch milliseconds from the store's clock: the store stamps an
|
|
7
|
+
* entry's `lastModified` on `set` and a tag's mark on `markTags` from the same
|
|
8
|
+
* clock (the database server's clock on Postgres), so instances with skewed
|
|
9
|
+
* clocks still compare one entry against one mark consistently.
|
|
10
|
+
*/
|
|
11
|
+
const BYTES_KEY = '__kranthoBytes';
|
|
12
|
+
const BASE64_CHUNK = 0x8000;
|
|
13
|
+
/** Unique tags in first-seen order */
|
|
14
|
+
export function uniquePageCacheTags(tags) {
|
|
15
|
+
return [...new Set(tags)];
|
|
16
|
+
}
|
|
17
|
+
/** Serialise a page cache value to text, binary leaves as base64 (see `PageCacheValue`) */
|
|
18
|
+
export function encodePageCacheValue(value) {
|
|
19
|
+
return JSON.stringify(toJsonValue(value));
|
|
20
|
+
}
|
|
21
|
+
/** Parse text written by `encodePageCacheValue`, restoring binary leaves as `Uint8Array` */
|
|
22
|
+
export function decodePageCacheValue(text) {
|
|
23
|
+
return JSON.parse(text, (_key, value) => {
|
|
24
|
+
if (!isBytesMarker(value))
|
|
25
|
+
return value;
|
|
26
|
+
return base64ToBytes(value[BYTES_KEY]);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** Replace binary leaves with base64 markers (before JSON.stringify, which would call `Buffer#toJSON`) */
|
|
30
|
+
function toJsonValue(value) {
|
|
31
|
+
if (value instanceof Uint8Array)
|
|
32
|
+
return { [BYTES_KEY]: bytesToBase64(value) };
|
|
33
|
+
if (Array.isArray(value))
|
|
34
|
+
return value.map((item) => toJsonValue(item));
|
|
35
|
+
if (value === null || typeof value !== 'object')
|
|
36
|
+
return value;
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [key, member] of Object.entries(value)) {
|
|
39
|
+
if (member !== undefined)
|
|
40
|
+
out[key] = toJsonValue(member);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
function isBytesMarker(value) {
|
|
45
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
46
|
+
return false;
|
|
47
|
+
const keys = Object.keys(value);
|
|
48
|
+
const encoded = value[BYTES_KEY];
|
|
49
|
+
return keys.length === 1 && keys[0] === BYTES_KEY && typeof encoded === 'string';
|
|
50
|
+
}
|
|
51
|
+
function bytesToBase64(bytes) {
|
|
52
|
+
let binary = '';
|
|
53
|
+
for (let i = 0; i < bytes.length; i += BASE64_CHUNK) {
|
|
54
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + BASE64_CHUNK));
|
|
55
|
+
}
|
|
56
|
+
return btoa(binary);
|
|
57
|
+
}
|
|
58
|
+
function base64ToBytes(text) {
|
|
59
|
+
const binary = atob(text);
|
|
60
|
+
const bytes = new Uint8Array(binary.length);
|
|
61
|
+
for (let i = 0; i < binary.length; i++)
|
|
62
|
+
bytes[i] = binary.charCodeAt(i);
|
|
63
|
+
return bytes;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=page-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-cache.js","sourceRoot":"","sources":["../src/page-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ContentContract, RepositoryDocument } from '@krantho/core';
|
|
2
|
+
import type { ContentDocument } from './index.js';
|
|
3
|
+
export type { RepositoryBasis, RepositoryDocument } from '@krantho/core';
|
|
4
|
+
/** Current-content export is not a history archive or an impersonation of its author. */
|
|
5
|
+
export declare function repositoryDocument(document: ContentDocument, checksum: string): RepositoryDocument;
|
|
6
|
+
/**
|
|
7
|
+
* Markdown with YAML frontmatter, readable and diffable: block style, plain
|
|
8
|
+
* keys (quoted when YAML would read them as something else), every one-line
|
|
9
|
+
* string double-quoted so a date, a number-like slug or "no" reads back as the
|
|
10
|
+
* string it was, and multi-line text as a block literal.
|
|
11
|
+
*/
|
|
12
|
+
export declare function repositoryMarkdown(document: RepositoryDocument): string;
|
|
13
|
+
/** What a repository file holds for a document */
|
|
14
|
+
export interface RepositoryFileInput {
|
|
15
|
+
fields: Record<string, unknown>;
|
|
16
|
+
body: string;
|
|
17
|
+
metadata: Record<string, unknown>;
|
|
18
|
+
defaults: readonly unknown[];
|
|
19
|
+
/** Written as the file's `status`, which the import reads: published, draft or archived */
|
|
20
|
+
status: string;
|
|
21
|
+
/** Written only for a locale other than the site's default */
|
|
22
|
+
locale?: string | undefined;
|
|
23
|
+
/** The order of the author fields in the frontmatter (the schema's declared order); others follow */
|
|
24
|
+
fieldOrder?: readonly string[] | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A document as a content file in a project's repository: the fields the author wrote, in the
|
|
28
|
+
* schema's order, then `status`, `locale` when it is not the default, and the metadata and
|
|
29
|
+
* defaults blocks when there are any. It leaves out what the file's place and the active contract
|
|
30
|
+
* already say (identity, contract, basis), so a diff against the file an editor changed shows
|
|
31
|
+
* the change, not a new layout. Strings are plain where YAML reads them back as strings and
|
|
32
|
+
* single-quoted where it would not, the way people write them.
|
|
33
|
+
*/
|
|
34
|
+
export declare function repositoryFile(input: RepositoryFileInput): string;
|
|
35
|
+
/** Files keep ordinary author fields in frontmatter; reserved keys carry the envelope. */
|
|
36
|
+
export declare function repositoryFrontmatter(data: Record<string, unknown>): {
|
|
37
|
+
fields: Record<string, unknown>;
|
|
38
|
+
metadata: unknown;
|
|
39
|
+
contract: unknown;
|
|
40
|
+
defaults: unknown;
|
|
41
|
+
identity: unknown;
|
|
42
|
+
basis: unknown;
|
|
43
|
+
};
|
|
44
|
+
export declare function contractReferenceMatches(reference: unknown, contract: ContentContract): boolean;
|
|
45
|
+
//# sourceMappingURL=repository-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository-content.d.ts","sourceRoot":"","sources":["../src/repository-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAExE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAExE,yFAAyF;AACzF,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,MAAM,GACf,kBAAkB,CAcpB;AAoBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAQvE;AAED,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IAC5B,2FAA2F;IAC3F,MAAM,EAAE,MAAM,CAAA;IACd,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,qGAAqG;IACrG,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CAC3C;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAoBjE;AAYD,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,OAAO,CAAA;CACf,CASA;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAK/F"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Document, visit } from 'yaml';
|
|
2
|
+
/** Current-content export is not a history archive or an impersonation of its author. */
|
|
3
|
+
export function repositoryDocument(document, checksum) {
|
|
4
|
+
return {
|
|
5
|
+
identity: {
|
|
6
|
+
collection: document.meta.collection,
|
|
7
|
+
slug: document.meta.slug,
|
|
8
|
+
locale: document.meta.locale,
|
|
9
|
+
},
|
|
10
|
+
contract: document.contract,
|
|
11
|
+
fields: document.fields,
|
|
12
|
+
metadata: document.metadata,
|
|
13
|
+
defaults: document.defaults,
|
|
14
|
+
body: document.body,
|
|
15
|
+
basis: { version: document.meta.version, checksum },
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** YAML frontmatter in block style, multi-line strings as block literals, `undefined` values left out */
|
|
19
|
+
function frontmatterYaml(data, parse, write) {
|
|
20
|
+
const yaml = new Document(Object.fromEntries(Object.entries(data).filter(([, value]) => value !== undefined)), parse);
|
|
21
|
+
visit(yaml, {
|
|
22
|
+
Scalar(_, node) {
|
|
23
|
+
if (typeof node.value === 'string' && node.value.includes('\n'))
|
|
24
|
+
node.type = 'BLOCK_LITERAL';
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
return yaml.toString({ defaultKeyType: 'PLAIN', lineWidth: 0, ...write });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Markdown with YAML frontmatter, readable and diffable: block style, plain
|
|
31
|
+
* keys (quoted when YAML would read them as something else), every one-line
|
|
32
|
+
* string double-quoted so a date, a number-like slug or "no" reads back as the
|
|
33
|
+
* string it was, and multi-line text as a block literal.
|
|
34
|
+
*/
|
|
35
|
+
export function repositoryMarkdown(document) {
|
|
36
|
+
const { fields, body, ...envelope } = document;
|
|
37
|
+
const frontmatter = frontmatterYaml({ ...fields, ...envelope }, {}, { defaultStringType: 'QUOTE_DOUBLE' });
|
|
38
|
+
return `---\n${frontmatter}---\n${body}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A document as a content file in a project's repository: the fields the author wrote, in the
|
|
42
|
+
* schema's order, then `status`, `locale` when it is not the default, and the metadata and
|
|
43
|
+
* defaults blocks when there are any. It leaves out what the file's place and the active contract
|
|
44
|
+
* already say (identity, contract, basis), so a diff against the file an editor changed shows
|
|
45
|
+
* the change, not a new layout. Strings are plain where YAML reads them back as strings and
|
|
46
|
+
* single-quoted where it would not, the way people write them.
|
|
47
|
+
*/
|
|
48
|
+
export function repositoryFile(input) {
|
|
49
|
+
const order = input.fieldOrder ?? [];
|
|
50
|
+
const names = [
|
|
51
|
+
...order.filter((name) => name in input.fields),
|
|
52
|
+
...Object.keys(input.fields).filter((name) => !order.includes(name)),
|
|
53
|
+
];
|
|
54
|
+
const data = Object.fromEntries(names.map((name) => [name, input.fields[name]]));
|
|
55
|
+
data['status'] = input.status;
|
|
56
|
+
if (input.locale !== undefined)
|
|
57
|
+
data['locale'] = input.locale;
|
|
58
|
+
if (Object.keys(input.metadata).length > 0)
|
|
59
|
+
data['metadata'] = input.metadata;
|
|
60
|
+
if (input.defaults.length > 0)
|
|
61
|
+
data['defaults'] = input.defaults;
|
|
62
|
+
// YAML 1.1 rules: gray-matter reads files with them, so a date or `yes` is quoted to stay a string
|
|
63
|
+
const frontmatter = frontmatterYaml(data, { version: '1.1' }, { defaultStringType: 'PLAIN', singleQuote: true });
|
|
64
|
+
return `---\n${frontmatter}---\n${input.body}`;
|
|
65
|
+
}
|
|
66
|
+
const ENVELOPE_KEYS = new Set([
|
|
67
|
+
'identity',
|
|
68
|
+
'contract',
|
|
69
|
+
'metadata',
|
|
70
|
+
'defaults',
|
|
71
|
+
'basis',
|
|
72
|
+
'locale',
|
|
73
|
+
'status',
|
|
74
|
+
]);
|
|
75
|
+
/** Files keep ordinary author fields in frontmatter; reserved keys carry the envelope. */
|
|
76
|
+
export function repositoryFrontmatter(data) {
|
|
77
|
+
return {
|
|
78
|
+
fields: Object.fromEntries(Object.entries(data).filter(([key]) => !ENVELOPE_KEYS.has(key))),
|
|
79
|
+
metadata: data['metadata'] === undefined ? {} : data['metadata'],
|
|
80
|
+
contract: data['contract'],
|
|
81
|
+
defaults: data['defaults'] ?? [],
|
|
82
|
+
identity: data['identity'],
|
|
83
|
+
basis: data['basis'],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export function contractReferenceMatches(reference, contract) {
|
|
87
|
+
if (reference === undefined || reference === null)
|
|
88
|
+
return true;
|
|
89
|
+
if (typeof reference !== 'object' || Array.isArray(reference))
|
|
90
|
+
return false;
|
|
91
|
+
const ref = reference;
|
|
92
|
+
return ref['id'] === contract.id && ref['revision'] === contract.revision;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=repository-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository-content.js","sourceRoot":"","sources":["../src/repository-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ORDER BY whitelist shared by every SQL adapter.
|
|
3
|
+
*
|
|
4
|
+
* A sort field arrives from a query string, so it can never be interpolated
|
|
5
|
+
* into SQL unchecked. Columns named here are emitted literally; anything else
|
|
6
|
+
* falls back to each adapter's JSON extraction expression, and only when it
|
|
7
|
+
* matches {@link SORT_FIELD_PATTERN}. Both adapters read these values from here
|
|
8
|
+
* so the two whitelists cannot drift apart.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DIRECT_SORT_COLUMNS: readonly ["slug", "status", "locale", "created_at"];
|
|
11
|
+
/** A column of the content table that may be sorted on directly */
|
|
12
|
+
export type DirectSortColumn = (typeof DIRECT_SORT_COLUMNS)[number];
|
|
13
|
+
/** Characters allowed in a content field name used as a sort key */
|
|
14
|
+
export declare const SORT_FIELD_PATTERN: RegExp;
|
|
15
|
+
/** Whether a sort field names a content table column that can be sorted directly */
|
|
16
|
+
export declare function isDirectSortColumn(field: string): field is DirectSortColumn;
|
|
17
|
+
/** Whether a sort field is safe to interpolate into a JSON extraction expression */
|
|
18
|
+
export declare function isSafeSortField(field: string): boolean;
|
|
19
|
+
//# sourceMappingURL=sort-columns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort-columns.d.ts","sourceRoot":"","sources":["../src/sort-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,qDAAsD,CAAA;AAEtF,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,QAAa,CAAA;AAE5C,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,gBAAgB,CAE3E;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD"}
|