@usecds/server 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/core/content-report.d.ts +37 -0
- package/dist/core/content-report.js +306 -0
- package/dist/core/content-report.js.map +1 -0
- package/dist/core/content-types.d.ts +40 -0
- package/dist/core/content-types.js +27 -0
- package/dist/core/content-types.js.map +1 -0
- package/dist/core/jsonld.d.ts +33 -0
- package/dist/core/jsonld.js +94 -0
- package/dist/core/jsonld.js.map +1 -0
- package/dist/core/media-metadata.d.ts +20 -0
- package/dist/core/media-metadata.js +53 -0
- package/dist/core/media-metadata.js.map +1 -0
- package/dist/core/media-names.d.ts +9 -0
- package/dist/core/media-names.js +58 -0
- package/dist/core/media-names.js.map +1 -0
- package/dist/core/menu.d.ts +33 -0
- package/dist/core/menu.js +89 -0
- package/dist/core/menu.js.map +1 -0
- package/dist/core/publisher.d.ts +77 -0
- package/dist/core/publisher.js +261 -0
- package/dist/core/publisher.js.map +1 -0
- package/dist/core/report.d.ts +54 -0
- package/dist/core/report.js +132 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/site.d.ts +37 -0
- package/dist/core/site.js +96 -0
- package/dist/core/site.js.map +1 -0
- package/dist/core/storage-report.d.ts +44 -0
- package/dist/core/storage-report.js +112 -0
- package/dist/core/storage-report.js.map +1 -0
- package/dist/core/targets.d.ts +63 -0
- package/dist/core/targets.js +213 -0
- package/dist/core/targets.js.map +1 -0
- package/dist/core/translations.d.ts +41 -0
- package/dist/core/translations.js +125 -0
- package/dist/core/translations.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/sources/fixture.d.ts +9 -0
- package/dist/sources/fixture.js +79 -0
- package/dist/sources/fixture.js.map +1 -0
- package/dist/storage/filesystem.d.ts +23 -0
- package/dist/storage/filesystem.js +125 -0
- package/dist/storage/filesystem.js.map +1 -0
- package/dist/types.d.ts +223 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +17 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation.d.ts +21 -0
- package/dist/validation.js +100 -0
- package/dist/validation.js.map +1 -0
- package/package.json +28 -3
- package/schemas/v1/block.json +31 -0
- package/schemas/v1/channel-manifest.json +23 -0
- package/schemas/v1/collection.json +89 -0
- package/schemas/v1/jsonld.json +19 -0
- package/schemas/v1/media-metadata.json +41 -0
- package/schemas/v1/menu.json +21 -0
- package/schemas/v1/page.json +12 -0
- package/schemas/v1/release-manifest.json +82 -0
- package/schemas/v1/route.json +24 -0
- package/schemas/v1/target.json +98 -0
- package/README.md +0 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./utils.js";
|
|
3
|
+
export * from "./validation.js";
|
|
4
|
+
export { Publisher, PublishRequirementsError } from "./core/publisher.js";
|
|
5
|
+
export * from "./core/storage-report.js";
|
|
6
|
+
export * from "./core/translations.js";
|
|
7
|
+
export * from "./core/targets.js";
|
|
8
|
+
export * from "./core/content-report.js";
|
|
9
|
+
export * from "./core/media-metadata.js";
|
|
10
|
+
export * from "./core/media-names.js";
|
|
11
|
+
export * from "./core/jsonld.js";
|
|
12
|
+
export * from "./core/site.js";
|
|
13
|
+
export * from "./core/menu.js";
|
|
14
|
+
export * from "./core/report.js";
|
|
15
|
+
export * from "./core/content-types.js";
|
|
16
|
+
export { FixtureSource } from "./sources/fixture.js";
|
|
17
|
+
export { FilesystemStore } from "./storage/filesystem.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,SAAS,EAKT,wBAAwB,EAGzB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ContentSource, CollectionItem, SourceMedia, SourceMap } from "../types.js";
|
|
2
|
+
export declare class FixtureSource implements ContentSource {
|
|
3
|
+
private collections;
|
|
4
|
+
private media;
|
|
5
|
+
getSourceMap?: () => Promise<SourceMap>;
|
|
6
|
+
constructor(collections?: Record<string, CollectionItem[]>, media?: SourceMedia[], sourceMap?: SourceMap);
|
|
7
|
+
getCollections(): Promise<Record<string, CollectionItem[]>>;
|
|
8
|
+
getMedia(): Promise<SourceMedia[]>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export class FixtureSource {
|
|
2
|
+
collections;
|
|
3
|
+
media;
|
|
4
|
+
getSourceMap;
|
|
5
|
+
constructor(collections, media, sourceMap) {
|
|
6
|
+
if (sourceMap) {
|
|
7
|
+
this.getSourceMap = async () => sourceMap;
|
|
8
|
+
}
|
|
9
|
+
this.collections = collections || {
|
|
10
|
+
categories: [
|
|
11
|
+
{
|
|
12
|
+
id: "cat_apparel",
|
|
13
|
+
key: "apparel",
|
|
14
|
+
translations: {
|
|
15
|
+
en: { name: "Apparel" },
|
|
16
|
+
de: { name: "Kleidung" }
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "cat_electronics",
|
|
21
|
+
key: "electronics",
|
|
22
|
+
translations: {
|
|
23
|
+
en: { name: "Electronics" },
|
|
24
|
+
de: { name: "Elektronik" }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
products: [
|
|
29
|
+
{
|
|
30
|
+
id: "prod_tshirt",
|
|
31
|
+
key: "classic-tshirt",
|
|
32
|
+
translations: {
|
|
33
|
+
en: { title: "Classic T-Shirt", description: "An everyday essential." },
|
|
34
|
+
de: { title: "Klassisches T-Shirt", description: "Ein Alltags-Klassiker." }
|
|
35
|
+
},
|
|
36
|
+
references: [
|
|
37
|
+
{ collection: "categories", id: "cat_apparel" }
|
|
38
|
+
],
|
|
39
|
+
media: [
|
|
40
|
+
"products/tshirt.png"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "prod_phone",
|
|
45
|
+
key: "smart-phone",
|
|
46
|
+
translations: {
|
|
47
|
+
en: { title: "Smart Phone", description: "Stay connected." },
|
|
48
|
+
de: { title: "Smartphone", description: "Immer in Verbindung." }
|
|
49
|
+
},
|
|
50
|
+
references: [
|
|
51
|
+
{ collection: "categories", id: "cat_electronics" }
|
|
52
|
+
],
|
|
53
|
+
media: [
|
|
54
|
+
"products/phone.png"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
this.media = media || [
|
|
60
|
+
{
|
|
61
|
+
virtualPath: "products/tshirt.png",
|
|
62
|
+
content: Buffer.from("fakedata-tshirt-png-image-content-here"),
|
|
63
|
+
mimeType: "image/png"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
virtualPath: "products/phone.png",
|
|
67
|
+
content: Buffer.from("fakedata-phone-png-image-content-here"),
|
|
68
|
+
mimeType: "image/png"
|
|
69
|
+
}
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
async getCollections() {
|
|
73
|
+
return this.collections;
|
|
74
|
+
}
|
|
75
|
+
async getMedia() {
|
|
76
|
+
return this.media;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=fixture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../src/sources/fixture.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,aAAa;IAChB,WAAW,CAAmC;IAC9C,KAAK,CAAgB;IAC7B,YAAY,CAA4B;IAExC,YACE,WAA8C,EAC9C,KAAqB,EACrB,SAAqB;QAErB,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,YAAY,GAAG,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,WAAW,IAAI;YAChC,UAAU,EAAE;gBACV;oBACE,EAAE,EAAE,aAAa;oBACjB,GAAG,EAAE,SAAS;oBACd,YAAY,EAAE;wBACZ,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;wBACvB,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;qBACzB;iBACF;gBACD;oBACE,EAAE,EAAE,iBAAiB;oBACrB,GAAG,EAAE,aAAa;oBAClB,YAAY,EAAE;wBACZ,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;wBAC3B,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;qBAC3B;iBACF;aACF;YACD,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,aAAa;oBACjB,GAAG,EAAE,gBAAgB;oBACrB,YAAY,EAAE;wBACZ,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,wBAAwB,EAAE;wBACvE,EAAE,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,wBAAwB,EAAE;qBAC5E;oBACD,UAAU,EAAE;wBACV,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE;qBAChD;oBACD,KAAK,EAAE;wBACL,qBAAqB;qBACtB;iBACF;gBACD;oBACE,EAAE,EAAE,YAAY;oBAChB,GAAG,EAAE,aAAa;oBAClB,YAAY,EAAE;wBACZ,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE;wBAC5D,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE;qBACjE;oBACD,UAAU,EAAE;wBACV,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,EAAE,iBAAiB,EAAE;qBACpD;oBACD,KAAK,EAAE;wBACL,oBAAoB;qBACrB;iBACF;aACF;SACF,CAAC;QAEF,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI;YACpB;gBACE,WAAW,EAAE,qBAAqB;gBAClC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC;gBAC9D,QAAQ,EAAE,WAAW;aACtB;YACD;gBACE,WAAW,EAAE,oBAAoB;gBACjC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC;gBAC7D,QAAQ,EAAE,WAAW;aACtB;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ObjectStore, ReleaseManifest, ChannelManifest } from "../types.js";
|
|
2
|
+
export declare class FilesystemStore implements ObjectStore {
|
|
3
|
+
private baseDir;
|
|
4
|
+
constructor(baseDir: string);
|
|
5
|
+
private ensureDir;
|
|
6
|
+
writeObject(hash: string, content: string): Promise<void>;
|
|
7
|
+
writeMedia(hash: string, ext: string, content: Buffer): Promise<void>;
|
|
8
|
+
writeRelease(releaseId: string, manifest: ReleaseManifest): Promise<void>;
|
|
9
|
+
writeChannelManifest(channel: string, manifest: ChannelManifest): Promise<void>;
|
|
10
|
+
listChannels(): Promise<string[]>;
|
|
11
|
+
readChannelManifest(channel: string): Promise<ChannelManifest | null>;
|
|
12
|
+
readRelease(releaseId: string): Promise<ReleaseManifest | null>;
|
|
13
|
+
listReleases(): Promise<string[]>;
|
|
14
|
+
deleteRelease(releaseId: string): Promise<void>;
|
|
15
|
+
readObject(hash: string): Promise<string | null>;
|
|
16
|
+
listObjects(): Promise<string[]>;
|
|
17
|
+
deleteObject(hash: string): Promise<void>;
|
|
18
|
+
objectSize(hash: string): Promise<number | null>;
|
|
19
|
+
listMedia(): Promise<string[]>;
|
|
20
|
+
deleteMedia(filename: string): Promise<void>;
|
|
21
|
+
mediaSize(filename: string): Promise<number | null>;
|
|
22
|
+
private fileSize;
|
|
23
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
export class FilesystemStore {
|
|
5
|
+
baseDir;
|
|
6
|
+
constructor(baseDir) {
|
|
7
|
+
this.baseDir = path.resolve(baseDir);
|
|
8
|
+
}
|
|
9
|
+
async ensureDir(dirPath) {
|
|
10
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
async writeObject(hash, content) {
|
|
13
|
+
const filePath = path.join(this.baseDir, "objects", `${hash}.json`);
|
|
14
|
+
await this.ensureDir(path.dirname(filePath));
|
|
15
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
16
|
+
}
|
|
17
|
+
async writeMedia(hash, ext, content) {
|
|
18
|
+
const extension = ext.startsWith(".") ? ext : `.${ext}`;
|
|
19
|
+
const filePath = path.join(this.baseDir, "media", `${hash}${extension}`);
|
|
20
|
+
await this.ensureDir(path.dirname(filePath));
|
|
21
|
+
await fs.writeFile(filePath, content);
|
|
22
|
+
}
|
|
23
|
+
async writeRelease(releaseId, manifest) {
|
|
24
|
+
// Escape filename just in case, though releaseId should be alphanumeric/dashes
|
|
25
|
+
const safeReleaseId = releaseId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
26
|
+
const filePath = path.join(this.baseDir, "releases", `${safeReleaseId}.json`);
|
|
27
|
+
await this.ensureDir(path.dirname(filePath));
|
|
28
|
+
await fs.writeFile(filePath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
29
|
+
}
|
|
30
|
+
async writeChannelManifest(channel, manifest) {
|
|
31
|
+
const safeChannel = channel.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
32
|
+
const filePath = path.join(this.baseDir, "channels", safeChannel, "manifest.json");
|
|
33
|
+
await this.ensureDir(path.dirname(filePath));
|
|
34
|
+
await fs.writeFile(filePath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
35
|
+
}
|
|
36
|
+
async listChannels() {
|
|
37
|
+
const dir = path.join(this.baseDir, "channels");
|
|
38
|
+
if (!existsSync(dir))
|
|
39
|
+
return [];
|
|
40
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
41
|
+
return entries
|
|
42
|
+
.filter((e) => e.isDirectory() && existsSync(path.join(dir, e.name, "manifest.json")))
|
|
43
|
+
.map((e) => e.name);
|
|
44
|
+
}
|
|
45
|
+
async readChannelManifest(channel) {
|
|
46
|
+
const safeChannel = channel.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
47
|
+
const filePath = path.join(this.baseDir, "channels", safeChannel, "manifest.json");
|
|
48
|
+
if (!existsSync(filePath)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
52
|
+
return JSON.parse(content);
|
|
53
|
+
}
|
|
54
|
+
async readRelease(releaseId) {
|
|
55
|
+
const safeReleaseId = releaseId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
56
|
+
const filePath = path.join(this.baseDir, "releases", `${safeReleaseId}.json`);
|
|
57
|
+
if (!existsSync(filePath)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
61
|
+
return JSON.parse(content);
|
|
62
|
+
}
|
|
63
|
+
async listReleases() {
|
|
64
|
+
const dir = path.join(this.baseDir, "releases");
|
|
65
|
+
if (!existsSync(dir))
|
|
66
|
+
return [];
|
|
67
|
+
const files = await fs.readdir(dir);
|
|
68
|
+
return files
|
|
69
|
+
.filter((f) => f.endsWith(".json"))
|
|
70
|
+
.map((f) => f.slice(0, -5)); // Remove .json extension
|
|
71
|
+
}
|
|
72
|
+
async deleteRelease(releaseId) {
|
|
73
|
+
const safeReleaseId = releaseId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
74
|
+
const filePath = path.join(this.baseDir, "releases", `${safeReleaseId}.json`);
|
|
75
|
+
if (existsSync(filePath)) {
|
|
76
|
+
await fs.unlink(filePath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async readObject(hash) {
|
|
80
|
+
const filePath = path.join(this.baseDir, "objects", `${hash}.json`);
|
|
81
|
+
if (!existsSync(filePath)) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return await fs.readFile(filePath, "utf-8");
|
|
85
|
+
}
|
|
86
|
+
async listObjects() {
|
|
87
|
+
const dir = path.join(this.baseDir, "objects");
|
|
88
|
+
if (!existsSync(dir))
|
|
89
|
+
return [];
|
|
90
|
+
const files = await fs.readdir(dir);
|
|
91
|
+
return files
|
|
92
|
+
.filter((f) => f.endsWith(".json"))
|
|
93
|
+
.map((f) => f.slice(0, -5)); // Return list of hashes
|
|
94
|
+
}
|
|
95
|
+
async deleteObject(hash) {
|
|
96
|
+
const filePath = path.join(this.baseDir, "objects", `${hash}.json`);
|
|
97
|
+
if (existsSync(filePath)) {
|
|
98
|
+
await fs.unlink(filePath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async objectSize(hash) {
|
|
102
|
+
return this.fileSize(path.join(this.baseDir, "objects", `${hash}.json`));
|
|
103
|
+
}
|
|
104
|
+
async listMedia() {
|
|
105
|
+
const dir = path.join(this.baseDir, "media");
|
|
106
|
+
if (!existsSync(dir))
|
|
107
|
+
return [];
|
|
108
|
+
return await fs.readdir(dir);
|
|
109
|
+
}
|
|
110
|
+
async deleteMedia(filename) {
|
|
111
|
+
const filePath = path.join(this.baseDir, "media", filename);
|
|
112
|
+
if (existsSync(filePath)) {
|
|
113
|
+
await fs.unlink(filePath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async mediaSize(filename) {
|
|
117
|
+
return this.fileSize(path.join(this.baseDir, "media", filename));
|
|
118
|
+
}
|
|
119
|
+
async fileSize(filePath) {
|
|
120
|
+
if (!existsSync(filePath))
|
|
121
|
+
return null;
|
|
122
|
+
return (await fs.stat(filePath)).size;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=filesystem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/storage/filesystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,OAAO,eAAe;IAClB,OAAO,CAAS;IAExB,YAAY,OAAe;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,OAAe;QACrC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAY,EAAE,OAAe;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;QACpE,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,GAAW,EAAE,OAAe;QACzD,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,CAAC;QACzE,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB,EAAE,QAAyB;QAC7D,+EAA+E;QAC/E,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,aAAa,OAAO,CAAC,CAAC;QAC9E,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,OAAe,EAAE,QAAyB;QACnE,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;QACnF,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,OAAO,OAAO;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;aACrF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAAe;QACvC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;QACnF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB;QACjC,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,aAAa,OAAO,CAAC,CAAC;QAC9E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aAC1C,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,yBAAyB;IAClE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,aAAa,OAAO,CAAC,CAAC;QAC9E,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;QACpE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aAC1C,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;IACjE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,IAAY;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;QACpE,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QAChC,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAgB;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5D,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnE,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,QAAgB;QACrC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
export interface ChannelManifest {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
channel: string;
|
|
4
|
+
releaseId: string;
|
|
5
|
+
updatedAt: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Reference {
|
|
8
|
+
collection: string;
|
|
9
|
+
id: string;
|
|
10
|
+
}
|
|
11
|
+
export type ProvenanceStatus = "original" | "human" | "machine" | "reviewed";
|
|
12
|
+
export interface ProvenanceMarker {
|
|
13
|
+
status: ProvenanceStatus;
|
|
14
|
+
from?: string;
|
|
15
|
+
sourceHash?: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Provenance {
|
|
19
|
+
translations?: Record<string, Record<string, ProvenanceMarker>>;
|
|
20
|
+
fields?: Record<string, ProvenanceMarker>;
|
|
21
|
+
}
|
|
22
|
+
export interface CollectionItem {
|
|
23
|
+
id: string;
|
|
24
|
+
key: string;
|
|
25
|
+
translations: Record<string, Record<string, any>>;
|
|
26
|
+
references?: Reference[];
|
|
27
|
+
media?: string[];
|
|
28
|
+
_provenance?: Provenance;
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}
|
|
31
|
+
export interface MediaMetadataItem extends CollectionItem {
|
|
32
|
+
name?: string;
|
|
33
|
+
translations: Record<string, {
|
|
34
|
+
alt?: string | null;
|
|
35
|
+
description?: string | null;
|
|
36
|
+
[field: string]: any;
|
|
37
|
+
}>;
|
|
38
|
+
focalPoint?: {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
};
|
|
42
|
+
focalPoints?: Record<string, {
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
}>;
|
|
46
|
+
width?: number;
|
|
47
|
+
height?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface Collection {
|
|
50
|
+
schemaVersion: 1;
|
|
51
|
+
collection: string;
|
|
52
|
+
items: CollectionItem[];
|
|
53
|
+
}
|
|
54
|
+
export interface CollectionMeta {
|
|
55
|
+
hash: string;
|
|
56
|
+
itemCount: number;
|
|
57
|
+
size: number;
|
|
58
|
+
}
|
|
59
|
+
export interface MediaMeta {
|
|
60
|
+
hash: string;
|
|
61
|
+
size: number;
|
|
62
|
+
mimeType: string;
|
|
63
|
+
}
|
|
64
|
+
export interface TranslationCounts {
|
|
65
|
+
expected: number;
|
|
66
|
+
translated: number;
|
|
67
|
+
missing: number;
|
|
68
|
+
stale: number;
|
|
69
|
+
machine: number;
|
|
70
|
+
}
|
|
71
|
+
export interface TranslationSummary {
|
|
72
|
+
sourceLocale: string;
|
|
73
|
+
locales: Record<string, TranslationCounts>;
|
|
74
|
+
overall: TranslationCounts;
|
|
75
|
+
}
|
|
76
|
+
export interface ReleaseManifest {
|
|
77
|
+
schemaVersion: 1;
|
|
78
|
+
releaseId: string;
|
|
79
|
+
createdAt: string;
|
|
80
|
+
sourceReadAt?: string;
|
|
81
|
+
collections: Record<string, CollectionMeta>;
|
|
82
|
+
media: Record<string, MediaMeta>;
|
|
83
|
+
translations?: TranslationSummary;
|
|
84
|
+
targets?: string[];
|
|
85
|
+
}
|
|
86
|
+
export interface SourceMedia {
|
|
87
|
+
virtualPath: string;
|
|
88
|
+
content: Buffer;
|
|
89
|
+
mimeType: string;
|
|
90
|
+
}
|
|
91
|
+
export interface SourceItemRef {
|
|
92
|
+
id: string;
|
|
93
|
+
collection?: string;
|
|
94
|
+
path?: string;
|
|
95
|
+
fields?: Record<string, SourceFieldRef>;
|
|
96
|
+
}
|
|
97
|
+
export interface SourceFieldRef {
|
|
98
|
+
collection: string;
|
|
99
|
+
id: string;
|
|
100
|
+
field: string;
|
|
101
|
+
format?: "plain" | "html";
|
|
102
|
+
editable?: boolean;
|
|
103
|
+
}
|
|
104
|
+
export type EditValue = string | number | boolean | null;
|
|
105
|
+
export interface SourceEdit {
|
|
106
|
+
ref: SourceFieldRef;
|
|
107
|
+
value: EditValue;
|
|
108
|
+
basedOn: EditValue;
|
|
109
|
+
}
|
|
110
|
+
export type EditResult = {
|
|
111
|
+
status: "saved";
|
|
112
|
+
value: EditValue;
|
|
113
|
+
} | {
|
|
114
|
+
status: "conflict";
|
|
115
|
+
current: EditValue;
|
|
116
|
+
} | {
|
|
117
|
+
status: "rejected";
|
|
118
|
+
message: string;
|
|
119
|
+
code?: number;
|
|
120
|
+
};
|
|
121
|
+
export interface EditorSession {
|
|
122
|
+
accessToken: string;
|
|
123
|
+
refreshToken?: string;
|
|
124
|
+
expiresAt?: number;
|
|
125
|
+
user?: {
|
|
126
|
+
id: string;
|
|
127
|
+
name?: string;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The optional write side of a source adapter. Edits go to the source system, never into a release:
|
|
132
|
+
* the next publish includes them. Adapters without it simply offer no editing.
|
|
133
|
+
*/
|
|
134
|
+
export interface SourceEditor {
|
|
135
|
+
login(credentials: {
|
|
136
|
+
email: string;
|
|
137
|
+
password: string;
|
|
138
|
+
}): Promise<EditorSession>;
|
|
139
|
+
refresh?(session: EditorSession): Promise<EditorSession>;
|
|
140
|
+
logout?(session: EditorSession): Promise<void>;
|
|
141
|
+
read(ref: SourceFieldRef, session: EditorSession): Promise<EditValue>;
|
|
142
|
+
write(edit: SourceEdit, session: EditorSession): Promise<EditResult>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Changes to the site structure in CDS terms (pages, routes, blocks, menus), for previews. Optional,
|
|
146
|
+
* like all editing. The source system's shape of these is site-specific, so this is implemented next
|
|
147
|
+
* to the site's mapping (the mapping's inverse), on top of the adapter's editor. Ids are CDS ids.
|
|
148
|
+
*/
|
|
149
|
+
export interface StructureEditor {
|
|
150
|
+
/** Block types that can be added, e.g. ["hero", "text"] */
|
|
151
|
+
blockTypes(): string[];
|
|
152
|
+
createPage(input: {
|
|
153
|
+
title: string;
|
|
154
|
+
slug: string;
|
|
155
|
+
status?: string;
|
|
156
|
+
}, session: EditorSession): Promise<{
|
|
157
|
+
page: string;
|
|
158
|
+
}>;
|
|
159
|
+
/** Adds a block after another (null: at the top, undefined: at the end) */
|
|
160
|
+
addBlock(input: {
|
|
161
|
+
page: string;
|
|
162
|
+
type: string;
|
|
163
|
+
after?: string | null;
|
|
164
|
+
texts?: Record<string, string>;
|
|
165
|
+
}, session: EditorSession): Promise<{
|
|
166
|
+
block: string;
|
|
167
|
+
}>;
|
|
168
|
+
/** Puts the page's published blocks in this order; content the release doesn't show keeps its place */
|
|
169
|
+
reorderBlocks(input: {
|
|
170
|
+
page: string;
|
|
171
|
+
blocks: string[];
|
|
172
|
+
}, session: EditorSession): Promise<void>;
|
|
173
|
+
/** A menu entry linking a page (canonical: the page's route) or a URL, under a menu or another entry */
|
|
174
|
+
addMenuEntry(input: {
|
|
175
|
+
parent: string;
|
|
176
|
+
label: string;
|
|
177
|
+
page?: string;
|
|
178
|
+
url?: string;
|
|
179
|
+
canonical?: boolean;
|
|
180
|
+
}, session: EditorSession): Promise<{
|
|
181
|
+
entry: string;
|
|
182
|
+
}>;
|
|
183
|
+
}
|
|
184
|
+
export interface SourceMediaRef extends SourceItemRef {
|
|
185
|
+
adapter: string;
|
|
186
|
+
}
|
|
187
|
+
export interface SourceMap {
|
|
188
|
+
sources: Record<string, {
|
|
189
|
+
baseUrl?: string;
|
|
190
|
+
}>;
|
|
191
|
+
collections: Record<string, {
|
|
192
|
+
source?: {
|
|
193
|
+
adapter: string;
|
|
194
|
+
collection: string;
|
|
195
|
+
};
|
|
196
|
+
items: Record<string, SourceItemRef>;
|
|
197
|
+
}>;
|
|
198
|
+
media: Record<string, SourceMediaRef>;
|
|
199
|
+
}
|
|
200
|
+
export interface ContentSource {
|
|
201
|
+
getCollections(): Promise<Record<string, CollectionItem[]>>;
|
|
202
|
+
getMedia(): Promise<SourceMedia[]>;
|
|
203
|
+
getSourceMap?(): Promise<SourceMap>;
|
|
204
|
+
getSourceLocale?(): Promise<string | undefined>;
|
|
205
|
+
}
|
|
206
|
+
export interface ObjectStore {
|
|
207
|
+
writeObject(hash: string, content: string): Promise<void>;
|
|
208
|
+
writeMedia(hash: string, ext: string, content: Buffer): Promise<void>;
|
|
209
|
+
writeRelease(releaseId: string, manifest: ReleaseManifest): Promise<void>;
|
|
210
|
+
writeChannelManifest(channel: string, manifest: ChannelManifest): Promise<void>;
|
|
211
|
+
listChannels(): Promise<string[]>;
|
|
212
|
+
readChannelManifest(channel: string): Promise<ChannelManifest | null>;
|
|
213
|
+
readRelease(releaseId: string): Promise<ReleaseManifest | null>;
|
|
214
|
+
listReleases(): Promise<string[]>;
|
|
215
|
+
deleteRelease(releaseId: string): Promise<void>;
|
|
216
|
+
readObject(hash: string): Promise<string | null>;
|
|
217
|
+
listObjects(): Promise<string[]>;
|
|
218
|
+
deleteObject(hash: string): Promise<void>;
|
|
219
|
+
objectSize(hash: string): Promise<number | null>;
|
|
220
|
+
listMedia(): Promise<string[]>;
|
|
221
|
+
deleteMedia(filename: string): Promise<void>;
|
|
222
|
+
mediaSize(filename: string): Promise<number | null>;
|
|
223
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
export function deterministicStringify(val) {
|
|
3
|
+
if (val === null)
|
|
4
|
+
return "null";
|
|
5
|
+
if (Array.isArray(val)) {
|
|
6
|
+
return "[" + val.map(v => deterministicStringify(v)).join(",") + "]";
|
|
7
|
+
}
|
|
8
|
+
if (typeof val === "object") {
|
|
9
|
+
const keys = Object.keys(val).sort();
|
|
10
|
+
return "{" + keys.map(k => JSON.stringify(k) + ":" + deterministicStringify(val[k])).join(",") + "}";
|
|
11
|
+
}
|
|
12
|
+
return JSON.stringify(val);
|
|
13
|
+
}
|
|
14
|
+
export function sha256(content) {
|
|
15
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,UAAU,sBAAsB,CAAC,GAAQ;IAC7C,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACvE,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACvG,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAwB;IAC7C,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare function validateChannelManifest(data: any): void;
|
|
2
|
+
export declare function validateReleaseManifest(data: any): void;
|
|
3
|
+
export declare function validateCollection(data: any): void;
|
|
4
|
+
export declare function validateTargetDefinition(data: any): void;
|
|
5
|
+
export declare function validateMediaMetadataItem(data: any): void;
|
|
6
|
+
export declare function validateJsonLdDefinition(data: any): void;
|
|
7
|
+
export declare const validateRouteItem: (data: any) => void;
|
|
8
|
+
export declare const validatePageItem: (data: any) => void;
|
|
9
|
+
export declare const validateBlockItem: (data: any) => void;
|
|
10
|
+
export declare const validateMenuItem: (data: any) => void;
|
|
11
|
+
export interface ContentSchemaError {
|
|
12
|
+
keyword: string;
|
|
13
|
+
instancePath: string;
|
|
14
|
+
params: Record<string, any>;
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Compiles a JSON Schema declared in a target definition (requirements/recommendations).
|
|
19
|
+
* The returned function lists all violations; an empty list means valid.
|
|
20
|
+
*/
|
|
21
|
+
export declare function compileContentSchema(schema: object): (data: any) => ContentSchemaError[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import AjvModule from "ajv";
|
|
2
|
+
import addFormatsModule from "ajv-formats";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
const Ajv = AjvModule.default || AjvModule;
|
|
7
|
+
const addFormats = addFormatsModule.default || addFormatsModule;
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
// Helper to load schema files from the monorepo root
|
|
11
|
+
function loadSchema(filename) {
|
|
12
|
+
// We look for schemas first in ../../schemas/v1/ (development/test run)
|
|
13
|
+
// or inside a relative path depending on build outputs.
|
|
14
|
+
const possiblePaths = [
|
|
15
|
+
path.join(__dirname, "..", "..", "schemas", "v1", filename),
|
|
16
|
+
path.join(__dirname, "..", "schemas", "v1", filename),
|
|
17
|
+
path.join(__dirname, "schemas", "v1", filename)
|
|
18
|
+
];
|
|
19
|
+
for (const p of possiblePaths) {
|
|
20
|
+
if (fs.existsSync(p)) {
|
|
21
|
+
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Schema file not found: ${filename}`);
|
|
25
|
+
}
|
|
26
|
+
const channelManifestSchema = loadSchema("channel-manifest.json");
|
|
27
|
+
const releaseManifestSchema = loadSchema("release-manifest.json");
|
|
28
|
+
const collectionSchema = loadSchema("collection.json");
|
|
29
|
+
const targetSchema = loadSchema("target.json");
|
|
30
|
+
const mediaMetadataSchema = loadSchema("media-metadata.json");
|
|
31
|
+
const jsonLdSchema = loadSchema("jsonld.json");
|
|
32
|
+
const ajv = new Ajv({ allErrors: true });
|
|
33
|
+
addFormats(ajv);
|
|
34
|
+
const validateChannelManifestFn = ajv.compile(channelManifestSchema);
|
|
35
|
+
const validateReleaseManifestFn = ajv.compile(releaseManifestSchema);
|
|
36
|
+
const validateCollectionFn = ajv.compile(collectionSchema);
|
|
37
|
+
const validateTargetFn = ajv.compile(targetSchema);
|
|
38
|
+
const validateMediaMetadataFn = ajv.compile(mediaMetadataSchema);
|
|
39
|
+
const validateJsonLdFn = ajv.compile(jsonLdSchema);
|
|
40
|
+
// Target-provided schemas are often partial (e.g. only "required"), so strict-mode type hints are off
|
|
41
|
+
const contentAjv = new Ajv({ allErrors: true, strict: false });
|
|
42
|
+
addFormats(contentAjv);
|
|
43
|
+
export function validateChannelManifest(data) {
|
|
44
|
+
const valid = validateChannelManifestFn(data);
|
|
45
|
+
if (!valid) {
|
|
46
|
+
throw new Error(`Invalid Channel Manifest: ${ajv.errorsText(validateChannelManifestFn.errors)}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function validateReleaseManifest(data) {
|
|
50
|
+
const valid = validateReleaseManifestFn(data);
|
|
51
|
+
if (!valid) {
|
|
52
|
+
throw new Error(`Invalid Release Manifest: ${ajv.errorsText(validateReleaseManifestFn.errors)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function validateCollection(data) {
|
|
56
|
+
const valid = validateCollectionFn(data);
|
|
57
|
+
if (!valid) {
|
|
58
|
+
throw new Error(`Invalid Collection (${data.collection || "unknown"}): ${ajv.errorsText(validateCollectionFn.errors)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function validateTargetDefinition(data) {
|
|
62
|
+
const valid = validateTargetFn(data);
|
|
63
|
+
if (!valid) {
|
|
64
|
+
throw new Error(`Invalid Target Definition (${data?.id || "unknown"}): ${ajv.errorsText(validateTargetFn.errors)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function validateMediaMetadataItem(data) {
|
|
68
|
+
const valid = validateMediaMetadataFn(data);
|
|
69
|
+
if (!valid) {
|
|
70
|
+
throw new Error(`Invalid _media item (${data?.id || "unknown"}): ${ajv.errorsText(validateMediaMetadataFn.errors)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function validateJsonLdDefinition(data) {
|
|
74
|
+
const valid = validateJsonLdFn(data);
|
|
75
|
+
if (!valid) {
|
|
76
|
+
throw new Error(`Invalid _jsonld item (${data?.id || "unknown"}): ${ajv.errorsText(validateJsonLdFn.errors)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Validators for items of the optional site-structure collections
|
|
80
|
+
function itemValidator(schemaFile, label) {
|
|
81
|
+
const validate = ajv.compile(loadSchema(schemaFile));
|
|
82
|
+
return (data) => {
|
|
83
|
+
if (!validate(data)) {
|
|
84
|
+
throw new Error(`Invalid ${label} item (${data?.id || "unknown"}): ${ajv.errorsText(validate.errors)}`);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export const validateRouteItem = itemValidator("route.json", "_routes");
|
|
89
|
+
export const validatePageItem = itemValidator("page.json", "_pages");
|
|
90
|
+
export const validateBlockItem = itemValidator("block.json", "_blocks");
|
|
91
|
+
export const validateMenuItem = itemValidator("menu.json", "_menu");
|
|
92
|
+
/**
|
|
93
|
+
* Compiles a JSON Schema declared in a target definition (requirements/recommendations).
|
|
94
|
+
* The returned function lists all violations; an empty list means valid.
|
|
95
|
+
*/
|
|
96
|
+
export function compileContentSchema(schema) {
|
|
97
|
+
const validate = contentAjv.compile(schema);
|
|
98
|
+
return (data) => (validate(data) ? [] : validate.errors);
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=validation.js.map
|