@markii/pack 0.16.0 → 0.17.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/archive.d.ts +2 -0
- package/dist/archive.js +10 -2
- package/dist/components.d.ts +52 -4
- package/dist/components.js +98 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/manifest.js +52 -5
- package/package.json +2 -2
package/dist/archive.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface PackArchiveContents {
|
|
|
41
41
|
readonly scriptBytes: Uint8Array;
|
|
42
42
|
/** The prebuilt stylesheet's bytes (`webview.css`), present only when the pack ships one. */
|
|
43
43
|
readonly stylesheetBytes?: Uint8Array;
|
|
44
|
+
/** The prebuilt terminal registration module (`terminal.js`), present only when the pack ships terminal views. Hosts that run no terminal engine ignore it. */
|
|
45
|
+
readonly terminalScriptBytes?: Uint8Array;
|
|
44
46
|
/** Shared Lua modules under `scripts/`, keyed by their path relative to that folder (e.g. `"http.lua"` for `scripts/http.lua`). Empty when the pack ships none. */
|
|
45
47
|
readonly scriptModules: Readonly<Record<string, Uint8Array>>;
|
|
46
48
|
/**
|
package/dist/archive.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `.mkp` pack archives (docs/packs.md, issue #16): a zip of a pack's
|
|
3
3
|
* PREBUILT form, files at the zip root — `pack.json`, `webview.js`,
|
|
4
|
-
* `webview.css` when the pack has styles, `scripts/` when the pack ships
|
|
4
|
+
* `webview.css` when the pack has styles, `terminal.js` when it ships terminal views, `scripts/` when the pack ships
|
|
5
5
|
* shared Lua. This is the reader half: given the raw bytes of a `.mkp`
|
|
6
6
|
* file, validate it and expose its contents for read-only loading.
|
|
7
7
|
*
|
|
@@ -37,6 +37,8 @@ import { parsePackManifest } from './manifest.js';
|
|
|
37
37
|
const MANIFEST_ENTRY = 'pack.json';
|
|
38
38
|
const SCRIPT_ENTRY = 'webview.js';
|
|
39
39
|
const STYLESHEET_ENTRY = 'webview.css';
|
|
40
|
+
/** The optional terminal registration module (docs/packs.md). A pack with no terminal views has none. */
|
|
41
|
+
const TERMINAL_ENTRY = 'terminal.js';
|
|
40
42
|
const SCRIPTS_DIR_PREFIX = 'scripts/';
|
|
41
43
|
/** macOS's zip metadata folder — never a real pack file, and never counted toward "is this archive a single wrapped folder" detection below (batch 7 #61). */
|
|
42
44
|
const MACOSX_PREFIX = '__MACOSX/';
|
|
@@ -198,12 +200,17 @@ export async function openPackArchive(bytes, options = {}) {
|
|
|
198
200
|
if (paths.includes(STYLESHEET_ENTRY)) {
|
|
199
201
|
stylesheetBytes = await storage.read(STYLESHEET_ENTRY);
|
|
200
202
|
}
|
|
203
|
+
let terminalScriptBytes;
|
|
204
|
+
if (paths.includes(TERMINAL_ENTRY)) {
|
|
205
|
+
terminalScriptBytes = await storage.read(TERMINAL_ENTRY);
|
|
206
|
+
}
|
|
201
207
|
const scriptModules = Object.create(null);
|
|
202
208
|
const ignoredEntries = [];
|
|
203
209
|
for (const path of paths) {
|
|
204
210
|
if (path === MANIFEST_ENTRY ||
|
|
205
211
|
path === SCRIPT_ENTRY ||
|
|
206
|
-
path === STYLESHEET_ENTRY
|
|
212
|
+
path === STYLESHEET_ENTRY ||
|
|
213
|
+
path === TERMINAL_ENTRY) {
|
|
207
214
|
continue;
|
|
208
215
|
}
|
|
209
216
|
if (isScriptModulePath(path)) {
|
|
@@ -222,6 +229,7 @@ export async function openPackArchive(bytes, options = {}) {
|
|
|
222
229
|
manifestWarnings: parsed.warnings,
|
|
223
230
|
scriptBytes,
|
|
224
231
|
...(stylesheetBytes !== undefined ? { stylesheetBytes } : {}),
|
|
232
|
+
...(terminalScriptBytes !== undefined ? { terminalScriptBytes } : {}),
|
|
225
233
|
scriptModules,
|
|
226
234
|
ignoredEntries,
|
|
227
235
|
},
|
package/dist/components.d.ts
CHANGED
|
@@ -71,7 +71,14 @@ export interface PackComponentAttribute {
|
|
|
71
71
|
* hover documentation.
|
|
72
72
|
*/
|
|
73
73
|
export interface PackComponentDefinition {
|
|
74
|
-
source
|
|
74
|
+
/** The source for the pack's top-level `engine`. Mutually exclusive with `sources`. */
|
|
75
|
+
source?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Engine id -> pack-relative source path, for a component that ships
|
|
78
|
+
* more than one implementation (for example `react` and `ansi`).
|
|
79
|
+
* Mutually exclusive with `source`.
|
|
80
|
+
*/
|
|
81
|
+
sources?: Readonly<Record<string, string>>;
|
|
75
82
|
description?: string;
|
|
76
83
|
kind?: PackComponentKind;
|
|
77
84
|
/**
|
|
@@ -93,7 +100,13 @@ export type PackComponentEntry = string | PackComponentDefinition;
|
|
|
93
100
|
* the manifest declared them (the string shorthand never has any of them).
|
|
94
101
|
*/
|
|
95
102
|
export interface ResolvedPackComponent {
|
|
96
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Engine id -> pack-relative source path, normalized from all three
|
|
105
|
+
* declared forms (string shorthand and `source` are keyed under the
|
|
106
|
+
* manifest's `engine`). Read it through `componentSourceForEngine`.
|
|
107
|
+
* Never empty. A null-prototype object.
|
|
108
|
+
*/
|
|
109
|
+
readonly sources: Readonly<Record<string, string>>;
|
|
97
110
|
readonly description?: string;
|
|
98
111
|
readonly kind?: PackComponentKind;
|
|
99
112
|
readonly attributes?: readonly PackComponentAttribute[];
|
|
@@ -102,6 +115,38 @@ export interface ResolvedPackComponent {
|
|
|
102
115
|
export interface PackComponentListing extends ResolvedPackComponent {
|
|
103
116
|
readonly localName: string;
|
|
104
117
|
}
|
|
118
|
+
/** A listing narrowed to one engine: `source` is that engine's source path. */
|
|
119
|
+
export interface EnginePackComponentListing extends PackComponentListing {
|
|
120
|
+
readonly source: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The pack-relative source this component has for `engine`, or `undefined`
|
|
124
|
+
* when it ships none. The one answer to "does this component run here".
|
|
125
|
+
*/
|
|
126
|
+
export declare function componentSourceForEngine(component: Pick<ResolvedPackComponent, 'sources'>, engine: string): string | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Whether a host for `engine` should install the pack: true when the pack
|
|
129
|
+
* declares no components (Lua-only) or at least one component has a source
|
|
130
|
+
* for `engine`. A pack with components but none for this engine is not
|
|
131
|
+
* installable here and a host reports it as dropped.
|
|
132
|
+
*/
|
|
133
|
+
export declare function packSupportsEngine(manifest: Pick<PackManifest, 'components' | 'engine'>, engine: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* The pack's components that have a source for `engine`, in declaration
|
|
136
|
+
* order, each carrying that engine's `source`. Components without one are
|
|
137
|
+
* absent (they fall back to the unknown-component box in that engine).
|
|
138
|
+
*/
|
|
139
|
+
export declare function packComponentsForEngine(manifest: Pick<PackManifest, 'components' | 'engine'>, engine: string): EnginePackComponentListing[];
|
|
140
|
+
/**
|
|
141
|
+
* Every engine id any component of the pack ships a source for, sorted,
|
|
142
|
+
* comma-joined for a "dropped pack" report. Falls back to the manifest's
|
|
143
|
+
* top-level `engine` when the pack declares no components.
|
|
144
|
+
*/
|
|
145
|
+
export declare function packDeclaredEngines(manifest: Pick<PackManifest, 'components' | 'engine'>): string;
|
|
146
|
+
/** Engine ids are lowercase words: a leading letter, then lowercase letters, digits and hyphens. */
|
|
147
|
+
export declare const PACK_ENGINE_ID_PATTERN: RegExp;
|
|
148
|
+
/** Whether `id` is a usable engine id (a `sources` key). */
|
|
149
|
+
export declare function isPackEngineId(id: unknown): id is string;
|
|
105
150
|
/**
|
|
106
151
|
* Normalizes a single `components` entry, string or object form, into the
|
|
107
152
|
* common `ResolvedPackComponent` shape. This is THE accessor for reading
|
|
@@ -116,8 +161,11 @@ export interface PackComponentListing extends ResolvedPackComponent {
|
|
|
116
161
|
* a value. This matters because a consumer may call this directly on
|
|
117
162
|
* attacker-controlled JSON that never went through `parsePackManifest` at
|
|
118
163
|
* all (for example a pack loaded from an untrusted bundle).
|
|
164
|
+
*
|
|
165
|
+
* `defaultEngine` is the manifest's top-level `engine`: the engine the
|
|
166
|
+
* string shorthand and an object entry's `source` are written for.
|
|
119
167
|
*/
|
|
120
|
-
export declare function resolvePackComponent(entry: unknown): ResolvedPackComponent | undefined;
|
|
168
|
+
export declare function resolvePackComponent(entry: unknown, defaultEngine: string): ResolvedPackComponent | undefined;
|
|
121
169
|
/**
|
|
122
170
|
* THE iterator every consumer uses instead of walking `manifest.components`
|
|
123
171
|
* directly: `Object.hasOwn`-guarded, in declaration key order, skipping any
|
|
@@ -131,4 +179,4 @@ export declare function resolvePackComponent(entry: unknown): ResolvedPackCompon
|
|
|
131
179
|
* object, or an array: all of those return `[]` rather than throwing, so a
|
|
132
180
|
* caller never needs to guard the call site itself.
|
|
133
181
|
*/
|
|
134
|
-
export declare function packComponents(manifest: Pick<PackManifest, 'components'>): PackComponentListing[];
|
|
182
|
+
export declare function packComponents(manifest: Pick<PackManifest, 'components' | 'engine'>): PackComponentListing[];
|
package/dist/components.js
CHANGED
|
@@ -29,6 +29,92 @@ export const PACK_ATTRIBUTE_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
|
29
29
|
export function isPackAttributeName(name) {
|
|
30
30
|
return typeof name === 'string' && PACK_ATTRIBUTE_NAME_PATTERN.test(name);
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* The pack-relative source this component has for `engine`, or `undefined`
|
|
34
|
+
* when it ships none. The one answer to "does this component run here".
|
|
35
|
+
*/
|
|
36
|
+
export function componentSourceForEngine(component, engine) {
|
|
37
|
+
return Object.hasOwn(component.sources, engine)
|
|
38
|
+
? component.sources[engine]
|
|
39
|
+
: undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Whether a host for `engine` should install the pack: true when the pack
|
|
43
|
+
* declares no components (Lua-only) or at least one component has a source
|
|
44
|
+
* for `engine`. A pack with components but none for this engine is not
|
|
45
|
+
* installable here and a host reports it as dropped.
|
|
46
|
+
*/
|
|
47
|
+
export function packSupportsEngine(manifest, engine) {
|
|
48
|
+
const all = packComponents(manifest);
|
|
49
|
+
if (all.length === 0)
|
|
50
|
+
return true;
|
|
51
|
+
return all.some((c) => componentSourceForEngine(c, engine) !== undefined);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The pack's components that have a source for `engine`, in declaration
|
|
55
|
+
* order, each carrying that engine's `source`. Components without one are
|
|
56
|
+
* absent (they fall back to the unknown-component box in that engine).
|
|
57
|
+
*/
|
|
58
|
+
export function packComponentsForEngine(manifest, engine) {
|
|
59
|
+
const result = [];
|
|
60
|
+
for (const listing of packComponents(manifest)) {
|
|
61
|
+
const source = componentSourceForEngine(listing, engine);
|
|
62
|
+
if (source !== undefined)
|
|
63
|
+
result.push({ ...listing, source });
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Every engine id any component of the pack ships a source for, sorted,
|
|
69
|
+
* comma-joined for a "dropped pack" report. Falls back to the manifest's
|
|
70
|
+
* top-level `engine` when the pack declares no components.
|
|
71
|
+
*/
|
|
72
|
+
export function packDeclaredEngines(manifest) {
|
|
73
|
+
const ids = new Set();
|
|
74
|
+
for (const listing of packComponents(manifest)) {
|
|
75
|
+
for (const id of Object.keys(listing.sources))
|
|
76
|
+
ids.add(id);
|
|
77
|
+
}
|
|
78
|
+
if (ids.size === 0)
|
|
79
|
+
ids.add(manifest.engine);
|
|
80
|
+
return [...ids].sort().join(', ');
|
|
81
|
+
}
|
|
82
|
+
/** Engine ids are lowercase words: a leading letter, then lowercase letters, digits and hyphens. */
|
|
83
|
+
export const PACK_ENGINE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
84
|
+
/** Whether `id` is a usable engine id (a `sources` key). */
|
|
85
|
+
export function isPackEngineId(id) {
|
|
86
|
+
return typeof id === 'string' && PACK_ENGINE_ID_PATTERN.test(id);
|
|
87
|
+
}
|
|
88
|
+
function nullProto(entries) {
|
|
89
|
+
const out = Object.create(null);
|
|
90
|
+
for (const [k, v] of entries)
|
|
91
|
+
out[k] = v;
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
/** Reads the engine-to-source map of an object entry, leniently: bad pairs are dropped. */
|
|
95
|
+
function resolveSources(entry, defaultEngine) {
|
|
96
|
+
const source = ownProperty(entry, 'source');
|
|
97
|
+
const sources = ownProperty(entry, 'sources');
|
|
98
|
+
if (typeof source === 'string' && source.length > 0) {
|
|
99
|
+
if (sources !== undefined)
|
|
100
|
+
return undefined;
|
|
101
|
+
return nullProto([[defaultEngine, source]]);
|
|
102
|
+
}
|
|
103
|
+
if (sources === null ||
|
|
104
|
+
typeof sources !== 'object' ||
|
|
105
|
+
Array.isArray(sources)) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const pairs = [];
|
|
109
|
+
for (const key of Object.keys(sources)) {
|
|
110
|
+
if (!Object.hasOwn(sources, key) || !isPackEngineId(key))
|
|
111
|
+
continue;
|
|
112
|
+
const value = sources[key];
|
|
113
|
+
if (typeof value === 'string' && value.length > 0)
|
|
114
|
+
pairs.push([key, value]);
|
|
115
|
+
}
|
|
116
|
+
return pairs.length > 0 ? nullProto(pairs) : undefined;
|
|
117
|
+
}
|
|
32
118
|
/**
|
|
33
119
|
* Reads one own property off an already-narrowed object, or `undefined`
|
|
34
120
|
* when it is absent. `Object.hasOwn` only, matching this package's rule
|
|
@@ -112,20 +198,24 @@ function resolveAttributes(raw) {
|
|
|
112
198
|
* a value. This matters because a consumer may call this directly on
|
|
113
199
|
* attacker-controlled JSON that never went through `parsePackManifest` at
|
|
114
200
|
* all (for example a pack loaded from an untrusted bundle).
|
|
201
|
+
*
|
|
202
|
+
* `defaultEngine` is the manifest's top-level `engine`: the engine the
|
|
203
|
+
* string shorthand and an object entry's `source` are written for.
|
|
115
204
|
*/
|
|
116
|
-
export function resolvePackComponent(entry) {
|
|
205
|
+
export function resolvePackComponent(entry, defaultEngine) {
|
|
117
206
|
if (typeof entry === 'string') {
|
|
118
|
-
return entry.length > 0
|
|
207
|
+
return entry.length > 0
|
|
208
|
+
? { sources: nullProto([[defaultEngine, entry]]) }
|
|
209
|
+
: undefined;
|
|
119
210
|
}
|
|
120
211
|
if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
121
212
|
return undefined;
|
|
122
213
|
}
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
214
|
+
const sources = resolveSources(entry, defaultEngine);
|
|
215
|
+
if (sources === undefined)
|
|
125
216
|
return undefined;
|
|
126
|
-
}
|
|
127
217
|
const result = {
|
|
128
|
-
|
|
218
|
+
sources,
|
|
129
219
|
};
|
|
130
220
|
const description = ownProperty(entry, 'description');
|
|
131
221
|
if (typeof description === 'string' && description.length > 0) {
|
|
@@ -156,6 +246,7 @@ export function resolvePackComponent(entry) {
|
|
|
156
246
|
*/
|
|
157
247
|
export function packComponents(manifest) {
|
|
158
248
|
const components = manifest?.components;
|
|
249
|
+
const engine = manifest?.engine;
|
|
159
250
|
if (components === null ||
|
|
160
251
|
typeof components !== 'object' ||
|
|
161
252
|
Array.isArray(components)) {
|
|
@@ -165,7 +256,7 @@ export function packComponents(manifest) {
|
|
|
165
256
|
for (const localName of Object.keys(components)) {
|
|
166
257
|
if (!Object.hasOwn(components, localName))
|
|
167
258
|
continue;
|
|
168
|
-
const resolved = resolvePackComponent(components[localName]);
|
|
259
|
+
const resolved = resolvePackComponent(components[localName], typeof engine === 'string' ? engine : '');
|
|
169
260
|
if (resolved === undefined)
|
|
170
261
|
continue;
|
|
171
262
|
listings.push({ localName, ...resolved });
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export type { PackManifest, PackManifestParseResult } from './manifest.js';
|
|
|
2
2
|
export { parsePackManifest } from './manifest.js';
|
|
3
3
|
export type { OpenPackArchiveOptions, OpenPackArchiveResult, PackArchiveContents, PackArchiveError, } from './archive.js';
|
|
4
4
|
export { openPackArchive, packArchiveFileName } from './archive.js';
|
|
5
|
-
export type { PackComponentAttribute, PackComponentDefinition, PackComponentEntry, PackComponentKind, PackComponentListing, ResolvedPackComponent, } from './components.js';
|
|
6
|
-
export { PACK_ATTRIBUTE_NAME_PATTERN, PACK_COMPONENT_KINDS, isPackAttributeName, packComponents, resolvePackComponent, } from './components.js';
|
|
5
|
+
export type { PackComponentAttribute, PackComponentDefinition, PackComponentEntry, PackComponentKind, EnginePackComponentListing, PackComponentListing, ResolvedPackComponent, } from './components.js';
|
|
6
|
+
export { PACK_ATTRIBUTE_NAME_PATTERN, PACK_COMPONENT_KINDS, PACK_ENGINE_ID_PATTERN, componentSourceForEngine, isPackAttributeName, isPackEngineId, packComponents, packComponentsForEngine, packDeclaredEngines, packSupportsEngine, resolvePackComponent, } from './components.js';
|
|
7
7
|
export type { ComposeDirectiveNameResult, NamespaceCollision, NamespaceValidationResult, } from './namespace.js';
|
|
8
8
|
export { RESERVED_NAMESPACE_SEGMENTS, composeDirectiveName, detectNamespaceCollisions, validateLocalComponentName, validatePackName, } from './namespace.js';
|
|
9
9
|
export type { UsesResolution } from './uses.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
// out, touching no real filesystem itself (a host does that).
|
|
8
8
|
export { parsePackManifest } from './manifest.js';
|
|
9
9
|
export { openPackArchive, packArchiveFileName } from './archive.js';
|
|
10
|
-
export { PACK_ATTRIBUTE_NAME_PATTERN, PACK_COMPONENT_KINDS, isPackAttributeName, packComponents, resolvePackComponent, } from './components.js';
|
|
10
|
+
export { PACK_ATTRIBUTE_NAME_PATTERN, PACK_COMPONENT_KINDS, PACK_ENGINE_ID_PATTERN, componentSourceForEngine, isPackAttributeName, isPackEngineId, packComponents, packComponentsForEngine, packDeclaredEngines, packSupportsEngine, resolvePackComponent, } from './components.js';
|
|
11
11
|
export { RESERVED_NAMESPACE_SEGMENTS, composeDirectiveName, detectNamespaceCollisions, validateLocalComponentName, validatePackName, } from './namespace.js';
|
|
12
12
|
export { isValidPackNameShape, resolveUses } from './uses.js';
|
package/dist/manifest.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { validateLocalComponentName, validatePackName } from './namespace.js';
|
|
2
|
-
import { PACK_ATTRIBUTE_NAME_PATTERN, PACK_COMPONENT_KINDS, isPackAttributeName, } from './components.js';
|
|
2
|
+
import { PACK_ATTRIBUTE_NAME_PATTERN, PACK_COMPONENT_KINDS, PACK_ENGINE_ID_PATTERN, isPackAttributeName, isPackEngineId, } from './components.js';
|
|
3
3
|
const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
4
4
|
'name',
|
|
5
5
|
'engine',
|
|
@@ -16,6 +16,7 @@ const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
|
16
16
|
// an unrecognized key here is a forward-compatible warning, not an error.
|
|
17
17
|
const KNOWN_COMPONENT_KEYS = new Set([
|
|
18
18
|
'source',
|
|
19
|
+
'sources',
|
|
19
20
|
'description',
|
|
20
21
|
'kind',
|
|
21
22
|
'attributes',
|
|
@@ -266,11 +267,57 @@ export function parsePackManifest(json) {
|
|
|
266
267
|
// the raw parsed one — the same discipline as the string-form branch
|
|
267
268
|
// and as the top-level Object.hasOwn guarding throughout this file.
|
|
268
269
|
let entryValid = true;
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
const hasSource = Object.hasOwn(value, 'source');
|
|
271
|
+
const hasSources = Object.hasOwn(value, 'sources');
|
|
272
|
+
const source = hasSource ? value.source : undefined;
|
|
273
|
+
let sourcesMap;
|
|
274
|
+
if (hasSource && hasSources) {
|
|
275
|
+
errors.push(`"components.${key}" must carry "source" or "sources", not both`);
|
|
272
276
|
entryValid = false;
|
|
273
277
|
}
|
|
278
|
+
else if (!hasSource && !hasSources) {
|
|
279
|
+
errors.push(`"components.${key}.source" is required (a pack-relative source path), or give a "sources" map instead`);
|
|
280
|
+
entryValid = false;
|
|
281
|
+
}
|
|
282
|
+
else if (hasSource) {
|
|
283
|
+
if (typeof source !== 'string' || source.length === 0) {
|
|
284
|
+
errors.push(`"components.${key}.source" must be a non-empty string (a pack-relative source path)`);
|
|
285
|
+
entryValid = false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const rawSources = value.sources;
|
|
290
|
+
if (!isPlainObject(rawSources)) {
|
|
291
|
+
errors.push(`"components.${key}.sources" must be an object mapping engine ids to pack-relative source paths`);
|
|
292
|
+
entryValid = false;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const parsedSources = Object.create(null);
|
|
296
|
+
let count = 0;
|
|
297
|
+
for (const engineId of Object.keys(rawSources)) {
|
|
298
|
+
if (!Object.hasOwn(rawSources, engineId))
|
|
299
|
+
continue;
|
|
300
|
+
count += 1;
|
|
301
|
+
const path = rawSources[engineId];
|
|
302
|
+
if (!isPackEngineId(engineId)) {
|
|
303
|
+
errors.push(`"components.${key}.sources" key "${engineId}" must be a lowercase engine id matching ${PACK_ENGINE_ID_PATTERN.source}`);
|
|
304
|
+
entryValid = false;
|
|
305
|
+
}
|
|
306
|
+
else if (typeof path !== 'string' || path.length === 0) {
|
|
307
|
+
errors.push(`"components.${key}.sources.${engineId}" must be a non-empty string (a pack-relative source path)`);
|
|
308
|
+
entryValid = false;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
parsedSources[engineId] = path;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (count === 0) {
|
|
315
|
+
errors.push(`"components.${key}.sources" must name at least one engine`);
|
|
316
|
+
entryValid = false;
|
|
317
|
+
}
|
|
318
|
+
sourcesMap = parsedSources;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
274
321
|
let description;
|
|
275
322
|
if (Object.hasOwn(value, 'description')) {
|
|
276
323
|
const rawDescription = value.description;
|
|
@@ -325,7 +372,7 @@ export function parsePackManifest(json) {
|
|
|
325
372
|
// field was present — only the string shorthand collapses to a bare
|
|
326
373
|
// string. This preserves the author's declared form rather than
|
|
327
374
|
// silently normalizing `{ "source": "./x.tsx" }` into `"./x.tsx"`.
|
|
328
|
-
const sanitized = { source: source };
|
|
375
|
+
const sanitized = hasSource ? { source: source } : { sources: sourcesMap };
|
|
329
376
|
if (description !== undefined)
|
|
330
377
|
sanitized.description = description;
|
|
331
378
|
if (kind !== undefined)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/pack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0-next.1",
|
|
4
4
|
"description": "Component pack contract for Markii (docs/packs.md): the pack.json manifest shape, namespace and engine rules, hand-rolled validation, and the .mkp prebuilt-archive reader. No React, no parsing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"lint": "eslint ."
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@markii/bundle": "^0.
|
|
45
|
+
"@markii/bundle": "^0.17.0-next.1"
|
|
46
46
|
}
|
|
47
47
|
}
|