@octane-xplat/workshop 0.1.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/MOBILE.md +143 -0
- package/README.md +305 -1
- package/dist/build/discover.js +404 -0
- package/dist/build/macos-node.js +77 -0
- package/dist/build/mobile-node.js +96 -0
- package/dist/build/mobile-regex.js +40 -0
- package/dist/build/node.js +426 -0
- package/dist/build/vite-mobile.js +75 -0
- package/dist/build/vite.js +230 -0
- package/dist/build/windows/preflight.js +83 -0
- package/dist/contract.js +50 -0
- package/dist/index.js +2 -0
- package/package.json +118 -5
- package/src/build/discover.ts +585 -0
- package/src/build/macos/shell.macos.ts +674 -0
- package/src/build/macos-node.ts +98 -0
- package/src/build/macos-vite.ts +48 -0
- package/src/build/mobile-node.ts +127 -0
- package/src/build/mobile-regex.ts +60 -0
- package/src/build/node.d.ts +65 -0
- package/src/build/node.ts +537 -0
- package/src/build/vite-mobile.d.ts +21 -0
- package/src/build/vite-mobile.ts +102 -0
- package/src/build/vite.d.ts +11 -0
- package/src/build/vite.ts +316 -0
- package/src/build/windows/preflight.ts +113 -0
- package/src/catalog.ts +58 -0
- package/src/contract.ts +257 -0
- package/src/controls.ts +159 -0
- package/src/errors.ts +47 -0
- package/src/fixture.ts +158 -0
- package/src/index.ts +6 -0
- package/src/macos/controller.ts +170 -0
- package/src/macos/index.macos.ts +2 -0
- package/src/macos/stage.macos.ts +182 -0
- package/src/mobile/controller.ts +134 -0
- package/src/mobile/entry.mobile.ts +607 -0
- package/src/mobile/index.mobile.ts +3 -0
- package/src/mobile/mount.mobile.ts +60 -0
- package/src/mobile/tree.mobile.tsrx +17 -0
- package/src/session.ts +520 -0
- package/src/web/entry.web.ts +270 -0
- package/src/web/index.web.ts +7 -0
- package/src/web/mount.web.ts +86 -0
- package/src/web/shell.css +449 -0
- package/src/web/shell.web.tsrx +759 -0
- package/types/generated/.tsrx-typegen-manifest.json +24 -0
- package/types/generated/catalog.d.ts +30 -0
- package/types/generated/contract.d.ts +145 -0
- package/types/generated/controls.d.ts +28 -0
- package/types/generated/errors.d.ts +13 -0
- package/types/generated/fixture.d.ts +53 -0
- package/types/generated/index.d.ts +6 -0
- package/types/generated/macos/controller.d.ts +52 -0
- package/types/generated/macos/index.macos.d.ts +2 -0
- package/types/generated/macos/stage.macos.d.ts +10 -0
- package/types/generated/mobile/controller.d.ts +31 -0
- package/types/generated/mobile/entry.mobile.d.ts +22 -0
- package/types/generated/mobile/index.mobile.d.ts +3 -0
- package/types/generated/mobile/mount.mobile.d.ts +4 -0
- package/types/generated/mobile/tree.mobile.d.ts +4 -0
- package/types/generated/session.d.ts +84 -0
- package/types/generated/web/entry.web.d.ts +53 -0
- package/types/generated/web/index.web.d.ts +7 -0
- package/types/generated/web/mount.web.d.ts +2 -0
- package/types/generated/web/shell.web.d.ts +4 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { discoverStories, normalizeInclude } from "./discover.js";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
//#region src/build/vite.ts
|
|
6
|
+
/**
|
|
7
|
+
* xplatWorkshop — the workshop dev-server plugin.
|
|
8
|
+
*
|
|
9
|
+
* Emits a virtual catalog module whose metadata comes from static discovery
|
|
10
|
+
* (never module evaluation), a virtual entry that boots the adaptive shell,
|
|
11
|
+
* and a virtual index.html. Story modules stay lazily imported per selection;
|
|
12
|
+
* loaders exist only for stories applicable to the build target, so a
|
|
13
|
+
* platform-specific leaf is never imported into an incompatible bundle.
|
|
14
|
+
*/
|
|
15
|
+
var CATALOG_ID = "\0xplat-workshop-catalog";
|
|
16
|
+
var ENTRY_ID = "\0xplat-workshop-entry";
|
|
17
|
+
var STORY_FILE_RE = /\.stories(?:\.(?:web|mobile|ios|android|macos|windows|linux))?\.(?:[cm]?[jt]s|[jt]sx|[jt]srx)$/;
|
|
18
|
+
var TARGET_RESOLUTION = {
|
|
19
|
+
web: {
|
|
20
|
+
conditions: ["web"],
|
|
21
|
+
extensions: [
|
|
22
|
+
".web.tsrx",
|
|
23
|
+
".tsrx",
|
|
24
|
+
".web.tsx",
|
|
25
|
+
".tsx",
|
|
26
|
+
".web.ts",
|
|
27
|
+
".ts",
|
|
28
|
+
".mts",
|
|
29
|
+
".ts",
|
|
30
|
+
".jsx",
|
|
31
|
+
".js",
|
|
32
|
+
".json"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
linux: {
|
|
36
|
+
conditions: ["linux", "web"],
|
|
37
|
+
extensions: [
|
|
38
|
+
".linux.tsrx",
|
|
39
|
+
".web.tsrx",
|
|
40
|
+
".tsrx",
|
|
41
|
+
".linux.tsx",
|
|
42
|
+
".web.tsx",
|
|
43
|
+
".tsx",
|
|
44
|
+
".linux.ts",
|
|
45
|
+
".web.ts",
|
|
46
|
+
".ts",
|
|
47
|
+
".mts",
|
|
48
|
+
".ts",
|
|
49
|
+
".jsx",
|
|
50
|
+
".js",
|
|
51
|
+
".json"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var fsUrl = (file) => `/@fs/${file.split(sep).join("/")}`;
|
|
56
|
+
function catalogCode(root, config, target) {
|
|
57
|
+
const storiesConfig = config?.stories ?? {};
|
|
58
|
+
const { entries, diagnostics } = discoverStories(root, {
|
|
59
|
+
include: storiesConfig.include,
|
|
60
|
+
exclude: storiesConfig.exclude,
|
|
61
|
+
target
|
|
62
|
+
});
|
|
63
|
+
const include = JSON.stringify(normalizeInclude(storiesConfig.include));
|
|
64
|
+
const loaders = entries.filter((entry) => entry.applicable && diagnostics.length === 0).map((entry) => `\t${JSON.stringify(entry.id)}: (stamp) => stamp === undefined
|
|
65
|
+
\t\t? import(${JSON.stringify(fsUrl(entry.file))})
|
|
66
|
+
\t\t: import(/* @vite-ignore */ ${JSON.stringify(fsUrl(entry.file))} + '?t=' + encodeURIComponent(String(stamp))),`).join("\n");
|
|
67
|
+
return `import { catalogDidUpdate } from '@octane-xplat/workshop/web'
|
|
68
|
+
|
|
69
|
+
export const catalogMeta = ${JSON.stringify(entries)}
|
|
70
|
+
export const diagnostics = ${JSON.stringify(diagnostics)}
|
|
71
|
+
export const includePatterns = ${include}
|
|
72
|
+
|
|
73
|
+
const loaders = {
|
|
74
|
+
${loaders}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const catalog = catalogMeta.map((entry) =>
|
|
78
|
+
loaders[entry.id] ? { ...entry, load: loaders[entry.id] } : entry,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
export function loadStory(id, stamp) {
|
|
82
|
+
const entry = catalog.find((item) => item.id === id)
|
|
83
|
+
if (!entry?.load) {
|
|
84
|
+
return Promise.reject(
|
|
85
|
+
new Error(
|
|
86
|
+
entry
|
|
87
|
+
? 'story "' + id + '" is not supported on this target'
|
|
88
|
+
: 'story "' + id + '" is not in the catalog',
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
return entry.load(stamp)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
catalogDidUpdate(catalog, diagnostics)
|
|
96
|
+
if (import.meta.hot) {
|
|
97
|
+
import.meta.hot.accept((next) => catalogDidUpdate(next?.catalog ?? catalog, next?.diagnostics ?? diagnostics))
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
function entryCode(config, target, story, packageDir, root) {
|
|
102
|
+
const providerModules = config?.providerModules ?? [];
|
|
103
|
+
const imports = providerModules.map((specifier, index) => {
|
|
104
|
+
const abs = resolve(root, specifier);
|
|
105
|
+
return `import * as __workshopPreset${index} from ${JSON.stringify(fsUrl(abs))}`;
|
|
106
|
+
}).join("\n");
|
|
107
|
+
const providers = providerModules.map((_, index) => `...(__workshopPreset${index}.providers ?? __workshopPreset${index}.default ?? [])`).join(", ");
|
|
108
|
+
const shellCss = fsUrl(join(packageDir, "web", "shell.css"));
|
|
109
|
+
return `import { catalog, loadStory, diagnostics } from ${JSON.stringify(CATALOG_ID)}
|
|
110
|
+
import { startWorkshop } from '@octane-xplat/workshop/web'
|
|
111
|
+
import '@octane-xplat/ui/theme/tokens.css'
|
|
112
|
+
import ${JSON.stringify(shellCss)}
|
|
113
|
+
${imports}
|
|
114
|
+
|
|
115
|
+
startWorkshop({
|
|
116
|
+
catalog,
|
|
117
|
+
diagnostics,
|
|
118
|
+
loadStory,
|
|
119
|
+
target: ${JSON.stringify(target)},
|
|
120
|
+
initialStoryId: ${JSON.stringify(story ?? null)},
|
|
121
|
+
globalProviders: [${providers}],
|
|
122
|
+
hot: import.meta.hot,
|
|
123
|
+
})
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
var WORKSHOP_HTML = `<!doctype html>
|
|
127
|
+
<html lang="en">
|
|
128
|
+
<head>
|
|
129
|
+
<meta charset="utf-8" />
|
|
130
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
131
|
+
<title>octane-xplat workshop</title>
|
|
132
|
+
<style>html,body{margin:0;height:100%}#workshop-root{height:100%}</style>
|
|
133
|
+
</head>
|
|
134
|
+
<body>
|
|
135
|
+
<div id="workshop-root"></div>
|
|
136
|
+
<script type="module" src="/@id/__x00__xplat-workshop-entry"><\/script>
|
|
137
|
+
</body>
|
|
138
|
+
</html>
|
|
139
|
+
`;
|
|
140
|
+
/**
|
|
141
|
+
* @param {object} [options]
|
|
142
|
+
* @param {string} options.cwd - consumer project root
|
|
143
|
+
* @param {object} [options.config] - resolved workshop.config.* contents
|
|
144
|
+
* @param {string} [options.target] - launch target (DOM hosts: 'web' | 'linux')
|
|
145
|
+
* @param {string} [options.story] - initial stable story id
|
|
146
|
+
*/
|
|
147
|
+
function xplatWorkshop(options = {}) {
|
|
148
|
+
const target = options.target ?? "web";
|
|
149
|
+
const story = options.story ?? null;
|
|
150
|
+
const resolution = TARGET_RESOLUTION[target] ?? TARGET_RESOLUTION.web;
|
|
151
|
+
let root = options.cwd ?? process.cwd();
|
|
152
|
+
let config = options.config ?? {};
|
|
153
|
+
return [{
|
|
154
|
+
name: "xplat-workshop-virtual",
|
|
155
|
+
configResolved(resolved) {
|
|
156
|
+
root = options.cwd ?? resolved.root;
|
|
157
|
+
},
|
|
158
|
+
resolveId(id) {
|
|
159
|
+
if (id === CATALOG_ID || id === ENTRY_ID) return id;
|
|
160
|
+
return null;
|
|
161
|
+
},
|
|
162
|
+
load(id) {
|
|
163
|
+
if (id === CATALOG_ID) return catalogCode(root, config, target);
|
|
164
|
+
if (id === ENTRY_ID) return entryCode(config, target, story, dirname(fileURLToPath(import.meta.url)), root);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}, {
|
|
168
|
+
name: "xplat-workshop-shell",
|
|
169
|
+
config() {
|
|
170
|
+
return {
|
|
171
|
+
optimizeDeps: {
|
|
172
|
+
exclude: ["@octane-xplat/workshop"],
|
|
173
|
+
include: ["@octane-xplat/workshop > storybook/preview-api"]
|
|
174
|
+
},
|
|
175
|
+
resolve: { conditions: [...resolution.conditions] }
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
configResolved(resolved) {
|
|
179
|
+
const apply = (scope) => {
|
|
180
|
+
scope.resolve.extensions = [...resolution.extensions, ...scope.resolve.extensions.filter((ext) => !resolution.extensions.includes(ext))];
|
|
181
|
+
scope.resolve.conditions = [...resolution.conditions, ...scope.resolve.conditions.filter((cond) => !resolution.conditions.includes(cond))];
|
|
182
|
+
};
|
|
183
|
+
apply(resolved);
|
|
184
|
+
for (const env of Object.values(resolved.environments ?? {})) apply(env);
|
|
185
|
+
},
|
|
186
|
+
hotUpdate: {
|
|
187
|
+
order: "pre",
|
|
188
|
+
handler(ctx) {
|
|
189
|
+
if (this.environment.name !== "client" || !STORY_FILE_RE.test(ctx.file)) return;
|
|
190
|
+
const catalog = this.environment.moduleGraph.getModuleById(CATALOG_ID);
|
|
191
|
+
if (!catalog) return;
|
|
192
|
+
this.environment.moduleGraph.invalidateModule(catalog);
|
|
193
|
+
return [catalog];
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
configureServer(s) {
|
|
197
|
+
s.middlewares.use((req, res, next) => {
|
|
198
|
+
const url = (req.url ?? "").split("?")[0];
|
|
199
|
+
if (url === "/" || url === "/index.html") {
|
|
200
|
+
res.statusCode = 200;
|
|
201
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
202
|
+
res.end(WORKSHOP_HTML);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (url.startsWith("/@workshop-assets/")) return serveAsset(config, root, url, res, next);
|
|
206
|
+
next();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}];
|
|
210
|
+
}
|
|
211
|
+
function serveAsset(config, root, url, res, next) {
|
|
212
|
+
const assets = config?.assets ?? {};
|
|
213
|
+
const rest = url.slice(18);
|
|
214
|
+
const name = rest.split("/")[0];
|
|
215
|
+
const dir = assets[name] ? resolve(root, assets[name]) : null;
|
|
216
|
+
if (!dir) {
|
|
217
|
+
next();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const file = resolve(dir, rest.slice(name.length + 1));
|
|
221
|
+
if (!(file === dir || file.startsWith(dir + sep)) || !existsSync(file) || !statSync(file).isFile()) {
|
|
222
|
+
res.statusCode = 404;
|
|
223
|
+
res.end("not found");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
res.statusCode = 200;
|
|
227
|
+
res.end(readFileSync(file));
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
export { xplatWorkshop };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
//#region src/build/windows/preflight.ts
|
|
6
|
+
/** Read-only Windows setup evidence. Prerequisites never qualify a host. */
|
|
7
|
+
function commandOutput(command, args) {
|
|
8
|
+
try {
|
|
9
|
+
return execFileSync(command, args, {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
timeout: 5e3,
|
|
12
|
+
maxBuffer: 65536,
|
|
13
|
+
stdio: [
|
|
14
|
+
"ignore",
|
|
15
|
+
"pipe",
|
|
16
|
+
"pipe"
|
|
17
|
+
],
|
|
18
|
+
windowsHide: true
|
|
19
|
+
}).trim();
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function windowsPreflight(root, { platform = process.platform, run = commandOutput } = {}) {
|
|
25
|
+
const manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
26
|
+
const require = createRequire(join(root, "package.json"));
|
|
27
|
+
const installed = (name) => {
|
|
28
|
+
for (const specifier of [`${name}/package.json`, name]) try {
|
|
29
|
+
require.resolve(specifier);
|
|
30
|
+
return true;
|
|
31
|
+
} catch {}
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
34
|
+
const runtime = manifest.devDependencies?.["@nativescript/windows"] ?? manifest.dependencies?.["@nativescript/windows"];
|
|
35
|
+
const requirements = {
|
|
36
|
+
"Windows host": platform === "win32",
|
|
37
|
+
"nativescript.config": [
|
|
38
|
+
"ts",
|
|
39
|
+
"mts",
|
|
40
|
+
"js",
|
|
41
|
+
"mjs"
|
|
42
|
+
].some((ext) => existsSync(join(root, `nativescript.config.${ext}`))),
|
|
43
|
+
"App_Resources/Windows/Package.appxmanifest": existsSync(join(root, "App_Resources/Windows/Package.appxmanifest")),
|
|
44
|
+
"@nativescript/windows exact version": typeof runtime === "string" && /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(runtime)
|
|
45
|
+
};
|
|
46
|
+
for (const name of [
|
|
47
|
+
"@nativescript/windows",
|
|
48
|
+
"@nativescript/core",
|
|
49
|
+
"@nativescript-community/octane",
|
|
50
|
+
"@nativescript/vite",
|
|
51
|
+
"nativescript",
|
|
52
|
+
"vite"
|
|
53
|
+
]) requirements[name] = installed(name);
|
|
54
|
+
if (platform === "win32") {
|
|
55
|
+
const sdk = run("dotnet", ["--version"]);
|
|
56
|
+
requirements[".NET SDK >= 10"] = typeof sdk === "string" && /^\d+\.\d+\.\d+/.test(sdk) && Number(sdk.split(".")[0]) >= 10;
|
|
57
|
+
requirements["Developer Mode (development launch)"] = run("powershell", [
|
|
58
|
+
"-NoProfile",
|
|
59
|
+
"-NonInteractive",
|
|
60
|
+
"-Command",
|
|
61
|
+
"(Get-ItemProperty \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock\" -Name AllowDevelopmentWithoutDevLicense -ErrorAction SilentlyContinue).AllowDevelopmentWithoutDevLicense"
|
|
62
|
+
])?.trim() === "1";
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
backend: "experimental nativescript/winui",
|
|
66
|
+
executionHost: {
|
|
67
|
+
required: "win32",
|
|
68
|
+
current: platform,
|
|
69
|
+
reachable: platform === "win32"
|
|
70
|
+
},
|
|
71
|
+
qualification: "unverified",
|
|
72
|
+
requirements,
|
|
73
|
+
status: platform !== "win32" ? "remote-host-required" : Object.values(requirements).every(Boolean) ? "unqualified-backend" : "missing-prerequisites",
|
|
74
|
+
note: "Windows workshop mounting, shell and reload are not implemented or host-qualified. " + (platform !== "win32" ? "Run these commands in a Windows checkout; SDK and Developer Mode checks were not run. " : "") + "Prerequisites do not prove renderer compatibility, input, accessibility or lifecycle support. No web fallback is used."
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function windowsLaunchBlocker(report) {
|
|
78
|
+
if (report.status === "remote-host-required") return "Windows workshop requires a real Windows host. Run `xplat workshop doctor` in that host’s checkout. No web fallback is used.";
|
|
79
|
+
const missing = Object.entries(report.requirements).filter(([, ok]) => !ok).map(([name]) => name);
|
|
80
|
+
return (missing.length ? `Windows prerequisites missing: ${missing.join(", ")}. ` : "") + "Windows workshop mounting, shell and reload remain unimplemented and unqualified; launch is blocked. Run `xplat workshop doctor` for setup evidence.";
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { windowsLaunchBlocker, windowsPreflight };
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/contract.ts
|
|
2
|
+
var WORKSHOP_TARGETS = [
|
|
3
|
+
"web",
|
|
4
|
+
"ios",
|
|
5
|
+
"android",
|
|
6
|
+
"macos",
|
|
7
|
+
"windows",
|
|
8
|
+
"linux"
|
|
9
|
+
];
|
|
10
|
+
/** Version of the authoring contract — bumped on breaking changes. */
|
|
11
|
+
var WORKSHOP_CONTRACT_VERSION = 1;
|
|
12
|
+
/** Declare a story. Validates cheap authoring rules eagerly. */
|
|
13
|
+
function defineStory(definition) {
|
|
14
|
+
assertLiteralAuthoring(definition);
|
|
15
|
+
return definition;
|
|
16
|
+
}
|
|
17
|
+
function assertLiteralAuthoring(definition) {
|
|
18
|
+
const fail = (message) => {
|
|
19
|
+
throw new Error(`defineStory: ${message}`);
|
|
20
|
+
};
|
|
21
|
+
if (typeof definition?.id !== "string" || !definition.id) fail("a story requires a non-empty string id");
|
|
22
|
+
if (typeof definition.title !== "string" || !definition.title) fail(`story "${definition.id}" requires a non-empty string title`);
|
|
23
|
+
const targets = definition.targets;
|
|
24
|
+
if (!(targets === "all" || Array.isArray(targets) && targets.length > 0 && targets.every((t) => WORKSHOP_TARGETS.includes(t)))) fail(`story "${definition.id}" — targets must be 'all' or a subset of: ${WORKSHOP_TARGETS.join(", ")}`);
|
|
25
|
+
if (typeof definition.component !== "function") fail(`story "${definition.id}" — component must be an Octane component`);
|
|
26
|
+
if (definition.args == null || typeof definition.args !== "object") fail(`story "${definition.id}" — args must be an object`);
|
|
27
|
+
if ("state" in definition && typeof definition.state !== "function") fail(`story "${definition.id}" — state must be a factory function`);
|
|
28
|
+
if ("fixture" in definition && typeof definition.fixture !== "function") fail(`story "${definition.id}" — fixture must be a factory function`);
|
|
29
|
+
if (definition.args && typeof definition.args === "object") {
|
|
30
|
+
for (const key of ["state", "fixture"]) if (key in definition.args) fail(`story "${definition.id}" — args may not declare reserved injected prop "${key}"`);
|
|
31
|
+
}
|
|
32
|
+
if (definition.controls) for (const [key, control] of Object.entries(definition.controls)) {
|
|
33
|
+
if (!control || typeof control !== "object") fail(`story "${definition.id}" — control "${key}" must be an object`);
|
|
34
|
+
const type = control.type;
|
|
35
|
+
if (![
|
|
36
|
+
"boolean",
|
|
37
|
+
"string",
|
|
38
|
+
"number",
|
|
39
|
+
"enum"
|
|
40
|
+
].includes(type ?? "")) fail(`story "${definition.id}" — control "${key}" has unknown type "${type}" (expected boolean, string, number or enum)`);
|
|
41
|
+
if (type === "enum" && !Array.isArray(control.options)) fail(`story "${definition.id}" — enum control "${key}" requires an options array`);
|
|
42
|
+
if (type === "number" && typeof control.min === "number" && typeof control.max === "number" && control.min > control.max) fail(`story "${definition.id}" — number control "${key}" has min > max`);
|
|
43
|
+
}
|
|
44
|
+
if (definition.providers !== void 0 && !Array.isArray(definition.providers)) fail(`story "${definition.id}" — providers must be an array`);
|
|
45
|
+
}
|
|
46
|
+
function defineWorkshopConfig(config) {
|
|
47
|
+
return config;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { WORKSHOP_CONTRACT_VERSION, WORKSHOP_TARGETS, defineStory, defineWorkshopConfig };
|
package/dist/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octane-xplat/workshop",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"description": "Development-only component workshop for Octane Xplat — typed stories, metadata discovery, adaptive shell, lifecycle and warm reload",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"components",
|
|
7
|
+
"cross-platform",
|
|
8
|
+
"dev-tools",
|
|
9
|
+
"octane",
|
|
10
|
+
"stories",
|
|
11
|
+
"storybook",
|
|
12
|
+
"workshop",
|
|
13
|
+
"xplat"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/octane-xplat/octane-xplat/tree/main/packages/workshop",
|
|
5
16
|
"license": "MIT",
|
|
6
17
|
"repository": {
|
|
7
18
|
"type": "git",
|
|
@@ -9,6 +20,108 @@
|
|
|
9
20
|
"directory": "packages/workshop"
|
|
10
21
|
},
|
|
11
22
|
"files": [
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
"types",
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"MOBILE.md",
|
|
27
|
+
"!src/**/*.test.*",
|
|
28
|
+
"!src/**/*.spec.*"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./types/generated/index.d.ts",
|
|
34
|
+
"node": "./dist/index.js",
|
|
35
|
+
"default": "./src/index.ts"
|
|
36
|
+
},
|
|
37
|
+
"./web": {
|
|
38
|
+
"types": "./types/generated/web/index.web.d.ts",
|
|
39
|
+
"default": "./src/web/index.web.ts"
|
|
40
|
+
},
|
|
41
|
+
"./vite": {
|
|
42
|
+
"types": "./src/build/vite.d.ts",
|
|
43
|
+
"default": "./dist/build/vite.js"
|
|
44
|
+
},
|
|
45
|
+
"./node": {
|
|
46
|
+
"types": "./src/build/node.d.ts",
|
|
47
|
+
"default": "./dist/build/node.js"
|
|
48
|
+
},
|
|
49
|
+
"./package.json": "./package.json",
|
|
50
|
+
"./mobile": {
|
|
51
|
+
"types": "./types/generated/mobile/index.mobile.d.ts",
|
|
52
|
+
"default": "./src/mobile/index.mobile.ts"
|
|
53
|
+
},
|
|
54
|
+
"./vite-mobile": {
|
|
55
|
+
"types": "./src/build/vite-mobile.d.ts",
|
|
56
|
+
"default": "./dist/build/vite-mobile.js"
|
|
57
|
+
},
|
|
58
|
+
"./macos": {
|
|
59
|
+
"types": "./types/generated/macos/index.macos.d.ts",
|
|
60
|
+
"default": "./src/macos/index.macos.ts"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@babel/core": "7.29.7",
|
|
68
|
+
"@babel/plugin-transform-unicode-property-regex": "7.29.7",
|
|
69
|
+
"@octane-xplat/cli": "0.16.0",
|
|
70
|
+
"@octane-xplat/platform": "0.16.0",
|
|
71
|
+
"@octane-xplat/ui": "0.16.0",
|
|
72
|
+
"storybook": "10.6.1",
|
|
73
|
+
"tinyglobby": "0.2.17"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@nativescript-community/octane": "0.2.5",
|
|
77
|
+
"@nativescript/core": "9.1.3",
|
|
78
|
+
"@nativescript/macos-node-api": "0.4.4-next.2026-08-09-31292056208",
|
|
79
|
+
"@nativescript/types": "9.1.4",
|
|
80
|
+
"@octane-xplat/macos-renderer": "0.16.0",
|
|
81
|
+
"@octanejs/vite-plugin": "0.1.61",
|
|
82
|
+
"octane": "0.11.0",
|
|
83
|
+
"tsrx-typegen": "0.3.1",
|
|
84
|
+
"typescript": "5.9.3",
|
|
85
|
+
"vite": "8.3.0"
|
|
86
|
+
},
|
|
87
|
+
"peerDependencies": {
|
|
88
|
+
"@nativescript-community/octane": ">=0.2.4 <1",
|
|
89
|
+
"@nativescript/core": ">=9.1.3 <10",
|
|
90
|
+
"@nativescript/macos-node-api": "0.4.4-next.2026-08-09-31292056208",
|
|
91
|
+
"@octane-xplat/macos-renderer": ">=0.14.0 <1",
|
|
92
|
+
"@octanejs/vite-plugin": ">=0.1.61",
|
|
93
|
+
"octane": ">=0.6.3 <1",
|
|
94
|
+
"vite": ">=8"
|
|
95
|
+
},
|
|
96
|
+
"peerDependenciesMeta": {
|
|
97
|
+
"vite": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"@nativescript/core": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"@nativescript-community/octane": {
|
|
104
|
+
"optional": true
|
|
105
|
+
},
|
|
106
|
+
"@octane-xplat/macos-renderer": {
|
|
107
|
+
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"@nativescript/macos-node-api": {
|
|
110
|
+
"optional": true
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"scripts": {
|
|
114
|
+
"typegen": "tsrx-typegen --target workshop",
|
|
115
|
+
"typecheck": "node ../../scripts/typecheck.ts",
|
|
116
|
+
"test": "vitest run && tsrx-tsc --noEmit -p tsconfig.test.json",
|
|
117
|
+
"test:e2e": "pnpm --filter @xplat/workshop-demo test:e2e",
|
|
118
|
+
"test:packed": "pnpm build && node tests/packed-consumer.web.mjs",
|
|
119
|
+
"test:packed:linux": "node tests/packed-consumer.linux.mjs",
|
|
120
|
+
"test:packed:macos": "node tests/packed-consumer.macos.mjs",
|
|
121
|
+
"test:packed:windows": "node tests/packed-windows-preflight.mjs",
|
|
122
|
+
"pack:check": "tsrx-typegen --pack-check",
|
|
123
|
+
"test:types": "tsrx-tsc --noEmit -p tsconfig.test.json",
|
|
124
|
+
"test:packed:mobile": "node tests/packed-consumer.mobile.mjs",
|
|
125
|
+
"build": "vite build"
|
|
126
|
+
}
|
|
127
|
+
}
|