@t09tanaka/stoneage 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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +768 -0
- package/dist/agent-skill.d.ts +1 -0
- package/dist/agent-skill.js +140 -0
- package/dist/assets.d.ts +125 -0
- package/dist/assets.js +341 -0
- package/dist/cli.d.ts +236 -0
- package/dist/cli.js +3077 -0
- package/dist/core.d.ts +473 -0
- package/dist/core.js +2897 -0
- package/dist/data.d.ts +121 -0
- package/dist/data.js +358 -0
- package/dist/deploy.d.ts +34 -0
- package/dist/deploy.js +203 -0
- package/dist/dev.d.ts +36 -0
- package/dist/dev.js +313 -0
- package/dist/example.d.ts +134 -0
- package/dist/example.js +1272 -0
- package/dist/fragment/client.d.ts +13 -0
- package/dist/fragment/client.js +150 -0
- package/dist/fragment.d.ts +15 -0
- package/dist/fragment.js +27 -0
- package/dist/html.d.ts +57 -0
- package/dist/html.js +208 -0
- package/dist/images.d.ts +95 -0
- package/dist/images.js +292 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/migration.d.ts +157 -0
- package/dist/migration.js +983 -0
- package/dist/optimize.d.ts +15 -0
- package/dist/optimize.js +215 -0
- package/dist/pagination.d.ts +26 -0
- package/dist/pagination.js +62 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +10 -0
- package/dist/search.d.ts +32 -0
- package/dist/search.js +55 -0
- package/dist/testing.d.ts +66 -0
- package/dist/testing.js +97 -0
- package/dist/validate.d.ts +2 -0
- package/dist/validate.js +1 -0
- package/jsx-loader.mjs +52 -0
- package/jsx-register.mjs +7 -0
- package/package.json +135 -0
- package/tsconfig.base.json +16 -0
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ArtifactContent, ArtifactEntry, ArtifactFamily, Dependency, PageMetadata, RouteEntry, RouteParams } from "./core.js";
|
|
2
|
+
export type PublicDataArtifact<Data> = {
|
|
3
|
+
key: string;
|
|
4
|
+
data: Data;
|
|
5
|
+
dependency: Dependency;
|
|
6
|
+
sourceDependencies: Dependency[];
|
|
7
|
+
};
|
|
8
|
+
export type PublicDataArtifactInput<Data> = {
|
|
9
|
+
key: string;
|
|
10
|
+
data: Data;
|
|
11
|
+
sourceDependencies?: Dependency[];
|
|
12
|
+
};
|
|
13
|
+
export type NormalizedDataSnapshot<Data> = PublicDataArtifact<Data> & {
|
|
14
|
+
path: string;
|
|
15
|
+
format: "json" | "csv";
|
|
16
|
+
bytes: number;
|
|
17
|
+
hash: string;
|
|
18
|
+
written: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type NormalizedJsonDataOptions<Data> = PublicDataArtifactInput<Data> & {
|
|
21
|
+
path: string;
|
|
22
|
+
pretty?: boolean;
|
|
23
|
+
renderedContent?: string | (() => string);
|
|
24
|
+
contentFingerprint?: NormalizedDataContentFingerprint;
|
|
25
|
+
cacheManifestPath?: string;
|
|
26
|
+
};
|
|
27
|
+
export type NormalizedCsvDataOptions<Row> = {
|
|
28
|
+
key: string;
|
|
29
|
+
path: string;
|
|
30
|
+
rows: readonly Row[];
|
|
31
|
+
columns: readonly CsvColumn<Row>[];
|
|
32
|
+
header?: boolean;
|
|
33
|
+
sourceDependencies?: Dependency[];
|
|
34
|
+
contentFingerprint?: NormalizedDataContentFingerprint;
|
|
35
|
+
cacheManifestPath?: string;
|
|
36
|
+
};
|
|
37
|
+
export type NormalizedDataContentFingerprint = {
|
|
38
|
+
hash: string;
|
|
39
|
+
bytes?: number;
|
|
40
|
+
};
|
|
41
|
+
export type NormalizedDataCacheManifest = {
|
|
42
|
+
version: 1;
|
|
43
|
+
snapshots: Record<string, NormalizedDataCacheManifestSnapshot>;
|
|
44
|
+
};
|
|
45
|
+
export type NormalizedDataCacheManifestSnapshot = {
|
|
46
|
+
path: string;
|
|
47
|
+
format: NormalizedDataSnapshot<unknown>["format"];
|
|
48
|
+
hash: string;
|
|
49
|
+
bytes: number;
|
|
50
|
+
};
|
|
51
|
+
export type ViewModelRecord<ViewModel> = {
|
|
52
|
+
viewModel: ViewModel;
|
|
53
|
+
dependencies: Dependency[];
|
|
54
|
+
};
|
|
55
|
+
export type ViewModelInput<ViewModel> = {
|
|
56
|
+
viewModel: ViewModel;
|
|
57
|
+
dependencies?: Dependency[];
|
|
58
|
+
};
|
|
59
|
+
export type DeriveViewModelsOptions = {
|
|
60
|
+
artifactDependency?: "fallback" | "always" | "never";
|
|
61
|
+
};
|
|
62
|
+
export type RecordDependency<RecordItem> = {
|
|
63
|
+
key: string;
|
|
64
|
+
value: RecordItem;
|
|
65
|
+
};
|
|
66
|
+
export type DeriveRecordViewModelsOptions<RecordItem, ViewModel> = DeriveViewModelsOptions & {
|
|
67
|
+
key: (record: RecordItem, index: number) => string;
|
|
68
|
+
dependencies?: (record: RecordItem, index: number) => Dependency[];
|
|
69
|
+
viewModel: (record: RecordItem, index: number) => ViewModel;
|
|
70
|
+
};
|
|
71
|
+
export type RouteEntriesFromViewModelsOptions<ViewModel> = {
|
|
72
|
+
params: (viewModel: ViewModel) => RouteParams;
|
|
73
|
+
path?: (viewModel: ViewModel) => string | undefined;
|
|
74
|
+
dependencies?: (viewModel: ViewModel) => Dependency[];
|
|
75
|
+
metadata: (viewModel: ViewModel) => PageMetadata;
|
|
76
|
+
render: (viewModel: ViewModel) => string | Promise<string>;
|
|
77
|
+
};
|
|
78
|
+
export type ArtifactEntriesFromPublicDataOptions<Data> = {
|
|
79
|
+
params: (artifact: PublicDataArtifact<Data>) => RouteParams;
|
|
80
|
+
path?: (artifact: PublicDataArtifact<Data>) => string | undefined;
|
|
81
|
+
dependencies?: (artifact: PublicDataArtifact<Data>) => Dependency[];
|
|
82
|
+
render: (artifact: PublicDataArtifact<Data>) => ArtifactContent | Promise<ArtifactContent>;
|
|
83
|
+
};
|
|
84
|
+
export type JsonArtifactOptions = {
|
|
85
|
+
pretty?: boolean;
|
|
86
|
+
};
|
|
87
|
+
export type CsvCell = string | number | boolean | null | undefined;
|
|
88
|
+
export type CsvColumn<Row> = {
|
|
89
|
+
header: string;
|
|
90
|
+
value: (row: Row, index: number) => CsvCell;
|
|
91
|
+
};
|
|
92
|
+
export type CsvArtifactOptions = {
|
|
93
|
+
header?: boolean;
|
|
94
|
+
};
|
|
95
|
+
export type PublicDataJsonManifestItem = {
|
|
96
|
+
key: string;
|
|
97
|
+
path: string;
|
|
98
|
+
};
|
|
99
|
+
export type PublicDataJsonManifest = {
|
|
100
|
+
version: 1;
|
|
101
|
+
artifacts: PublicDataJsonManifestItem[];
|
|
102
|
+
};
|
|
103
|
+
export type PublicDataJsonArtifactsOptions = {
|
|
104
|
+
name?: string;
|
|
105
|
+
path: (artifact: PublicDataArtifact<unknown>) => string;
|
|
106
|
+
manifestPath?: string | false;
|
|
107
|
+
manifestKey?: string;
|
|
108
|
+
pretty?: boolean;
|
|
109
|
+
dependencies?: (artifact: PublicDataArtifact<unknown>) => Dependency[];
|
|
110
|
+
};
|
|
111
|
+
export declare function definePublicDataArtifact<Data>(input: PublicDataArtifactInput<Data>): PublicDataArtifact<Data>;
|
|
112
|
+
export declare function writeNormalizedJsonData<Data>(options: NormalizedJsonDataOptions<Data>): Promise<NormalizedDataSnapshot<Data>>;
|
|
113
|
+
export declare function writeNormalizedCsvData<Row>(options: NormalizedCsvDataOptions<Row>): Promise<NormalizedDataSnapshot<readonly Row[]>>;
|
|
114
|
+
export declare function recordDependency<RecordItem>(key: string, record: RecordItem): RecordDependency<RecordItem>;
|
|
115
|
+
export declare function deriveViewModels<Data, ViewModel>(artifact: PublicDataArtifact<Data>, derive: (data: Data) => ViewModelInput<ViewModel>[], options?: DeriveViewModelsOptions): ViewModelRecord<ViewModel>[];
|
|
116
|
+
export declare function deriveRecordViewModels<RecordItem, ViewModel>(artifact: PublicDataArtifact<readonly RecordItem[]>, options: DeriveRecordViewModelsOptions<RecordItem, ViewModel>): ViewModelRecord<ViewModel>[];
|
|
117
|
+
export declare function routeEntriesFromViewModels<ViewModel>(records: ViewModelRecord<ViewModel>[], options: RouteEntriesFromViewModelsOptions<ViewModel>): RouteEntry[];
|
|
118
|
+
export declare function artifactEntriesFromPublicData<Data>(artifacts: PublicDataArtifact<Data>[], options: ArtifactEntriesFromPublicDataOptions<Data>): ArtifactEntry[];
|
|
119
|
+
export declare function definePublicDataJsonArtifacts(artifacts: readonly PublicDataArtifact<unknown>[], options: PublicDataJsonArtifactsOptions): ArtifactFamily;
|
|
120
|
+
export declare function renderJsonArtifact(value: unknown, options?: JsonArtifactOptions): string;
|
|
121
|
+
export declare function renderCsvArtifact<Row>(rows: readonly Row[], columns: readonly CsvColumn<Row>[], options?: CsvArtifactOptions): string;
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
const normalizedDataManifestUpdateQueues = new Map();
|
|
5
|
+
export function definePublicDataArtifact(input) {
|
|
6
|
+
return {
|
|
7
|
+
key: input.key,
|
|
8
|
+
data: input.data,
|
|
9
|
+
dependency: {
|
|
10
|
+
key: input.key,
|
|
11
|
+
value: input.data,
|
|
12
|
+
},
|
|
13
|
+
sourceDependencies: [...(input.sourceDependencies ?? [])],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function writeNormalizedJsonData(options) {
|
|
17
|
+
const cached = await readCachedNormalizedDataSnapshot({
|
|
18
|
+
key: options.key,
|
|
19
|
+
path: options.path,
|
|
20
|
+
data: options.data,
|
|
21
|
+
format: "json",
|
|
22
|
+
contentFingerprint: options.contentFingerprint,
|
|
23
|
+
cacheManifestPath: options.cacheManifestPath,
|
|
24
|
+
sourceDependencies: options.sourceDependencies,
|
|
25
|
+
});
|
|
26
|
+
if (cached) {
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
const content = typeof options.renderedContent === "function"
|
|
30
|
+
? options.renderedContent()
|
|
31
|
+
: (options.renderedContent ?? renderJsonArtifact(options.data, { pretty: options.pretty }));
|
|
32
|
+
return writeNormalizedDataSnapshot({
|
|
33
|
+
key: options.key,
|
|
34
|
+
path: options.path,
|
|
35
|
+
data: options.data,
|
|
36
|
+
format: "json",
|
|
37
|
+
content,
|
|
38
|
+
contentFingerprint: options.contentFingerprint,
|
|
39
|
+
cacheManifestPath: options.cacheManifestPath,
|
|
40
|
+
sourceDependencies: options.sourceDependencies,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export async function writeNormalizedCsvData(options) {
|
|
44
|
+
const cached = await readCachedNormalizedDataSnapshot({
|
|
45
|
+
key: options.key,
|
|
46
|
+
path: options.path,
|
|
47
|
+
data: options.rows,
|
|
48
|
+
format: "csv",
|
|
49
|
+
contentFingerprint: options.contentFingerprint,
|
|
50
|
+
cacheManifestPath: options.cacheManifestPath,
|
|
51
|
+
sourceDependencies: options.sourceDependencies,
|
|
52
|
+
});
|
|
53
|
+
if (cached) {
|
|
54
|
+
return cached;
|
|
55
|
+
}
|
|
56
|
+
const content = renderCsvArtifact(options.rows, options.columns, {
|
|
57
|
+
header: options.header,
|
|
58
|
+
});
|
|
59
|
+
return writeNormalizedDataSnapshot({
|
|
60
|
+
key: options.key,
|
|
61
|
+
path: options.path,
|
|
62
|
+
data: options.rows,
|
|
63
|
+
format: "csv",
|
|
64
|
+
content,
|
|
65
|
+
contentFingerprint: options.contentFingerprint,
|
|
66
|
+
cacheManifestPath: options.cacheManifestPath,
|
|
67
|
+
sourceDependencies: options.sourceDependencies,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function recordDependency(key, record) {
|
|
71
|
+
return { key, value: record };
|
|
72
|
+
}
|
|
73
|
+
export function deriveViewModels(artifact, derive, options = {}) {
|
|
74
|
+
const artifactDependency = options.artifactDependency ?? "fallback";
|
|
75
|
+
return derive(artifact.data).map((item) => ({
|
|
76
|
+
viewModel: item.viewModel,
|
|
77
|
+
dependencies: dependenciesForViewModel(artifact.dependency, item.dependencies ?? [], artifactDependency),
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
export function deriveRecordViewModels(artifact, options) {
|
|
81
|
+
const artifactDependency = options.artifactDependency ?? "fallback";
|
|
82
|
+
return artifact.data.map((record, index) => {
|
|
83
|
+
const dependencies = [
|
|
84
|
+
recordDependency(options.key(record, index), record),
|
|
85
|
+
...(options.dependencies?.(record, index) ?? []),
|
|
86
|
+
];
|
|
87
|
+
return {
|
|
88
|
+
viewModel: options.viewModel(record, index),
|
|
89
|
+
dependencies: dependenciesForViewModel(artifact.dependency, dependencies, artifactDependency),
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function dependenciesForViewModel(artifactDependency, dependencies, mode) {
|
|
94
|
+
if (mode === "always") {
|
|
95
|
+
return [artifactDependency, ...dependencies];
|
|
96
|
+
}
|
|
97
|
+
if (mode === "never" || dependencies.length > 0) {
|
|
98
|
+
return dependencies;
|
|
99
|
+
}
|
|
100
|
+
return [artifactDependency];
|
|
101
|
+
}
|
|
102
|
+
export function routeEntriesFromViewModels(records, options) {
|
|
103
|
+
return records.map((record) => ({
|
|
104
|
+
params: options.params(record.viewModel),
|
|
105
|
+
path: options.path?.(record.viewModel),
|
|
106
|
+
dependencies: [
|
|
107
|
+
...record.dependencies,
|
|
108
|
+
...(options.dependencies?.(record.viewModel) ?? []),
|
|
109
|
+
],
|
|
110
|
+
metadata: options.metadata(record.viewModel),
|
|
111
|
+
render: () => options.render(record.viewModel),
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
export function artifactEntriesFromPublicData(artifacts, options) {
|
|
115
|
+
return artifacts.map((artifact) => ({
|
|
116
|
+
params: options.params(artifact),
|
|
117
|
+
path: options.path?.(artifact),
|
|
118
|
+
dependencies: [
|
|
119
|
+
artifact.dependency,
|
|
120
|
+
...artifact.sourceDependencies,
|
|
121
|
+
...(options.dependencies?.(artifact) ?? []),
|
|
122
|
+
],
|
|
123
|
+
render: () => options.render(artifact),
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
export function definePublicDataJsonArtifacts(artifacts, options) {
|
|
127
|
+
const manifestPath = options.manifestPath === undefined ? "/site-data.json" : options.manifestPath;
|
|
128
|
+
return {
|
|
129
|
+
name: options.name ?? "publicData",
|
|
130
|
+
pattern: "/:name.json",
|
|
131
|
+
entries: () => {
|
|
132
|
+
const publicEntries = artifacts.map((artifact) => {
|
|
133
|
+
const path = options.path(artifact);
|
|
134
|
+
return {
|
|
135
|
+
params: { name: fileStem(path) },
|
|
136
|
+
path,
|
|
137
|
+
dependencies: [
|
|
138
|
+
artifact.dependency,
|
|
139
|
+
...artifact.sourceDependencies,
|
|
140
|
+
...(options.dependencies?.(artifact) ?? []),
|
|
141
|
+
],
|
|
142
|
+
render: () => renderJsonArtifact(artifact.data, { pretty: options.pretty }),
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
if (manifestPath === false) {
|
|
146
|
+
return publicEntries;
|
|
147
|
+
}
|
|
148
|
+
const manifest = {
|
|
149
|
+
version: 1,
|
|
150
|
+
artifacts: publicEntries
|
|
151
|
+
.map((entry, index) => ({
|
|
152
|
+
key: artifacts[index]?.key ?? "",
|
|
153
|
+
path: entry.path,
|
|
154
|
+
}))
|
|
155
|
+
.sort((left, right) => left.key.localeCompare(right.key)),
|
|
156
|
+
};
|
|
157
|
+
return [
|
|
158
|
+
...publicEntries,
|
|
159
|
+
{
|
|
160
|
+
params: { name: fileStem(manifestPath) },
|
|
161
|
+
path: manifestPath,
|
|
162
|
+
dependencies: [
|
|
163
|
+
{
|
|
164
|
+
key: options.manifestKey ?? "public-data:manifest",
|
|
165
|
+
value: manifest,
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
render: () => renderJsonArtifact(manifest, { pretty: options.pretty }),
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
export function renderJsonArtifact(value, options = {}) {
|
|
175
|
+
return `${JSON.stringify(value, null, options.pretty ? 2 : undefined)}\n`;
|
|
176
|
+
}
|
|
177
|
+
export function renderCsvArtifact(rows, columns, options = {}) {
|
|
178
|
+
const includeHeader = options.header ?? true;
|
|
179
|
+
const lines = [
|
|
180
|
+
...(includeHeader ? [columns.map((column) => escapeCsvCell(column.header)).join(",")] : []),
|
|
181
|
+
...rows.map((row, index) => columns.map((column) => escapeCsvCell(column.value(row, index))).join(",")),
|
|
182
|
+
];
|
|
183
|
+
return `${lines.join("\n")}\n`;
|
|
184
|
+
}
|
|
185
|
+
function escapeCsvCell(value) {
|
|
186
|
+
if (value === null || value === undefined) {
|
|
187
|
+
return "";
|
|
188
|
+
}
|
|
189
|
+
const text = String(value);
|
|
190
|
+
return /[",\r\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
|
|
191
|
+
}
|
|
192
|
+
function fileStem(path) {
|
|
193
|
+
const filename = path.split("/").filter(Boolean).at(-1) ?? "data";
|
|
194
|
+
return filename.replace(/\.[^.]*$/, "") || "data";
|
|
195
|
+
}
|
|
196
|
+
async function writeNormalizedDataSnapshot(options) {
|
|
197
|
+
const useFingerprintCache = hasNormalizedDataFingerprintCache(options);
|
|
198
|
+
const hash = useFingerprintCache ? options.contentFingerprint.hash : hashContent(options.content);
|
|
199
|
+
const written = await writeTextIfChanged(options.path, options.content);
|
|
200
|
+
const bytes = Buffer.byteLength(options.content);
|
|
201
|
+
if (useFingerprintCache) {
|
|
202
|
+
await writeNormalizedDataCacheManifestSnapshot(options.cacheManifestPath, {
|
|
203
|
+
key: options.key,
|
|
204
|
+
path: options.path,
|
|
205
|
+
format: options.format,
|
|
206
|
+
hash,
|
|
207
|
+
bytes,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
key: options.key,
|
|
212
|
+
data: options.data,
|
|
213
|
+
dependency: {
|
|
214
|
+
key: options.key,
|
|
215
|
+
hash,
|
|
216
|
+
},
|
|
217
|
+
sourceDependencies: [...(options.sourceDependencies ?? [])],
|
|
218
|
+
path: options.path,
|
|
219
|
+
format: options.format,
|
|
220
|
+
bytes,
|
|
221
|
+
hash,
|
|
222
|
+
written,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function hasNormalizedDataFingerprintCache(options) {
|
|
226
|
+
return Boolean(options.contentFingerprint && options.cacheManifestPath);
|
|
227
|
+
}
|
|
228
|
+
async function readCachedNormalizedDataSnapshot(options) {
|
|
229
|
+
if (!options.contentFingerprint || !options.cacheManifestPath) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
const manifest = await readNormalizedDataCacheManifest(options.cacheManifestPath);
|
|
233
|
+
const snapshot = manifest.snapshots[options.key];
|
|
234
|
+
if (!snapshot ||
|
|
235
|
+
snapshot.path !== options.path ||
|
|
236
|
+
snapshot.format !== options.format ||
|
|
237
|
+
snapshot.hash !== options.contentFingerprint.hash ||
|
|
238
|
+
(options.contentFingerprint.bytes !== undefined &&
|
|
239
|
+
snapshot.bytes !== options.contentFingerprint.bytes)) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
const current = await readFileStats(options.path);
|
|
243
|
+
const bytes = options.contentFingerprint.bytes ?? snapshot.bytes;
|
|
244
|
+
if (!current || current.size !== bytes) {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
key: options.key,
|
|
249
|
+
data: options.data,
|
|
250
|
+
dependency: {
|
|
251
|
+
key: options.key,
|
|
252
|
+
hash: options.contentFingerprint.hash,
|
|
253
|
+
},
|
|
254
|
+
sourceDependencies: [...(options.sourceDependencies ?? [])],
|
|
255
|
+
path: options.path,
|
|
256
|
+
format: options.format,
|
|
257
|
+
bytes,
|
|
258
|
+
hash: options.contentFingerprint.hash,
|
|
259
|
+
written: false,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async function writeNormalizedDataCacheManifestSnapshot(manifestPath, snapshot) {
|
|
263
|
+
const previous = normalizedDataManifestUpdateQueues.get(manifestPath) ?? Promise.resolve();
|
|
264
|
+
const next = previous.catch(() => undefined).then(() => writeNormalizedDataCacheManifestSnapshotWithoutQueue(manifestPath, snapshot));
|
|
265
|
+
const queued = next.finally(() => {
|
|
266
|
+
if (normalizedDataManifestUpdateQueues.get(manifestPath) === queued) {
|
|
267
|
+
normalizedDataManifestUpdateQueues.delete(manifestPath);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
normalizedDataManifestUpdateQueues.set(manifestPath, queued);
|
|
271
|
+
await next;
|
|
272
|
+
}
|
|
273
|
+
async function writeNormalizedDataCacheManifestSnapshotWithoutQueue(manifestPath, snapshot) {
|
|
274
|
+
const manifest = await readNormalizedDataCacheManifest(manifestPath);
|
|
275
|
+
manifest.snapshots[snapshot.key] = {
|
|
276
|
+
path: snapshot.path,
|
|
277
|
+
format: snapshot.format,
|
|
278
|
+
hash: snapshot.hash,
|
|
279
|
+
bytes: snapshot.bytes,
|
|
280
|
+
};
|
|
281
|
+
await mkdir(dirname(manifestPath), { recursive: true });
|
|
282
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
283
|
+
}
|
|
284
|
+
async function readNormalizedDataCacheManifest(manifestPath) {
|
|
285
|
+
try {
|
|
286
|
+
const parsed = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
287
|
+
return {
|
|
288
|
+
version: 1,
|
|
289
|
+
snapshots: isObjectRecord(parsed.snapshots) ? normalizeCacheSnapshots(parsed.snapshots) : {},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (isMissingPath(error)) {
|
|
294
|
+
return { version: 1, snapshots: {} };
|
|
295
|
+
}
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function normalizeCacheSnapshots(snapshots) {
|
|
300
|
+
const normalized = {};
|
|
301
|
+
for (const [key, value] of Object.entries(snapshots)) {
|
|
302
|
+
if (!isObjectRecord(value)) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const snapshot = value;
|
|
306
|
+
if (typeof snapshot.path === "string" &&
|
|
307
|
+
isNormalizedDataFormat(snapshot.format) &&
|
|
308
|
+
typeof snapshot.hash === "string" &&
|
|
309
|
+
typeof snapshot.bytes === "number") {
|
|
310
|
+
normalized[key] = {
|
|
311
|
+
path: snapshot.path,
|
|
312
|
+
format: snapshot.format,
|
|
313
|
+
hash: snapshot.hash,
|
|
314
|
+
bytes: snapshot.bytes,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return normalized;
|
|
319
|
+
}
|
|
320
|
+
function isNormalizedDataFormat(value) {
|
|
321
|
+
return value === "json" || value === "csv";
|
|
322
|
+
}
|
|
323
|
+
function isObjectRecord(value) {
|
|
324
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
325
|
+
}
|
|
326
|
+
async function readFileStats(path) {
|
|
327
|
+
try {
|
|
328
|
+
return await stat(path);
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
if (isMissingPath(error)) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function writeTextIfChanged(path, content) {
|
|
338
|
+
try {
|
|
339
|
+
const current = await readFile(path, "utf8");
|
|
340
|
+
if (current === content) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
if (!isMissingPath(error)) {
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
await mkdir(dirname(path), { recursive: true });
|
|
350
|
+
await writeFile(path, content);
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
function hashContent(content) {
|
|
354
|
+
return createHash("sha256").update(content).digest().subarray(0, 16).toString("base64url");
|
|
355
|
+
}
|
|
356
|
+
function isMissingPath(error) {
|
|
357
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
358
|
+
}
|
package/dist/deploy.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type DeployProvider = "netlify" | "github-pages";
|
|
2
|
+
export type DeployArtifactsOptions = {
|
|
3
|
+
outDir: string;
|
|
4
|
+
provider: DeployProvider;
|
|
5
|
+
/**
|
|
6
|
+
* Custom domain for GitHub Pages. Only the host is written to CNAME
|
|
7
|
+
* (protocol and path are stripped). Ignored by other providers.
|
|
8
|
+
*/
|
|
9
|
+
cname?: string;
|
|
10
|
+
};
|
|
11
|
+
export type DeployArtifactsResult = {
|
|
12
|
+
provider: DeployProvider;
|
|
13
|
+
files: string[];
|
|
14
|
+
redirects: number;
|
|
15
|
+
headers: number;
|
|
16
|
+
};
|
|
17
|
+
type RedirectsManifest = {
|
|
18
|
+
redirects: Array<{
|
|
19
|
+
from: string;
|
|
20
|
+
to: string;
|
|
21
|
+
status: number;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
type HeadersManifest = {
|
|
25
|
+
headers: Array<{
|
|
26
|
+
path: string;
|
|
27
|
+
headers: Record<string, string>;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
export declare function writeDeployArtifacts(options: DeployArtifactsOptions): Promise<DeployArtifactsResult>;
|
|
31
|
+
export declare function normalizeCname(cname: string | undefined): string | undefined;
|
|
32
|
+
export declare function renderNetlifyRedirects(redirects: RedirectsManifest["redirects"]): string;
|
|
33
|
+
export declare function renderNetlifyHeaders(headers: HeadersManifest["headers"]): string;
|
|
34
|
+
export {};
|