@earendil-works/chord 0.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/README.md +205 -0
- package/dist/api.d.ts +15 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +72 -0
- package/dist/api.js.map +1 -0
- package/dist/bundler.d.ts +6 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +3 -0
- package/dist/bundler.js.map +1 -0
- package/dist/context/index.d.ts +24 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +97 -0
- package/dist/context/index.js.map +1 -0
- package/dist/delta/index.d.ts +119 -0
- package/dist/delta/index.d.ts.map +1 -0
- package/dist/delta/index.js +1213 -0
- package/dist/delta/index.js.map +1 -0
- package/dist/facets/host.d.ts +12 -0
- package/dist/facets/host.d.ts.map +1 -0
- package/dist/facets/host.js +763 -0
- package/dist/facets/host.js.map +1 -0
- package/dist/facets/loader.d.ts +3 -0
- package/dist/facets/loader.d.ts.map +1 -0
- package/dist/facets/loader.js +5 -0
- package/dist/facets/loader.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +4 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +57 -0
- package/dist/json.js.map +1 -0
- package/dist/node/bundle-loader.d.ts +30 -0
- package/dist/node/bundle-loader.d.ts.map +1 -0
- package/dist/node/bundle-loader.js +377 -0
- package/dist/node/bundle-loader.js.map +1 -0
- package/dist/node/bundle.d.ts +26 -0
- package/dist/node/bundle.d.ts.map +1 -0
- package/dist/node/bundle.js +183 -0
- package/dist/node/bundle.js.map +1 -0
- package/dist/node/manifest.d.ts +36 -0
- package/dist/node/manifest.d.ts.map +1 -0
- package/dist/node/manifest.js +6 -0
- package/dist/node/manifest.js.map +1 -0
- package/dist/node/package.d.ts +15 -0
- package/dist/node/package.d.ts.map +1 -0
- package/dist/node/package.js +199 -0
- package/dist/node/package.js.map +1 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +3 -0
- package/dist/node.js.map +1 -0
- package/dist/services/consumer.d.ts +11 -0
- package/dist/services/consumer.d.ts.map +1 -0
- package/dist/services/consumer.js +537 -0
- package/dist/services/consumer.js.map +1 -0
- package/dist/services/errors.d.ts +8 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +22 -0
- package/dist/services/errors.js.map +1 -0
- package/dist/services/handle.d.ts +15 -0
- package/dist/services/handle.d.ts.map +1 -0
- package/dist/services/handle.js +99 -0
- package/dist/services/handle.js.map +1 -0
- package/dist/services/instances.d.ts +25 -0
- package/dist/services/instances.d.ts.map +1 -0
- package/dist/services/instances.js +137 -0
- package/dist/services/instances.js.map +1 -0
- package/dist/services/loopback.d.ts +5 -0
- package/dist/services/loopback.d.ts.map +1 -0
- package/dist/services/loopback.js +15 -0
- package/dist/services/loopback.js.map +1 -0
- package/dist/services/provider.d.ts +37 -0
- package/dist/services/provider.d.ts.map +1 -0
- package/dist/services/provider.js +472 -0
- package/dist/services/provider.js.map +1 -0
- package/dist/services/state-codec.d.ts +15 -0
- package/dist/services/state-codec.d.ts.map +1 -0
- package/dist/services/state-codec.js +115 -0
- package/dist/services/state-codec.js.map +1 -0
- package/dist/services/state-internals.d.ts +11 -0
- package/dist/services/state-internals.d.ts.map +1 -0
- package/dist/services/state-internals.js +10 -0
- package/dist/services/state-internals.js.map +1 -0
- package/dist/services/state.d.ts +23 -0
- package/dist/services/state.d.ts.map +1 -0
- package/dist/services/state.js +124 -0
- package/dist/services/state.js.map +1 -0
- package/dist/services/wire.d.ts +60 -0
- package/dist/services/wire.d.ts.map +1 -0
- package/dist/services/wire.js +173 -0
- package/dist/services/wire.js.map +1 -0
- package/dist/types.d.ts +203 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { build } from "esbuild";
|
|
5
|
+
import { FACET_BUNDLE_FORMAT, FACET_BUNDLE_FORMAT_VERSION, FACET_BUNDLE_MANIFEST_FILE, } from "./manifest.js";
|
|
6
|
+
/** Bundle each opaque facet entry into an independent content-addressed CommonJS file. */
|
|
7
|
+
export async function bundleFacets(options) {
|
|
8
|
+
validateOptions(options);
|
|
9
|
+
const workingDirectory = resolve(options.workingDirectory ?? process.cwd());
|
|
10
|
+
const outputDirectory = resolve(workingDirectory, options.outdir);
|
|
11
|
+
const outputParent = dirname(outputDirectory);
|
|
12
|
+
await mkdir(outputParent, { recursive: true });
|
|
13
|
+
const temporaryDirectory = join(outputParent, `.${basename(outputDirectory)}.tmp-${randomUUID()}`);
|
|
14
|
+
await mkdir(temporaryDirectory);
|
|
15
|
+
try {
|
|
16
|
+
const entries = {};
|
|
17
|
+
for (const [entryName, source] of Object.entries(options.entries).sort(([left], [right]) => left.localeCompare(right))) {
|
|
18
|
+
entries[entryName] = await bundleEntry({
|
|
19
|
+
entryName,
|
|
20
|
+
source: resolve(workingDirectory, source),
|
|
21
|
+
temporaryDirectory,
|
|
22
|
+
workingDirectory,
|
|
23
|
+
options,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const manifest = Object.freeze({
|
|
27
|
+
format: FACET_BUNDLE_FORMAT,
|
|
28
|
+
formatVersion: FACET_BUNDLE_FORMAT_VERSION,
|
|
29
|
+
plugin: Object.freeze({
|
|
30
|
+
id: options.plugin.id,
|
|
31
|
+
...(options.plugin.version === undefined ? {} : { version: options.plugin.version }),
|
|
32
|
+
}),
|
|
33
|
+
entries: Object.freeze(entries),
|
|
34
|
+
});
|
|
35
|
+
await writeFile(join(temporaryDirectory, FACET_BUNDLE_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
36
|
+
await replaceDirectory(temporaryDirectory, outputDirectory);
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
manifest,
|
|
39
|
+
manifestPath: join(outputDirectory, FACET_BUNDLE_MANIFEST_FILE),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
await rm(temporaryDirectory, { force: true, recursive: true });
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function bundleEntry(input) {
|
|
48
|
+
const entryPrefix = `facet-${shortHash(input.entryName)}`;
|
|
49
|
+
const requestedPlatform = input.options.platform ?? "node";
|
|
50
|
+
const buildOptions = {
|
|
51
|
+
absWorkingDir: input.workingDirectory,
|
|
52
|
+
banner: { js: '"use strict";' },
|
|
53
|
+
bundle: true,
|
|
54
|
+
define: input.options.define,
|
|
55
|
+
entryNames: `${entryPrefix}-[hash]`,
|
|
56
|
+
entryPoints: [input.source],
|
|
57
|
+
external: [...new Set(["@earendil-works/chord", "@earendil-works/chord/*", ...(input.options.external ?? [])])],
|
|
58
|
+
format: "cjs",
|
|
59
|
+
legalComments: "none",
|
|
60
|
+
logLevel: "silent",
|
|
61
|
+
metafile: true,
|
|
62
|
+
minify: input.options.minify ?? false,
|
|
63
|
+
outdir: input.temporaryDirectory,
|
|
64
|
+
outExtension: { ".js": ".cjs" },
|
|
65
|
+
platform: requestedPlatform,
|
|
66
|
+
sourcemap: input.options.sourceMap === true ? "external" : false,
|
|
67
|
+
supported: { "dynamic-import": false },
|
|
68
|
+
target: input.options.target === undefined
|
|
69
|
+
? requestedPlatform === "node"
|
|
70
|
+
? "node22.19"
|
|
71
|
+
: "es2022"
|
|
72
|
+
: typeof input.options.target === "string"
|
|
73
|
+
? input.options.target
|
|
74
|
+
: [...input.options.target],
|
|
75
|
+
write: true,
|
|
76
|
+
};
|
|
77
|
+
let result;
|
|
78
|
+
try {
|
|
79
|
+
result = await build(buildOptions);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const diagnostics = esbuildMessages(error);
|
|
83
|
+
const detail = diagnostics.length === 0 ? "" : `\n${diagnostics.join("\n")}`;
|
|
84
|
+
throw new Error(`Could not bundle facet entry ${input.entryName}${detail}`, { cause: error });
|
|
85
|
+
}
|
|
86
|
+
const metafile = result.metafile;
|
|
87
|
+
if (metafile === undefined)
|
|
88
|
+
throw new Error(`Facet entry ${input.entryName} produced no build metadata`);
|
|
89
|
+
const outputs = Object.entries(metafile.outputs).filter(([path, output]) => output.entryPoint !== undefined && extname(path) === ".cjs");
|
|
90
|
+
if (outputs.length !== 1)
|
|
91
|
+
throw new Error(`Facet entry ${input.entryName} did not produce exactly one JavaScript file`);
|
|
92
|
+
const [outputPath, metadata] = outputs[0];
|
|
93
|
+
const absoluteOutputPath = resolve(input.workingDirectory, outputPath);
|
|
94
|
+
const file = relative(input.temporaryDirectory, absoluteOutputPath);
|
|
95
|
+
if (file.length === 0 || file.startsWith(`..${sep}`) || basename(file) !== file) {
|
|
96
|
+
throw new Error(`Facet entry ${input.entryName} produced an invalid output path`);
|
|
97
|
+
}
|
|
98
|
+
const sourceMap = input.options.sourceMap === true ? `${file}.map` : undefined;
|
|
99
|
+
const allowedOutputs = new Set([absoluteOutputPath]);
|
|
100
|
+
if (sourceMap !== undefined)
|
|
101
|
+
allowedOutputs.add(join(input.temporaryDirectory, sourceMap));
|
|
102
|
+
const unexpectedOutputs = Object.keys(metafile.outputs)
|
|
103
|
+
.map((path) => resolve(input.workingDirectory, path))
|
|
104
|
+
.filter((path) => !allowedOutputs.has(path));
|
|
105
|
+
if (unexpectedOutputs.length > 0) {
|
|
106
|
+
throw new Error(`Facet entry ${input.entryName} produced files other than its JavaScript bundle and source map`);
|
|
107
|
+
}
|
|
108
|
+
const contents = await readFile(absoluteOutputPath);
|
|
109
|
+
if (sourceMap !== undefined)
|
|
110
|
+
await stat(join(input.temporaryDirectory, sourceMap));
|
|
111
|
+
return Object.freeze({
|
|
112
|
+
file,
|
|
113
|
+
integrity: `sha256-${createHash("sha256").update(contents).digest("base64")}`,
|
|
114
|
+
externalImports: Object.freeze([...new Set(metadata.imports.filter(({ external }) => external).map(({ path }) => path))].sort()),
|
|
115
|
+
...(sourceMap === undefined ? {} : { sourceMap }),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function validateOptions(options) {
|
|
119
|
+
if (options.plugin.id.length === 0)
|
|
120
|
+
throw new TypeError("Facet bundle plugin ID must not be empty");
|
|
121
|
+
if (options.plugin.version !== undefined && options.plugin.version.length === 0) {
|
|
122
|
+
throw new TypeError("Facet bundle plugin version must not be empty");
|
|
123
|
+
}
|
|
124
|
+
const entries = Object.entries(options.entries);
|
|
125
|
+
if (entries.length === 0)
|
|
126
|
+
throw new TypeError("Facet bundle must contain at least one entry");
|
|
127
|
+
for (const [name, source] of entries) {
|
|
128
|
+
if (name.length === 0)
|
|
129
|
+
throw new TypeError("Facet bundle entry name must not be empty");
|
|
130
|
+
if (source.length === 0)
|
|
131
|
+
throw new TypeError(`Facet bundle entry ${name} must have a source path`);
|
|
132
|
+
}
|
|
133
|
+
for (const external of options.external ?? []) {
|
|
134
|
+
if (external.length === 0)
|
|
135
|
+
throw new TypeError("Facet bundle external import must not be empty");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function replaceDirectory(temporaryDirectory, outputDirectory) {
|
|
139
|
+
const backupDirectory = `${outputDirectory}.old-${randomUUID()}`;
|
|
140
|
+
let movedExisting = false;
|
|
141
|
+
try {
|
|
142
|
+
await rename(outputDirectory, backupDirectory);
|
|
143
|
+
movedExisting = true;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (!isMissingPath(error))
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
await rename(temporaryDirectory, outputDirectory);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (movedExisting)
|
|
154
|
+
await rename(backupDirectory, outputDirectory);
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
if (movedExisting)
|
|
158
|
+
await rm(backupDirectory, { force: true, recursive: true });
|
|
159
|
+
}
|
|
160
|
+
function shortHash(value) {
|
|
161
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
162
|
+
}
|
|
163
|
+
function esbuildMessages(error) {
|
|
164
|
+
if (!isRecord(error) || !Array.isArray(error.errors))
|
|
165
|
+
return [];
|
|
166
|
+
return error.errors.flatMap((candidate) => isEsbuildMessage(candidate) ? [formatEsbuildMessage(candidate)] : []);
|
|
167
|
+
}
|
|
168
|
+
function formatEsbuildMessage(message) {
|
|
169
|
+
const location = message.location;
|
|
170
|
+
return location === null
|
|
171
|
+
? message.text
|
|
172
|
+
: `${location.file}:${location.line}:${location.column + 1}: ${message.text}`;
|
|
173
|
+
}
|
|
174
|
+
function isEsbuildMessage(value) {
|
|
175
|
+
return isRecord(value) && typeof value.text === "string" && (value.location === null || isRecord(value.location));
|
|
176
|
+
}
|
|
177
|
+
function isMissingPath(error) {
|
|
178
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
179
|
+
}
|
|
180
|
+
function isRecord(value) {
|
|
181
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/node/bundle.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, extname, join, relative, resolve, sep } from \"node:path\";\nimport { type BuildOptions, build, type Message } from \"esbuild\";\nimport {\n\tFACET_BUNDLE_FORMAT,\n\tFACET_BUNDLE_FORMAT_VERSION,\n\tFACET_BUNDLE_MANIFEST_FILE,\n\ttype FacetBundleEntry,\n\ttype FacetBundleManifest,\n} from \"./manifest.ts\";\n\nexport type FacetBundlePlatform = \"node\" | \"browser\" | \"neutral\";\n\nexport interface BundleFacetsOptions {\n\treadonly plugin: {\n\t\treadonly id: string;\n\t\treadonly version?: string;\n\t};\n\t/** Opaque application-selected entry names mapped to TypeScript or JavaScript source files. */\n\treadonly entries: Readonly<Record<string, string>>;\n\treadonly outdir: string;\n\treadonly workingDirectory?: string;\n\t/** Additional package imports intentionally left for the loading application to resolve. */\n\treadonly external?: readonly string[];\n\treadonly sourceMap?: boolean;\n\treadonly minify?: boolean;\n\treadonly define?: Readonly<Record<string, string>>;\n\treadonly platform?: FacetBundlePlatform;\n\treadonly target?: string | readonly string[];\n}\n\nexport interface BundleFacetsResult {\n\treadonly manifest: FacetBundleManifest;\n\treadonly manifestPath: string;\n}\n\n/** Bundle each opaque facet entry into an independent content-addressed CommonJS file. */\nexport async function bundleFacets(options: BundleFacetsOptions): Promise<BundleFacetsResult> {\n\tvalidateOptions(options);\n\tconst workingDirectory = resolve(options.workingDirectory ?? process.cwd());\n\tconst outputDirectory = resolve(workingDirectory, options.outdir);\n\tconst outputParent = dirname(outputDirectory);\n\tawait mkdir(outputParent, { recursive: true });\n\tconst temporaryDirectory = join(outputParent, `.${basename(outputDirectory)}.tmp-${randomUUID()}`);\n\tawait mkdir(temporaryDirectory);\n\ttry {\n\t\tconst entries: Record<string, FacetBundleEntry> = {};\n\t\tfor (const [entryName, source] of Object.entries(options.entries).sort(([left], [right]) =>\n\t\t\tleft.localeCompare(right),\n\t\t)) {\n\t\t\tentries[entryName] = await bundleEntry({\n\t\t\t\tentryName,\n\t\t\t\tsource: resolve(workingDirectory, source),\n\t\t\t\ttemporaryDirectory,\n\t\t\t\tworkingDirectory,\n\t\t\t\toptions,\n\t\t\t});\n\t\t}\n\t\tconst manifest: FacetBundleManifest = Object.freeze({\n\t\t\tformat: FACET_BUNDLE_FORMAT,\n\t\t\tformatVersion: FACET_BUNDLE_FORMAT_VERSION,\n\t\t\tplugin: Object.freeze({\n\t\t\t\tid: options.plugin.id,\n\t\t\t\t...(options.plugin.version === undefined ? {} : { version: options.plugin.version }),\n\t\t\t}),\n\t\t\tentries: Object.freeze(entries),\n\t\t});\n\t\tawait writeFile(join(temporaryDirectory, FACET_BUNDLE_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`);\n\t\tawait replaceDirectory(temporaryDirectory, outputDirectory);\n\t\treturn Object.freeze({\n\t\t\tmanifest,\n\t\t\tmanifestPath: join(outputDirectory, FACET_BUNDLE_MANIFEST_FILE),\n\t\t});\n\t} catch (error) {\n\t\tawait rm(temporaryDirectory, { force: true, recursive: true });\n\t\tthrow error;\n\t}\n}\n\nasync function bundleEntry(input: {\n\treadonly entryName: string;\n\treadonly source: string;\n\treadonly temporaryDirectory: string;\n\treadonly workingDirectory: string;\n\treadonly options: BundleFacetsOptions;\n}): Promise<FacetBundleEntry> {\n\tconst entryPrefix = `facet-${shortHash(input.entryName)}`;\n\tconst requestedPlatform = input.options.platform ?? \"node\";\n\tconst buildOptions: BuildOptions = {\n\t\tabsWorkingDir: input.workingDirectory,\n\t\tbanner: { js: '\"use strict\";' },\n\t\tbundle: true,\n\t\tdefine: input.options.define,\n\t\tentryNames: `${entryPrefix}-[hash]`,\n\t\tentryPoints: [input.source],\n\t\texternal: [...new Set([\"@earendil-works/chord\", \"@earendil-works/chord/*\", ...(input.options.external ?? [])])],\n\t\tformat: \"cjs\",\n\t\tlegalComments: \"none\",\n\t\tlogLevel: \"silent\",\n\t\tmetafile: true,\n\t\tminify: input.options.minify ?? false,\n\t\toutdir: input.temporaryDirectory,\n\t\toutExtension: { \".js\": \".cjs\" },\n\t\tplatform: requestedPlatform,\n\t\tsourcemap: input.options.sourceMap === true ? \"external\" : false,\n\t\tsupported: { \"dynamic-import\": false },\n\t\ttarget:\n\t\t\tinput.options.target === undefined\n\t\t\t\t? requestedPlatform === \"node\"\n\t\t\t\t\t? \"node22.19\"\n\t\t\t\t\t: \"es2022\"\n\t\t\t\t: typeof input.options.target === \"string\"\n\t\t\t\t\t? input.options.target\n\t\t\t\t\t: [...input.options.target],\n\t\twrite: true,\n\t};\n\tlet result: Awaited<ReturnType<typeof build>>;\n\ttry {\n\t\tresult = await build(buildOptions);\n\t} catch (error) {\n\t\tconst diagnostics = esbuildMessages(error);\n\t\tconst detail = diagnostics.length === 0 ? \"\" : `\\n${diagnostics.join(\"\\n\")}`;\n\t\tthrow new Error(`Could not bundle facet entry ${input.entryName}${detail}`, { cause: error });\n\t}\n\tconst metafile = result.metafile;\n\tif (metafile === undefined) throw new Error(`Facet entry ${input.entryName} produced no build metadata`);\n\tconst outputs = Object.entries(metafile.outputs).filter(\n\t\t([path, output]) => output.entryPoint !== undefined && extname(path) === \".cjs\",\n\t);\n\tif (outputs.length !== 1)\n\t\tthrow new Error(`Facet entry ${input.entryName} did not produce exactly one JavaScript file`);\n\tconst [outputPath, metadata] = outputs[0]!;\n\tconst absoluteOutputPath = resolve(input.workingDirectory, outputPath);\n\tconst file = relative(input.temporaryDirectory, absoluteOutputPath);\n\tif (file.length === 0 || file.startsWith(`..${sep}`) || basename(file) !== file) {\n\t\tthrow new Error(`Facet entry ${input.entryName} produced an invalid output path`);\n\t}\n\tconst sourceMap = input.options.sourceMap === true ? `${file}.map` : undefined;\n\tconst allowedOutputs = new Set([absoluteOutputPath]);\n\tif (sourceMap !== undefined) allowedOutputs.add(join(input.temporaryDirectory, sourceMap));\n\tconst unexpectedOutputs = Object.keys(metafile.outputs)\n\t\t.map((path) => resolve(input.workingDirectory, path))\n\t\t.filter((path) => !allowedOutputs.has(path));\n\tif (unexpectedOutputs.length > 0) {\n\t\tthrow new Error(`Facet entry ${input.entryName} produced files other than its JavaScript bundle and source map`);\n\t}\n\tconst contents = await readFile(absoluteOutputPath);\n\tif (sourceMap !== undefined) await stat(join(input.temporaryDirectory, sourceMap));\n\treturn Object.freeze({\n\t\tfile,\n\t\tintegrity: `sha256-${createHash(\"sha256\").update(contents).digest(\"base64\")}`,\n\t\texternalImports: Object.freeze(\n\t\t\t[...new Set(metadata.imports.filter(({ external }) => external).map(({ path }) => path))].sort(),\n\t\t),\n\t\t...(sourceMap === undefined ? {} : { sourceMap }),\n\t});\n}\n\nfunction validateOptions(options: BundleFacetsOptions): void {\n\tif (options.plugin.id.length === 0) throw new TypeError(\"Facet bundle plugin ID must not be empty\");\n\tif (options.plugin.version !== undefined && options.plugin.version.length === 0) {\n\t\tthrow new TypeError(\"Facet bundle plugin version must not be empty\");\n\t}\n\tconst entries = Object.entries(options.entries);\n\tif (entries.length === 0) throw new TypeError(\"Facet bundle must contain at least one entry\");\n\tfor (const [name, source] of entries) {\n\t\tif (name.length === 0) throw new TypeError(\"Facet bundle entry name must not be empty\");\n\t\tif (source.length === 0) throw new TypeError(`Facet bundle entry ${name} must have a source path`);\n\t}\n\tfor (const external of options.external ?? []) {\n\t\tif (external.length === 0) throw new TypeError(\"Facet bundle external import must not be empty\");\n\t}\n}\n\nasync function replaceDirectory(temporaryDirectory: string, outputDirectory: string): Promise<void> {\n\tconst backupDirectory = `${outputDirectory}.old-${randomUUID()}`;\n\tlet movedExisting = false;\n\ttry {\n\t\tawait rename(outputDirectory, backupDirectory);\n\t\tmovedExisting = true;\n\t} catch (error) {\n\t\tif (!isMissingPath(error)) throw error;\n\t}\n\ttry {\n\t\tawait rename(temporaryDirectory, outputDirectory);\n\t} catch (error) {\n\t\tif (movedExisting) await rename(backupDirectory, outputDirectory);\n\t\tthrow error;\n\t}\n\tif (movedExisting) await rm(backupDirectory, { force: true, recursive: true });\n}\n\nfunction shortHash(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 12);\n}\n\nfunction esbuildMessages(error: unknown): string[] {\n\tif (!isRecord(error) || !Array.isArray(error.errors)) return [];\n\treturn (error.errors as readonly unknown[]).flatMap((candidate) =>\n\t\tisEsbuildMessage(candidate) ? [formatEsbuildMessage(candidate)] : [],\n\t);\n}\n\nfunction formatEsbuildMessage(message: Message): string {\n\tconst location = message.location;\n\treturn location === null\n\t\t? message.text\n\t\t: `${location.file}:${location.line}:${location.column + 1}: ${message.text}`;\n}\n\nfunction isEsbuildMessage(value: unknown): value is Message {\n\treturn isRecord(value) && typeof value.text === \"string\" && (value.location === null || isRecord(value.location));\n}\n\nfunction isMissingPath(error: unknown): boolean {\n\treturn isRecord(error) && error.code === \"ENOENT\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const FACET_BUNDLE_FORMAT = "chord.facet-bundle";
|
|
2
|
+
export declare const FACET_BUNDLE_FORMAT_VERSION = 2;
|
|
3
|
+
export declare const FACET_BUNDLE_MANIFEST_FILE = "chord-facets.json";
|
|
4
|
+
export declare const FACET_BUNDLE_ARTIFACT_FORMAT = "chord.facet-bundle-artifact";
|
|
5
|
+
export declare const FACET_BUNDLE_ARTIFACT_FORMAT_VERSION = 2;
|
|
6
|
+
export interface FacetBundleEntry {
|
|
7
|
+
/** Content-addressed CommonJS filename relative to the manifest. */
|
|
8
|
+
readonly file: string;
|
|
9
|
+
/** SHA-256 subresource-integrity value for the JavaScript file. */
|
|
10
|
+
readonly integrity: string;
|
|
11
|
+
/** Imports intentionally left for the loading application to resolve. */
|
|
12
|
+
readonly externalImports: readonly string[];
|
|
13
|
+
/** Source map filename relative to the manifest, when emitted. */
|
|
14
|
+
readonly sourceMap?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface FacetBundleManifest {
|
|
17
|
+
readonly format: typeof FACET_BUNDLE_FORMAT;
|
|
18
|
+
readonly formatVersion: typeof FACET_BUNDLE_FORMAT_VERSION;
|
|
19
|
+
readonly plugin: FacetBundlePlugin;
|
|
20
|
+
readonly entries: Readonly<Record<string, FacetBundleEntry>>;
|
|
21
|
+
}
|
|
22
|
+
export interface FacetBundlePlugin {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly version?: string;
|
|
25
|
+
}
|
|
26
|
+
/** One self-contained manifest entry suitable for storage or transport to another Node host. */
|
|
27
|
+
export interface FacetBundleArtifact {
|
|
28
|
+
readonly format: typeof FACET_BUNDLE_ARTIFACT_FORMAT;
|
|
29
|
+
readonly formatVersion: typeof FACET_BUNDLE_ARTIFACT_FORMAT_VERSION;
|
|
30
|
+
readonly plugin: FacetBundlePlugin;
|
|
31
|
+
readonly entryName: string;
|
|
32
|
+
readonly entry: FacetBundleEntry;
|
|
33
|
+
readonly source: string;
|
|
34
|
+
readonly sourceMapContents?: string;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/node/manifest.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AACxD,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAC9D,eAAO,MAAM,4BAA4B,gCAAgC,CAAC;AAC1E,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAChC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,MAAM,EAAE,OAAO,mBAAmB,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,OAAO,2BAA2B,CAAC;IAC3D,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,gGAAgG;AAChG,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,MAAM,EAAE,OAAO,4BAA4B,CAAC;IACrD,QAAQ,CAAC,aAAa,EAAE,OAAO,oCAAoC,CAAC;IACpE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC","sourcesContent":["export const FACET_BUNDLE_FORMAT = \"chord.facet-bundle\";\nexport const FACET_BUNDLE_FORMAT_VERSION = 2;\nexport const FACET_BUNDLE_MANIFEST_FILE = \"chord-facets.json\";\nexport const FACET_BUNDLE_ARTIFACT_FORMAT = \"chord.facet-bundle-artifact\";\nexport const FACET_BUNDLE_ARTIFACT_FORMAT_VERSION = 2;\n\nexport interface FacetBundleEntry {\n\t/** Content-addressed CommonJS filename relative to the manifest. */\n\treadonly file: string;\n\t/** SHA-256 subresource-integrity value for the JavaScript file. */\n\treadonly integrity: string;\n\t/** Imports intentionally left for the loading application to resolve. */\n\treadonly externalImports: readonly string[];\n\t/** Source map filename relative to the manifest, when emitted. */\n\treadonly sourceMap?: string;\n}\n\nexport interface FacetBundleManifest {\n\treadonly format: typeof FACET_BUNDLE_FORMAT;\n\treadonly formatVersion: typeof FACET_BUNDLE_FORMAT_VERSION;\n\treadonly plugin: FacetBundlePlugin;\n\treadonly entries: Readonly<Record<string, FacetBundleEntry>>;\n}\n\nexport interface FacetBundlePlugin {\n\treadonly id: string;\n\treadonly version?: string;\n}\n\n/** One self-contained manifest entry suitable for storage or transport to another Node host. */\nexport interface FacetBundleArtifact {\n\treadonly format: typeof FACET_BUNDLE_ARTIFACT_FORMAT;\n\treadonly formatVersion: typeof FACET_BUNDLE_ARTIFACT_FORMAT_VERSION;\n\treadonly plugin: FacetBundlePlugin;\n\treadonly entryName: string;\n\treadonly entry: FacetBundleEntry;\n\treadonly source: string;\n\treadonly sourceMapContents?: string;\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const FACET_BUNDLE_FORMAT = "chord.facet-bundle";
|
|
2
|
+
export const FACET_BUNDLE_FORMAT_VERSION = 2;
|
|
3
|
+
export const FACET_BUNDLE_MANIFEST_FILE = "chord-facets.json";
|
|
4
|
+
export const FACET_BUNDLE_ARTIFACT_FORMAT = "chord.facet-bundle-artifact";
|
|
5
|
+
export const FACET_BUNDLE_ARTIFACT_FORMAT_VERSION = 2;
|
|
6
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/node/manifest.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AACxD,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAC9D,MAAM,CAAC,MAAM,4BAA4B,GAAG,6BAA6B,CAAC;AAC1E,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,CAAC","sourcesContent":["export const FACET_BUNDLE_FORMAT = \"chord.facet-bundle\";\nexport const FACET_BUNDLE_FORMAT_VERSION = 2;\nexport const FACET_BUNDLE_MANIFEST_FILE = \"chord-facets.json\";\nexport const FACET_BUNDLE_ARTIFACT_FORMAT = \"chord.facet-bundle-artifact\";\nexport const FACET_BUNDLE_ARTIFACT_FORMAT_VERSION = 2;\n\nexport interface FacetBundleEntry {\n\t/** Content-addressed CommonJS filename relative to the manifest. */\n\treadonly file: string;\n\t/** SHA-256 subresource-integrity value for the JavaScript file. */\n\treadonly integrity: string;\n\t/** Imports intentionally left for the loading application to resolve. */\n\treadonly externalImports: readonly string[];\n\t/** Source map filename relative to the manifest, when emitted. */\n\treadonly sourceMap?: string;\n}\n\nexport interface FacetBundleManifest {\n\treadonly format: typeof FACET_BUNDLE_FORMAT;\n\treadonly formatVersion: typeof FACET_BUNDLE_FORMAT_VERSION;\n\treadonly plugin: FacetBundlePlugin;\n\treadonly entries: Readonly<Record<string, FacetBundleEntry>>;\n}\n\nexport interface FacetBundlePlugin {\n\treadonly id: string;\n\treadonly version?: string;\n}\n\n/** One self-contained manifest entry suitable for storage or transport to another Node host. */\nexport interface FacetBundleArtifact {\n\treadonly format: typeof FACET_BUNDLE_ARTIFACT_FORMAT;\n\treadonly formatVersion: typeof FACET_BUNDLE_ARTIFACT_FORMAT_VERSION;\n\treadonly plugin: FacetBundlePlugin;\n\treadonly entryName: string;\n\treadonly entry: FacetBundleEntry;\n\treadonly source: string;\n\treadonly sourceMapContents?: string;\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type BundleFacetsResult } from "./bundle.ts";
|
|
2
|
+
export interface BundleFacetPackageOptions {
|
|
3
|
+
/** Plugin package directory or its package.json path. */
|
|
4
|
+
readonly packagePath: string;
|
|
5
|
+
readonly outdir: string;
|
|
6
|
+
/** Application conventions applied when the corresponding source file exists. */
|
|
7
|
+
readonly defaultFacets?: Readonly<Record<string, string>>;
|
|
8
|
+
}
|
|
9
|
+
export interface BundleFacetPackageResult extends BundleFacetsResult {
|
|
10
|
+
readonly packageDirectory: string;
|
|
11
|
+
readonly packageJsonPath: string;
|
|
12
|
+
}
|
|
13
|
+
/** Build a plugin package using package.json metadata and application-provided facet conventions. */
|
|
14
|
+
export declare function bundleFacetPackage(options: BundleFacetPackageOptions): Promise<BundleFacetPackageResult>;
|
|
15
|
+
//# sourceMappingURL=package.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../src/node/package.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAgB,MAAM,aAAa,CAAC;AAEpE,MAAM,WAAW,yBAAyB;IACzC,yDAAyD;IACzD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB;IACnE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CACjC;AAaD,qGAAqG;AACrG,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAoB9G","sourcesContent":["import { readFile, realpath, stat } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { type BundleFacetsResult, bundleFacets } from \"./bundle.ts\";\n\nexport interface BundleFacetPackageOptions {\n\t/** Plugin package directory or its package.json path. */\n\treadonly packagePath: string;\n\treadonly outdir: string;\n\t/** Application conventions applied when the corresponding source file exists. */\n\treadonly defaultFacets?: Readonly<Record<string, string>>;\n}\n\nexport interface BundleFacetPackageResult extends BundleFacetsResult {\n\treadonly packageDirectory: string;\n\treadonly packageJsonPath: string;\n}\n\ninterface FacetPackageMetadata {\n\treadonly packageDirectory: string;\n\treadonly packageJsonPath: string;\n\treadonly name: string;\n\treadonly version: string;\n\treadonly peerDependencies: readonly string[];\n\treadonly configuredFacets: Readonly<Record<string, string | false>>;\n\treadonly external: readonly string[];\n\treadonly sourceMap: boolean;\n}\n\n/** Build a plugin package using package.json metadata and application-provided facet conventions. */\nexport async function bundleFacetPackage(options: BundleFacetPackageOptions): Promise<BundleFacetPackageResult> {\n\tconst metadata = await readFacetPackageMetadata(options.packagePath);\n\tconst entries = await resolveFacetEntries(metadata, options.defaultFacets ?? {});\n\tconst external = [...metadata.peerDependencies, ...metadata.external].flatMap((specifier) => [\n\t\tspecifier,\n\t\t`${specifier}/*`,\n\t]);\n\tconst result = await bundleFacets({\n\t\tplugin: { id: metadata.name, version: metadata.version },\n\t\tentries,\n\t\toutdir: options.outdir,\n\t\tworkingDirectory: metadata.packageDirectory,\n\t\texternal,\n\t\tsourceMap: metadata.sourceMap,\n\t});\n\treturn Object.freeze({\n\t\t...result,\n\t\tpackageDirectory: metadata.packageDirectory,\n\t\tpackageJsonPath: metadata.packageJsonPath,\n\t});\n}\n\nasync function readFacetPackageMetadata(packagePath: string): Promise<FacetPackageMetadata> {\n\tif (packagePath.length === 0) throw new TypeError(\"Facet package path must not be empty\");\n\tconst candidate = resolve(packagePath);\n\tlet packageDirectory: string;\n\tlet packageJsonPath: string;\n\tlet candidateStats: Awaited<ReturnType<typeof stat>>;\n\ttry {\n\t\tcandidateStats = await stat(candidate);\n\t} catch (error) {\n\t\tthrow new Error(`Could not access facet package ${candidate}`, { cause: error });\n\t}\n\tif (candidateStats.isDirectory()) {\n\t\tpackageDirectory = await realpath(candidate);\n\t\tpackageJsonPath = join(packageDirectory, \"package.json\");\n\t} else if (candidateStats.isFile() && basename(candidate) === \"package.json\") {\n\t\tpackageJsonPath = await realpath(candidate);\n\t\tpackageDirectory = dirname(packageJsonPath);\n\t} else {\n\t\tthrow new Error(`Facet package path must name a directory or package.json: ${candidate}`);\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(await readFile(packageJsonPath, \"utf8\"));\n\t} catch (error) {\n\t\tthrow new Error(`Could not read facet package metadata ${packageJsonPath}`, { cause: error });\n\t}\n\tif (!isRecord(parsed)) throw new Error(`Facet package metadata must be an object: ${packageJsonPath}`);\n\tif (typeof parsed.name !== \"string\" || parsed.name.length === 0) {\n\t\tthrow new Error(`Facet package must have a non-empty name: ${packageJsonPath}`);\n\t}\n\tif (typeof parsed.version !== \"string\" || parsed.version.length === 0) {\n\t\tthrow new Error(`Facet package must have a non-empty version: ${packageJsonPath}`);\n\t}\n\tconst peerDependencies = parsePeerDependencies(parsed.peerDependencies, packageJsonPath);\n\tconst chord = parseChordConfiguration(parsed.chord, packageJsonPath);\n\treturn {\n\t\tpackageDirectory,\n\t\tpackageJsonPath,\n\t\tname: parsed.name,\n\t\tversion: parsed.version,\n\t\tpeerDependencies,\n\t\tconfiguredFacets: chord.facets,\n\t\texternal: chord.external,\n\t\tsourceMap: chord.sourceMap,\n\t};\n}\n\nfunction parsePeerDependencies(value: unknown, packageJsonPath: string): readonly string[] {\n\tif (value === undefined) return [];\n\tif (\n\t\t!isRecord(value) ||\n\t\tObject.entries(value).some(([name, version]) => name.length === 0 || typeof version !== \"string\")\n\t) {\n\t\tthrow new Error(`Facet package has invalid peerDependencies: ${packageJsonPath}`);\n\t}\n\treturn Object.freeze(Object.keys(value).sort());\n}\n\nfunction parseChordConfiguration(\n\tvalue: unknown,\n\tpackageJsonPath: string,\n): {\n\treadonly facets: Readonly<Record<string, string | false>>;\n\treadonly external: readonly string[];\n\treadonly sourceMap: boolean;\n} {\n\tif (value === undefined) return { facets: Object.freeze({}), external: Object.freeze([]), sourceMap: true };\n\tif (!isRecord(value)) throw new Error(`Facet package chord configuration must be an object: ${packageJsonPath}`);\n\tif (Object.keys(value).some((key) => key !== \"facets\" && key !== \"external\" && key !== \"sourceMap\")) {\n\t\tthrow new Error(`Facet package chord configuration has an unknown field: ${packageJsonPath}`);\n\t}\n\tconst facets: Record<string, string | false> = {};\n\tif (value.facets !== undefined) {\n\t\tif (!isRecord(value.facets)) {\n\t\t\tthrow new Error(`Facet package chord.facets must be an object: ${packageJsonPath}`);\n\t\t}\n\t\tfor (const [name, source] of Object.entries(value.facets)) {\n\t\t\tif (name.length === 0 || (typeof source !== \"string\" && source !== false) || source === \"\") {\n\t\t\t\tthrow new Error(`Facet package has an invalid chord.facets entry: ${packageJsonPath}`);\n\t\t\t}\n\t\t\tfacets[name] = source;\n\t\t}\n\t}\n\tlet external: readonly string[] = [];\n\tif (value.external !== undefined) {\n\t\tif (\n\t\t\t!Array.isArray(value.external) ||\n\t\t\tvalue.external.some((specifier) => typeof specifier !== \"string\" || specifier.length === 0)\n\t\t) {\n\t\t\tthrow new Error(`Facet package chord.external must contain non-empty strings: ${packageJsonPath}`);\n\t\t}\n\t\texternal = Object.freeze([...new Set(value.external as readonly string[])].sort());\n\t}\n\tif (value.sourceMap !== undefined && typeof value.sourceMap !== \"boolean\") {\n\t\tthrow new Error(`Facet package chord.sourceMap must be a boolean: ${packageJsonPath}`);\n\t}\n\treturn {\n\t\tfacets: Object.freeze(facets),\n\t\texternal,\n\t\tsourceMap: value.sourceMap ?? true,\n\t};\n}\n\nasync function resolveFacetEntries(\n\tmetadata: FacetPackageMetadata,\n\tdefaultFacets: Readonly<Record<string, string>>,\n): Promise<Readonly<Record<string, string>>> {\n\tconst entries: Record<string, string> = {};\n\tfor (const [name, source] of Object.entries(defaultFacets)) {\n\t\tvalidateFacetMapping(name, source, \"default\");\n\t\tconst path = resolvePackageEntry(metadata.packageDirectory, source, name);\n\t\ttry {\n\t\t\tconst entryStats = await stat(path);\n\t\t\tif (!entryStats.isFile()) throw new Error(`Default facet entry ${name} is not a file: ${path}`);\n\t\t\tconst canonicalPath = await realpath(path);\n\t\t\tvalidateCanonicalPackageEntry(metadata.packageDirectory, canonicalPath, name);\n\t\t\tentries[name] = canonicalPath;\n\t\t} catch (error) {\n\t\t\tif (isMissingPath(error)) continue;\n\t\t\tthrow error;\n\t\t}\n\t}\n\tfor (const [name, source] of Object.entries(metadata.configuredFacets)) {\n\t\tif (source === false) {\n\t\t\tdelete entries[name];\n\t\t\tcontinue;\n\t\t}\n\t\tvalidateFacetMapping(name, source, \"configured\");\n\t\tconst path = resolvePackageEntry(metadata.packageDirectory, source, name);\n\t\tlet entryStats: Awaited<ReturnType<typeof stat>>;\n\t\ttry {\n\t\t\tentryStats = await stat(path);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Could not access configured facet entry ${name}: ${path}`, { cause: error });\n\t\t}\n\t\tif (!entryStats.isFile()) throw new Error(`Configured facet entry ${name} is not a file: ${path}`);\n\t\tconst canonicalPath = await realpath(path);\n\t\tvalidateCanonicalPackageEntry(metadata.packageDirectory, canonicalPath, name);\n\t\tentries[name] = canonicalPath;\n\t}\n\tif (Object.keys(entries).length === 0) {\n\t\tthrow new Error(`Facet package ${metadata.name} has no configured or conventional facet entries`);\n\t}\n\treturn Object.freeze(entries);\n}\n\nfunction validateFacetMapping(name: string, source: string, kind: string): void {\n\tif (name.length === 0) throw new Error(`Facet package ${kind} entry name must not be empty`);\n\tif (source.length === 0) throw new Error(`Facet package ${kind} entry ${name} must have a source path`);\n}\n\nfunction resolvePackageEntry(packageDirectory: string, source: string, name: string): string {\n\tif (isAbsolute(source)) throw new Error(`Facet package entry ${name} must be relative to the package directory`);\n\tconst path = resolve(packageDirectory, source);\n\tconst relativePath = relative(packageDirectory, path);\n\tif (\n\t\trelativePath.length === 0 ||\n\t\trelativePath === \"..\" ||\n\t\trelativePath.startsWith(`..${sep}`) ||\n\t\tisAbsolute(relativePath)\n\t) {\n\t\tthrow new Error(`Facet package entry ${name} escapes the package directory`);\n\t}\n\treturn path;\n}\n\nfunction validateCanonicalPackageEntry(packageDirectory: string, path: string, name: string): void {\n\tconst relativePath = relative(packageDirectory, path);\n\tif (relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {\n\t\tthrow new Error(`Facet package entry ${name} resolves outside the package directory`);\n\t}\n}\n\nfunction isMissingPath(error: unknown): boolean {\n\treturn isRecord(error) && error.code === \"ENOENT\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { bundleFacets } from "./bundle.js";
|
|
4
|
+
/** Build a plugin package using package.json metadata and application-provided facet conventions. */
|
|
5
|
+
export async function bundleFacetPackage(options) {
|
|
6
|
+
const metadata = await readFacetPackageMetadata(options.packagePath);
|
|
7
|
+
const entries = await resolveFacetEntries(metadata, options.defaultFacets ?? {});
|
|
8
|
+
const external = [...metadata.peerDependencies, ...metadata.external].flatMap((specifier) => [
|
|
9
|
+
specifier,
|
|
10
|
+
`${specifier}/*`,
|
|
11
|
+
]);
|
|
12
|
+
const result = await bundleFacets({
|
|
13
|
+
plugin: { id: metadata.name, version: metadata.version },
|
|
14
|
+
entries,
|
|
15
|
+
outdir: options.outdir,
|
|
16
|
+
workingDirectory: metadata.packageDirectory,
|
|
17
|
+
external,
|
|
18
|
+
sourceMap: metadata.sourceMap,
|
|
19
|
+
});
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
...result,
|
|
22
|
+
packageDirectory: metadata.packageDirectory,
|
|
23
|
+
packageJsonPath: metadata.packageJsonPath,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async function readFacetPackageMetadata(packagePath) {
|
|
27
|
+
if (packagePath.length === 0)
|
|
28
|
+
throw new TypeError("Facet package path must not be empty");
|
|
29
|
+
const candidate = resolve(packagePath);
|
|
30
|
+
let packageDirectory;
|
|
31
|
+
let packageJsonPath;
|
|
32
|
+
let candidateStats;
|
|
33
|
+
try {
|
|
34
|
+
candidateStats = await stat(candidate);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`Could not access facet package ${candidate}`, { cause: error });
|
|
38
|
+
}
|
|
39
|
+
if (candidateStats.isDirectory()) {
|
|
40
|
+
packageDirectory = await realpath(candidate);
|
|
41
|
+
packageJsonPath = join(packageDirectory, "package.json");
|
|
42
|
+
}
|
|
43
|
+
else if (candidateStats.isFile() && basename(candidate) === "package.json") {
|
|
44
|
+
packageJsonPath = await realpath(candidate);
|
|
45
|
+
packageDirectory = dirname(packageJsonPath);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw new Error(`Facet package path must name a directory or package.json: ${candidate}`);
|
|
49
|
+
}
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`Could not read facet package metadata ${packageJsonPath}`, { cause: error });
|
|
56
|
+
}
|
|
57
|
+
if (!isRecord(parsed))
|
|
58
|
+
throw new Error(`Facet package metadata must be an object: ${packageJsonPath}`);
|
|
59
|
+
if (typeof parsed.name !== "string" || parsed.name.length === 0) {
|
|
60
|
+
throw new Error(`Facet package must have a non-empty name: ${packageJsonPath}`);
|
|
61
|
+
}
|
|
62
|
+
if (typeof parsed.version !== "string" || parsed.version.length === 0) {
|
|
63
|
+
throw new Error(`Facet package must have a non-empty version: ${packageJsonPath}`);
|
|
64
|
+
}
|
|
65
|
+
const peerDependencies = parsePeerDependencies(parsed.peerDependencies, packageJsonPath);
|
|
66
|
+
const chord = parseChordConfiguration(parsed.chord, packageJsonPath);
|
|
67
|
+
return {
|
|
68
|
+
packageDirectory,
|
|
69
|
+
packageJsonPath,
|
|
70
|
+
name: parsed.name,
|
|
71
|
+
version: parsed.version,
|
|
72
|
+
peerDependencies,
|
|
73
|
+
configuredFacets: chord.facets,
|
|
74
|
+
external: chord.external,
|
|
75
|
+
sourceMap: chord.sourceMap,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function parsePeerDependencies(value, packageJsonPath) {
|
|
79
|
+
if (value === undefined)
|
|
80
|
+
return [];
|
|
81
|
+
if (!isRecord(value) ||
|
|
82
|
+
Object.entries(value).some(([name, version]) => name.length === 0 || typeof version !== "string")) {
|
|
83
|
+
throw new Error(`Facet package has invalid peerDependencies: ${packageJsonPath}`);
|
|
84
|
+
}
|
|
85
|
+
return Object.freeze(Object.keys(value).sort());
|
|
86
|
+
}
|
|
87
|
+
function parseChordConfiguration(value, packageJsonPath) {
|
|
88
|
+
if (value === undefined)
|
|
89
|
+
return { facets: Object.freeze({}), external: Object.freeze([]), sourceMap: true };
|
|
90
|
+
if (!isRecord(value))
|
|
91
|
+
throw new Error(`Facet package chord configuration must be an object: ${packageJsonPath}`);
|
|
92
|
+
if (Object.keys(value).some((key) => key !== "facets" && key !== "external" && key !== "sourceMap")) {
|
|
93
|
+
throw new Error(`Facet package chord configuration has an unknown field: ${packageJsonPath}`);
|
|
94
|
+
}
|
|
95
|
+
const facets = {};
|
|
96
|
+
if (value.facets !== undefined) {
|
|
97
|
+
if (!isRecord(value.facets)) {
|
|
98
|
+
throw new Error(`Facet package chord.facets must be an object: ${packageJsonPath}`);
|
|
99
|
+
}
|
|
100
|
+
for (const [name, source] of Object.entries(value.facets)) {
|
|
101
|
+
if (name.length === 0 || (typeof source !== "string" && source !== false) || source === "") {
|
|
102
|
+
throw new Error(`Facet package has an invalid chord.facets entry: ${packageJsonPath}`);
|
|
103
|
+
}
|
|
104
|
+
facets[name] = source;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
let external = [];
|
|
108
|
+
if (value.external !== undefined) {
|
|
109
|
+
if (!Array.isArray(value.external) ||
|
|
110
|
+
value.external.some((specifier) => typeof specifier !== "string" || specifier.length === 0)) {
|
|
111
|
+
throw new Error(`Facet package chord.external must contain non-empty strings: ${packageJsonPath}`);
|
|
112
|
+
}
|
|
113
|
+
external = Object.freeze([...new Set(value.external)].sort());
|
|
114
|
+
}
|
|
115
|
+
if (value.sourceMap !== undefined && typeof value.sourceMap !== "boolean") {
|
|
116
|
+
throw new Error(`Facet package chord.sourceMap must be a boolean: ${packageJsonPath}`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
facets: Object.freeze(facets),
|
|
120
|
+
external,
|
|
121
|
+
sourceMap: value.sourceMap ?? true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function resolveFacetEntries(metadata, defaultFacets) {
|
|
125
|
+
const entries = {};
|
|
126
|
+
for (const [name, source] of Object.entries(defaultFacets)) {
|
|
127
|
+
validateFacetMapping(name, source, "default");
|
|
128
|
+
const path = resolvePackageEntry(metadata.packageDirectory, source, name);
|
|
129
|
+
try {
|
|
130
|
+
const entryStats = await stat(path);
|
|
131
|
+
if (!entryStats.isFile())
|
|
132
|
+
throw new Error(`Default facet entry ${name} is not a file: ${path}`);
|
|
133
|
+
const canonicalPath = await realpath(path);
|
|
134
|
+
validateCanonicalPackageEntry(metadata.packageDirectory, canonicalPath, name);
|
|
135
|
+
entries[name] = canonicalPath;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (isMissingPath(error))
|
|
139
|
+
continue;
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const [name, source] of Object.entries(metadata.configuredFacets)) {
|
|
144
|
+
if (source === false) {
|
|
145
|
+
delete entries[name];
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
validateFacetMapping(name, source, "configured");
|
|
149
|
+
const path = resolvePackageEntry(metadata.packageDirectory, source, name);
|
|
150
|
+
let entryStats;
|
|
151
|
+
try {
|
|
152
|
+
entryStats = await stat(path);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
throw new Error(`Could not access configured facet entry ${name}: ${path}`, { cause: error });
|
|
156
|
+
}
|
|
157
|
+
if (!entryStats.isFile())
|
|
158
|
+
throw new Error(`Configured facet entry ${name} is not a file: ${path}`);
|
|
159
|
+
const canonicalPath = await realpath(path);
|
|
160
|
+
validateCanonicalPackageEntry(metadata.packageDirectory, canonicalPath, name);
|
|
161
|
+
entries[name] = canonicalPath;
|
|
162
|
+
}
|
|
163
|
+
if (Object.keys(entries).length === 0) {
|
|
164
|
+
throw new Error(`Facet package ${metadata.name} has no configured or conventional facet entries`);
|
|
165
|
+
}
|
|
166
|
+
return Object.freeze(entries);
|
|
167
|
+
}
|
|
168
|
+
function validateFacetMapping(name, source, kind) {
|
|
169
|
+
if (name.length === 0)
|
|
170
|
+
throw new Error(`Facet package ${kind} entry name must not be empty`);
|
|
171
|
+
if (source.length === 0)
|
|
172
|
+
throw new Error(`Facet package ${kind} entry ${name} must have a source path`);
|
|
173
|
+
}
|
|
174
|
+
function resolvePackageEntry(packageDirectory, source, name) {
|
|
175
|
+
if (isAbsolute(source))
|
|
176
|
+
throw new Error(`Facet package entry ${name} must be relative to the package directory`);
|
|
177
|
+
const path = resolve(packageDirectory, source);
|
|
178
|
+
const relativePath = relative(packageDirectory, path);
|
|
179
|
+
if (relativePath.length === 0 ||
|
|
180
|
+
relativePath === ".." ||
|
|
181
|
+
relativePath.startsWith(`..${sep}`) ||
|
|
182
|
+
isAbsolute(relativePath)) {
|
|
183
|
+
throw new Error(`Facet package entry ${name} escapes the package directory`);
|
|
184
|
+
}
|
|
185
|
+
return path;
|
|
186
|
+
}
|
|
187
|
+
function validateCanonicalPackageEntry(packageDirectory, path, name) {
|
|
188
|
+
const relativePath = relative(packageDirectory, path);
|
|
189
|
+
if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
190
|
+
throw new Error(`Facet package entry ${name} resolves outside the package directory`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function isMissingPath(error) {
|
|
194
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
195
|
+
}
|
|
196
|
+
function isRecord(value) {
|
|
197
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","sourceRoot":"","sources":["../../src/node/package.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, realpath, stat } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { type BundleFacetsResult, bundleFacets } from \"./bundle.ts\";\n\nexport interface BundleFacetPackageOptions {\n\t/** Plugin package directory or its package.json path. */\n\treadonly packagePath: string;\n\treadonly outdir: string;\n\t/** Application conventions applied when the corresponding source file exists. */\n\treadonly defaultFacets?: Readonly<Record<string, string>>;\n}\n\nexport interface BundleFacetPackageResult extends BundleFacetsResult {\n\treadonly packageDirectory: string;\n\treadonly packageJsonPath: string;\n}\n\ninterface FacetPackageMetadata {\n\treadonly packageDirectory: string;\n\treadonly packageJsonPath: string;\n\treadonly name: string;\n\treadonly version: string;\n\treadonly peerDependencies: readonly string[];\n\treadonly configuredFacets: Readonly<Record<string, string | false>>;\n\treadonly external: readonly string[];\n\treadonly sourceMap: boolean;\n}\n\n/** Build a plugin package using package.json metadata and application-provided facet conventions. */\nexport async function bundleFacetPackage(options: BundleFacetPackageOptions): Promise<BundleFacetPackageResult> {\n\tconst metadata = await readFacetPackageMetadata(options.packagePath);\n\tconst entries = await resolveFacetEntries(metadata, options.defaultFacets ?? {});\n\tconst external = [...metadata.peerDependencies, ...metadata.external].flatMap((specifier) => [\n\t\tspecifier,\n\t\t`${specifier}/*`,\n\t]);\n\tconst result = await bundleFacets({\n\t\tplugin: { id: metadata.name, version: metadata.version },\n\t\tentries,\n\t\toutdir: options.outdir,\n\t\tworkingDirectory: metadata.packageDirectory,\n\t\texternal,\n\t\tsourceMap: metadata.sourceMap,\n\t});\n\treturn Object.freeze({\n\t\t...result,\n\t\tpackageDirectory: metadata.packageDirectory,\n\t\tpackageJsonPath: metadata.packageJsonPath,\n\t});\n}\n\nasync function readFacetPackageMetadata(packagePath: string): Promise<FacetPackageMetadata> {\n\tif (packagePath.length === 0) throw new TypeError(\"Facet package path must not be empty\");\n\tconst candidate = resolve(packagePath);\n\tlet packageDirectory: string;\n\tlet packageJsonPath: string;\n\tlet candidateStats: Awaited<ReturnType<typeof stat>>;\n\ttry {\n\t\tcandidateStats = await stat(candidate);\n\t} catch (error) {\n\t\tthrow new Error(`Could not access facet package ${candidate}`, { cause: error });\n\t}\n\tif (candidateStats.isDirectory()) {\n\t\tpackageDirectory = await realpath(candidate);\n\t\tpackageJsonPath = join(packageDirectory, \"package.json\");\n\t} else if (candidateStats.isFile() && basename(candidate) === \"package.json\") {\n\t\tpackageJsonPath = await realpath(candidate);\n\t\tpackageDirectory = dirname(packageJsonPath);\n\t} else {\n\t\tthrow new Error(`Facet package path must name a directory or package.json: ${candidate}`);\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(await readFile(packageJsonPath, \"utf8\"));\n\t} catch (error) {\n\t\tthrow new Error(`Could not read facet package metadata ${packageJsonPath}`, { cause: error });\n\t}\n\tif (!isRecord(parsed)) throw new Error(`Facet package metadata must be an object: ${packageJsonPath}`);\n\tif (typeof parsed.name !== \"string\" || parsed.name.length === 0) {\n\t\tthrow new Error(`Facet package must have a non-empty name: ${packageJsonPath}`);\n\t}\n\tif (typeof parsed.version !== \"string\" || parsed.version.length === 0) {\n\t\tthrow new Error(`Facet package must have a non-empty version: ${packageJsonPath}`);\n\t}\n\tconst peerDependencies = parsePeerDependencies(parsed.peerDependencies, packageJsonPath);\n\tconst chord = parseChordConfiguration(parsed.chord, packageJsonPath);\n\treturn {\n\t\tpackageDirectory,\n\t\tpackageJsonPath,\n\t\tname: parsed.name,\n\t\tversion: parsed.version,\n\t\tpeerDependencies,\n\t\tconfiguredFacets: chord.facets,\n\t\texternal: chord.external,\n\t\tsourceMap: chord.sourceMap,\n\t};\n}\n\nfunction parsePeerDependencies(value: unknown, packageJsonPath: string): readonly string[] {\n\tif (value === undefined) return [];\n\tif (\n\t\t!isRecord(value) ||\n\t\tObject.entries(value).some(([name, version]) => name.length === 0 || typeof version !== \"string\")\n\t) {\n\t\tthrow new Error(`Facet package has invalid peerDependencies: ${packageJsonPath}`);\n\t}\n\treturn Object.freeze(Object.keys(value).sort());\n}\n\nfunction parseChordConfiguration(\n\tvalue: unknown,\n\tpackageJsonPath: string,\n): {\n\treadonly facets: Readonly<Record<string, string | false>>;\n\treadonly external: readonly string[];\n\treadonly sourceMap: boolean;\n} {\n\tif (value === undefined) return { facets: Object.freeze({}), external: Object.freeze([]), sourceMap: true };\n\tif (!isRecord(value)) throw new Error(`Facet package chord configuration must be an object: ${packageJsonPath}`);\n\tif (Object.keys(value).some((key) => key !== \"facets\" && key !== \"external\" && key !== \"sourceMap\")) {\n\t\tthrow new Error(`Facet package chord configuration has an unknown field: ${packageJsonPath}`);\n\t}\n\tconst facets: Record<string, string | false> = {};\n\tif (value.facets !== undefined) {\n\t\tif (!isRecord(value.facets)) {\n\t\t\tthrow new Error(`Facet package chord.facets must be an object: ${packageJsonPath}`);\n\t\t}\n\t\tfor (const [name, source] of Object.entries(value.facets)) {\n\t\t\tif (name.length === 0 || (typeof source !== \"string\" && source !== false) || source === \"\") {\n\t\t\t\tthrow new Error(`Facet package has an invalid chord.facets entry: ${packageJsonPath}`);\n\t\t\t}\n\t\t\tfacets[name] = source;\n\t\t}\n\t}\n\tlet external: readonly string[] = [];\n\tif (value.external !== undefined) {\n\t\tif (\n\t\t\t!Array.isArray(value.external) ||\n\t\t\tvalue.external.some((specifier) => typeof specifier !== \"string\" || specifier.length === 0)\n\t\t) {\n\t\t\tthrow new Error(`Facet package chord.external must contain non-empty strings: ${packageJsonPath}`);\n\t\t}\n\t\texternal = Object.freeze([...new Set(value.external as readonly string[])].sort());\n\t}\n\tif (value.sourceMap !== undefined && typeof value.sourceMap !== \"boolean\") {\n\t\tthrow new Error(`Facet package chord.sourceMap must be a boolean: ${packageJsonPath}`);\n\t}\n\treturn {\n\t\tfacets: Object.freeze(facets),\n\t\texternal,\n\t\tsourceMap: value.sourceMap ?? true,\n\t};\n}\n\nasync function resolveFacetEntries(\n\tmetadata: FacetPackageMetadata,\n\tdefaultFacets: Readonly<Record<string, string>>,\n): Promise<Readonly<Record<string, string>>> {\n\tconst entries: Record<string, string> = {};\n\tfor (const [name, source] of Object.entries(defaultFacets)) {\n\t\tvalidateFacetMapping(name, source, \"default\");\n\t\tconst path = resolvePackageEntry(metadata.packageDirectory, source, name);\n\t\ttry {\n\t\t\tconst entryStats = await stat(path);\n\t\t\tif (!entryStats.isFile()) throw new Error(`Default facet entry ${name} is not a file: ${path}`);\n\t\t\tconst canonicalPath = await realpath(path);\n\t\t\tvalidateCanonicalPackageEntry(metadata.packageDirectory, canonicalPath, name);\n\t\t\tentries[name] = canonicalPath;\n\t\t} catch (error) {\n\t\t\tif (isMissingPath(error)) continue;\n\t\t\tthrow error;\n\t\t}\n\t}\n\tfor (const [name, source] of Object.entries(metadata.configuredFacets)) {\n\t\tif (source === false) {\n\t\t\tdelete entries[name];\n\t\t\tcontinue;\n\t\t}\n\t\tvalidateFacetMapping(name, source, \"configured\");\n\t\tconst path = resolvePackageEntry(metadata.packageDirectory, source, name);\n\t\tlet entryStats: Awaited<ReturnType<typeof stat>>;\n\t\ttry {\n\t\t\tentryStats = await stat(path);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Could not access configured facet entry ${name}: ${path}`, { cause: error });\n\t\t}\n\t\tif (!entryStats.isFile()) throw new Error(`Configured facet entry ${name} is not a file: ${path}`);\n\t\tconst canonicalPath = await realpath(path);\n\t\tvalidateCanonicalPackageEntry(metadata.packageDirectory, canonicalPath, name);\n\t\tentries[name] = canonicalPath;\n\t}\n\tif (Object.keys(entries).length === 0) {\n\t\tthrow new Error(`Facet package ${metadata.name} has no configured or conventional facet entries`);\n\t}\n\treturn Object.freeze(entries);\n}\n\nfunction validateFacetMapping(name: string, source: string, kind: string): void {\n\tif (name.length === 0) throw new Error(`Facet package ${kind} entry name must not be empty`);\n\tif (source.length === 0) throw new Error(`Facet package ${kind} entry ${name} must have a source path`);\n}\n\nfunction resolvePackageEntry(packageDirectory: string, source: string, name: string): string {\n\tif (isAbsolute(source)) throw new Error(`Facet package entry ${name} must be relative to the package directory`);\n\tconst path = resolve(packageDirectory, source);\n\tconst relativePath = relative(packageDirectory, path);\n\tif (\n\t\trelativePath.length === 0 ||\n\t\trelativePath === \"..\" ||\n\t\trelativePath.startsWith(`..${sep}`) ||\n\t\tisAbsolute(relativePath)\n\t) {\n\t\tthrow new Error(`Facet package entry ${name} escapes the package directory`);\n\t}\n\treturn path;\n}\n\nfunction validateCanonicalPackageEntry(packageDirectory: string, path: string, name: string): void {\n\tconst relativePath = relative(packageDirectory, path);\n\tif (relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {\n\t\tthrow new Error(`Facet package entry ${name} resolves outside the package directory`);\n\t}\n}\n\nfunction isMissingPath(error: unknown): boolean {\n\treturn isRecord(error) && error.code === \"ENOENT\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { FacetBundleArtifactLoaderOptions, FacetBundleExternalResolver, FacetBundleLoaderOptions, } from "./node/bundle-loader.ts";
|
|
2
|
+
export { createFacetBundleArtifactLoader, createFacetBundleLoader, readFacetBundleArtifact, readFacetBundleManifest, } from "./node/bundle-loader.ts";
|
|
3
|
+
export type { FacetBundleArtifact, FacetBundleEntry, FacetBundleManifest, FacetBundlePlugin, } from "./node/manifest.ts";
|
|
4
|
+
export { FACET_BUNDLE_ARTIFACT_FORMAT, FACET_BUNDLE_ARTIFACT_FORMAT_VERSION, FACET_BUNDLE_FORMAT, FACET_BUNDLE_FORMAT_VERSION, FACET_BUNDLE_MANIFEST_FILE, } from "./node/manifest.ts";
|
|
5
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,gCAAgC,EAChC,2BAA2B,EAC3B,wBAAwB,GACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,+BAA+B,EAC/B,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,4BAA4B,EAC5B,oCAAoC,EACpC,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC","sourcesContent":["export type {\n\tFacetBundleArtifactLoaderOptions,\n\tFacetBundleExternalResolver,\n\tFacetBundleLoaderOptions,\n} from \"./node/bundle-loader.ts\";\nexport {\n\tcreateFacetBundleArtifactLoader,\n\tcreateFacetBundleLoader,\n\treadFacetBundleArtifact,\n\treadFacetBundleManifest,\n} from \"./node/bundle-loader.ts\";\nexport type {\n\tFacetBundleArtifact,\n\tFacetBundleEntry,\n\tFacetBundleManifest,\n\tFacetBundlePlugin,\n} from \"./node/manifest.ts\";\nexport {\n\tFACET_BUNDLE_ARTIFACT_FORMAT,\n\tFACET_BUNDLE_ARTIFACT_FORMAT_VERSION,\n\tFACET_BUNDLE_FORMAT,\n\tFACET_BUNDLE_FORMAT_VERSION,\n\tFACET_BUNDLE_MANIFEST_FILE,\n} from \"./node/manifest.ts\";\n"]}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createFacetBundleArtifactLoader, createFacetBundleLoader, readFacetBundleArtifact, readFacetBundleManifest, } from "./node/bundle-loader.js";
|
|
2
|
+
export { FACET_BUNDLE_ARTIFACT_FORMAT, FACET_BUNDLE_ARTIFACT_FORMAT_VERSION, FACET_BUNDLE_FORMAT, FACET_BUNDLE_FORMAT_VERSION, FACET_BUNDLE_MANIFEST_FILE, } from "./node/manifest.js";
|
|
3
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAKA,OAAO,EACN,+BAA+B,EAC/B,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,yBAAyB,CAAC;AAOjC,OAAO,EACN,4BAA4B,EAC5B,oCAAoC,EACpC,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC","sourcesContent":["export type {\n\tFacetBundleArtifactLoaderOptions,\n\tFacetBundleExternalResolver,\n\tFacetBundleLoaderOptions,\n} from \"./node/bundle-loader.ts\";\nexport {\n\tcreateFacetBundleArtifactLoader,\n\tcreateFacetBundleLoader,\n\treadFacetBundleArtifact,\n\treadFacetBundleManifest,\n} from \"./node/bundle-loader.ts\";\nexport type {\n\tFacetBundleArtifact,\n\tFacetBundleEntry,\n\tFacetBundleManifest,\n\tFacetBundlePlugin,\n} from \"./node/manifest.ts\";\nexport {\n\tFACET_BUNDLE_ARTIFACT_FORMAT,\n\tFACET_BUNDLE_ARTIFACT_FORMAT_VERSION,\n\tFACET_BUNDLE_FORMAT,\n\tFACET_BUNDLE_FORMAT_VERSION,\n\tFACET_BUNDLE_MANIFEST_FILE,\n} from \"./node/manifest.ts\";\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context, RemoteServiceBinding, RemoteServiceBindingOptions, Service } from "../types.ts";
|
|
2
|
+
export declare class RemoteServiceBindingImpl implements RemoteServiceBinding {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options: RemoteServiceBindingOptions);
|
|
5
|
+
use<T>(service: Service<T>): T;
|
|
6
|
+
observe<T>(service: Service<T>, handler: (service: T, context: Context) => void | Promise<void>): () => void;
|
|
7
|
+
ready(context: Context): Promise<void>;
|
|
8
|
+
rebind(bound: boolean, context: Context): Promise<void>;
|
|
9
|
+
dispose(context: Context): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=consumer.d.ts.map
|