@preference-sl/pref-configurator-sdk 0.1.2-beta.2 → 0.2.0-beta.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/README.md +15 -5
- package/_framework/{PrefConfigurator.Abstractions.m5p9u43rne.wasm → PrefConfigurator.Abstractions.rpd793fq3k.wasm} +0 -0
- package/_framework/PrefConfigurator.Abstractions.rpd793fq3k.wasm.br +0 -0
- package/_framework/PrefConfigurator.Abstractions.rpd793fq3k.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Application.nu5908ldle.wasm +0 -0
- package/_framework/PrefConfigurator.Application.nu5908ldle.wasm.br +0 -0
- package/_framework/PrefConfigurator.Application.nu5908ldle.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Domain.0b6siqr4nn.wasm +0 -0
- package/_framework/PrefConfigurator.Domain.0b6siqr4nn.wasm.br +0 -0
- package/_framework/PrefConfigurator.Domain.0b6siqr4nn.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Infrastructure.drxw72xtyf.wasm +0 -0
- package/_framework/PrefConfigurator.Infrastructure.drxw72xtyf.wasm.br +0 -0
- package/_framework/PrefConfigurator.Infrastructure.drxw72xtyf.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Wasm.q2s08d6agk.wasm +0 -0
- package/_framework/PrefConfigurator.Wasm.q2s08d6agk.wasm.br +0 -0
- package/_framework/PrefConfigurator.Wasm.q2s08d6agk.wasm.gz +0 -0
- package/_framework/{System.Private.CoreLib.77dyhxu8ng.wasm → System.Private.CoreLib.3it7p8nh6k.wasm} +0 -0
- package/_framework/System.Private.CoreLib.3it7p8nh6k.wasm.br +0 -0
- package/_framework/System.Private.CoreLib.3it7p8nh6k.wasm.gz +0 -0
- package/_framework/System.Runtime.InteropServices.JavaScript.luprqfrwge.wasm +0 -0
- package/_framework/System.Runtime.InteropServices.JavaScript.luprqfrwge.wasm.br +0 -0
- package/_framework/System.Runtime.InteropServices.JavaScript.luprqfrwge.wasm.gz +0 -0
- package/_framework/dotnet.js +17 -17
- package/_framework/dotnet.js.br +0 -0
- package/_framework/dotnet.js.gz +0 -0
- package/_framework/{dotnet.native.3hup5hq7f6.wasm → dotnet.native.0iyv779ixl.wasm} +0 -0
- package/_framework/dotnet.native.0iyv779ixl.wasm.br +0 -0
- package/_framework/{dotnet.native.3hup5hq7f6.wasm.gz → dotnet.native.0iyv779ixl.wasm.gz} +0 -0
- package/index.d.ts +42 -0
- package/index.js +8 -0
- package/js/gltf-merger.d.ts +25 -0
- package/js/gltf-merger.js +18 -3
- package/js/gltf-storage.d.ts +23 -0
- package/js/prefconfigurator-api.d.ts +2 -2
- package/js/sdk-framework-assets.d.ts +3 -0
- package/js/sdk-framework-assets.js +85 -0
- package/js/sdk-init.d.ts +39 -0
- package/js/sdk-init.js +227 -0
- package/js/sdk-worker.d.ts +3 -0
- package/js/sdk-worker.js +138 -0
- package/package.json +32 -22
- package/_framework/PrefConfigurator.Abstractions.m5p9u43rne.wasm.br +0 -0
- package/_framework/PrefConfigurator.Abstractions.m5p9u43rne.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Application.bb2ale30gh.wasm +0 -0
- package/_framework/PrefConfigurator.Application.bb2ale30gh.wasm.br +0 -0
- package/_framework/PrefConfigurator.Application.bb2ale30gh.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Domain.lz24uq9hxw.wasm +0 -0
- package/_framework/PrefConfigurator.Domain.lz24uq9hxw.wasm.br +0 -0
- package/_framework/PrefConfigurator.Domain.lz24uq9hxw.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Infrastructure.p8wrxjw2mh.wasm +0 -0
- package/_framework/PrefConfigurator.Infrastructure.p8wrxjw2mh.wasm.br +0 -0
- package/_framework/PrefConfigurator.Infrastructure.p8wrxjw2mh.wasm.gz +0 -0
- package/_framework/PrefConfigurator.Wasm.fm0zddqw93.wasm +0 -0
- package/_framework/PrefConfigurator.Wasm.fm0zddqw93.wasm.br +0 -0
- package/_framework/PrefConfigurator.Wasm.fm0zddqw93.wasm.gz +0 -0
- package/_framework/System.Private.CoreLib.77dyhxu8ng.wasm.br +0 -0
- package/_framework/System.Private.CoreLib.77dyhxu8ng.wasm.gz +0 -0
- package/_framework/System.Runtime.InteropServices.JavaScript.hz5gi57d06.wasm +0 -0
- package/_framework/System.Runtime.InteropServices.JavaScript.hz5gi57d06.wasm.br +0 -0
- package/_framework/System.Runtime.InteropServices.JavaScript.hz5gi57d06.wasm.gz +0 -0
- package/_framework/dotnet.native.3hup5hq7f6.wasm.br +0 -0
- package/main.d.ts +0 -4
- package/main.js +0 -2
- package/scripts/postinstall.js +0 -128
package/js/sdk-init.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// PrefConfigurator WASM SDK — main-thread entry.
|
|
2
|
+
//
|
|
3
|
+
// init(env, opts?) spawns sdk-worker.js as a module Worker and returns a Proxy that
|
|
4
|
+
// forwards every method call to the worker over postMessage. The Proxy mirrors
|
|
5
|
+
// BoundApi<JsInteropApiRaw> from prefconfigurator-api.d.ts: it stringifies object
|
|
6
|
+
// args for the methods that need it and unwraps OperationResult<T> via ensureOk.
|
|
7
|
+
//
|
|
8
|
+
// All bundler-aware URL resolution lives here in the main thread, never in the
|
|
9
|
+
// worker — Next.js + webpack 5 does not reliably bundle a worker chunk's transitive
|
|
10
|
+
// imports, so the worker is kept leaf and we hand it pre-resolved URLs over the init
|
|
11
|
+
// message. See docs/wasm-worker-init.md.
|
|
12
|
+
|
|
13
|
+
import { ensureOk } from "./prefconfigurator-api.js";
|
|
14
|
+
import { FRAMEWORK_ASSETS } from "./sdk-framework-assets.js";
|
|
15
|
+
|
|
16
|
+
// new URL(literal, import.meta.url) is the bundler-recognized pattern; webpack 5 / Vite /
|
|
17
|
+
// esbuild rewrite each of these into asset emits and substitute the runtime URL. Any
|
|
18
|
+
// expression chained off the URL (e.g. .href) defeats the AST matcher in webpack 5,
|
|
19
|
+
// so we keep these expressions unchained and call String(...) at the use site below.
|
|
20
|
+
//
|
|
21
|
+
// We coerce with String(...) (not .href) because webpack 5 may produce URL-like wrappers
|
|
22
|
+
// whose .href is a getter that returns the URL instance itself or otherwise carries
|
|
23
|
+
// non-cloneable own properties. String(...) goes through .toString() which is guaranteed
|
|
24
|
+
// to return a primitive string and survives postMessage's structured clone.
|
|
25
|
+
const GLTF_STORAGE_URL = new URL("./gltf-storage.js", import.meta.url);
|
|
26
|
+
const GLTF_MERGER_URL = new URL("./gltf-merger.js", import.meta.url);
|
|
27
|
+
const PREF_GLTF_MERGER_WASM_URL = new URL("./PrefGltfMergerWasm.js", import.meta.url);
|
|
28
|
+
// PrefGltfMergerWasm.js is an Emscripten module that loads its companion .wasm
|
|
29
|
+
// at runtime via Module.locateFile. We MUST also resolve the .wasm here so the
|
|
30
|
+
// bundler emits both files; without this declaration webpack/Vite/etc. ignore
|
|
31
|
+
// the .wasm and Emscripten's default sibling-by-name lookup gets a 404 because
|
|
32
|
+
// the consumer's bundler hashed the .js to a different filename. The worker then
|
|
33
|
+
// passes both URLs to gltf-merger.js so locateFile can return the right one.
|
|
34
|
+
const PREF_GLTF_MERGER_WASM_BIN_URL = new URL("./PrefGltfMergerWasm.wasm", import.meta.url);
|
|
35
|
+
|
|
36
|
+
// Per-method arg adapters: equivalent to the typed shims in prefconfigurator-api.ts
|
|
37
|
+
// that today only exist in the .ts and not in the runtime .js, forcing consumers to
|
|
38
|
+
// reimplement them. Centralizing them here means npm consumers get the typed DX
|
|
39
|
+
// without a custom Proxy on their side.
|
|
40
|
+
const ARG_ADAPTERS = {
|
|
41
|
+
UpdateOptions: ([optionList, changedOptionNames]) => [
|
|
42
|
+
JSON.stringify(optionList),
|
|
43
|
+
JSON.stringify(changedOptionNames ?? []),
|
|
44
|
+
],
|
|
45
|
+
SetDimensions: ([modelId, updates]) => [modelId, JSON.stringify(updates)],
|
|
46
|
+
SetOptions: ([modelId, updates]) => [modelId, JSON.stringify(updates)],
|
|
47
|
+
GetModelImage: ([modelId, config, sceneName, optionList]) => [
|
|
48
|
+
modelId,
|
|
49
|
+
JSON.stringify(config),
|
|
50
|
+
sceneName ?? null,
|
|
51
|
+
optionList != null ? JSON.stringify(optionList) : null,
|
|
52
|
+
],
|
|
53
|
+
GetUsdImages: ([request, optionList]) => [
|
|
54
|
+
JSON.stringify(request),
|
|
55
|
+
optionList != null ? JSON.stringify(optionList) : null,
|
|
56
|
+
],
|
|
57
|
+
GetUsdImagesHcStatus: ([request, optionList]) => [
|
|
58
|
+
JSON.stringify(request),
|
|
59
|
+
JSON.stringify(optionList ?? null),
|
|
60
|
+
],
|
|
61
|
+
GetModelImageHcStatus: ([modelId, config]) => [modelId, JSON.stringify(config)],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// InitializeFromEnvJson is implicit — init() runs it during boot and the Proxy hides it.
|
|
65
|
+
const HIDDEN_METHODS = new Set(["InitializeFromEnvJson"]);
|
|
66
|
+
|
|
67
|
+
// String properties that must NOT be intercepted as remote method calls.
|
|
68
|
+
//
|
|
69
|
+
// The Promise resolution algorithm probes any returned value for `then` to
|
|
70
|
+
// decide whether it is a thenable; if our Proxy returns a function for `then`,
|
|
71
|
+
// JavaScript will call it with native (resolve, reject) callbacks, which are
|
|
72
|
+
// not structured-cloneable and crash postMessage with DataCloneError. Returning
|
|
73
|
+
// undefined makes the Proxy look like a plain object to the Promise machinery.
|
|
74
|
+
//
|
|
75
|
+
// `catch` / `finally` are kept here for the same reason in long Promise chains.
|
|
76
|
+
// `__esModule` is webpack's interop marker — if our Proxy returned an async
|
|
77
|
+
// function for it, dynamic imports could mis-classify the module.
|
|
78
|
+
//
|
|
79
|
+
// All Symbol-keyed probes (Symbol.toPrimitive / Symbol.iterator / Symbol.asyncIterator
|
|
80
|
+
// / Symbol.toStringTag / Symbol.dispose / etc.) are filtered earlier by the
|
|
81
|
+
// `typeof prop === "symbol"` check in the get trap, so they don't appear here.
|
|
82
|
+
const NON_REMOTE_KEYS = new Set([
|
|
83
|
+
"then",
|
|
84
|
+
"catch",
|
|
85
|
+
"finally",
|
|
86
|
+
"constructor",
|
|
87
|
+
"toJSON",
|
|
88
|
+
"toString",
|
|
89
|
+
"valueOf",
|
|
90
|
+
"__esModule",
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
function rebuildError(serialized) {
|
|
94
|
+
const message = serialized?.message ?? "Unknown SDK error";
|
|
95
|
+
const err = new Error(message);
|
|
96
|
+
if (serialized?.name) err.name = serialized.name;
|
|
97
|
+
if (serialized?.stack) err.stack = serialized.stack;
|
|
98
|
+
if (serialized?.code !== undefined) err.code = serialized.code;
|
|
99
|
+
if (serialized?.source !== undefined) err.source = serialized.source;
|
|
100
|
+
if (serialized?.httpStatus !== undefined) err.httpStatus = serialized.httpStatus;
|
|
101
|
+
if (serialized?.requestId !== undefined) err.requestId = serialized.requestId;
|
|
102
|
+
if (serialized?.correlationId !== undefined) err.correlationId = serialized.correlationId;
|
|
103
|
+
if (serialized?.traceParent !== undefined) err.traceParent = serialized.traceParent;
|
|
104
|
+
if (serialized?.detail !== undefined) err.detail = serialized.detail;
|
|
105
|
+
if (serialized?.callId !== undefined) err.callId = serialized.callId;
|
|
106
|
+
if (serialized?.result !== undefined) err.result = serialized.result;
|
|
107
|
+
return err;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function makeRpc(worker) {
|
|
111
|
+
let nextId = 1;
|
|
112
|
+
const pending = new Map();
|
|
113
|
+
let terminated = false;
|
|
114
|
+
|
|
115
|
+
worker.addEventListener("message", (ev) => {
|
|
116
|
+
const msg = ev?.data;
|
|
117
|
+
if (!msg || typeof msg !== "object" || msg.id === undefined) return;
|
|
118
|
+
const ctl = pending.get(msg.id);
|
|
119
|
+
if (!ctl) return;
|
|
120
|
+
pending.delete(msg.id);
|
|
121
|
+
if (msg.type === "result" || msg.type === "init-result") ctl.resolve(msg);
|
|
122
|
+
else if (msg.type === "error" || msg.type === "init-error") ctl.reject(rebuildError(msg.error));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
worker.addEventListener("error", (ev) => {
|
|
126
|
+
const err = new Error(ev?.message ?? "PrefConfigurator worker crashed");
|
|
127
|
+
for (const ctl of pending.values()) ctl.reject(err);
|
|
128
|
+
pending.clear();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
function send(message) {
|
|
132
|
+
if (terminated) return Promise.reject(new Error("PrefConfigurator SDK has been disposed."));
|
|
133
|
+
const id = nextId++;
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
pending.set(id, { resolve, reject });
|
|
136
|
+
worker.postMessage({ ...message, id });
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function dispose() {
|
|
141
|
+
terminated = true;
|
|
142
|
+
const err = new Error("PrefConfigurator SDK has been disposed.");
|
|
143
|
+
for (const ctl of pending.values()) ctl.reject(err);
|
|
144
|
+
pending.clear();
|
|
145
|
+
worker.terminate();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { send, dispose };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Boots the SDK runtime in a Web Worker and returns a proxy that mirrors the
|
|
153
|
+
* BoundApi shape exported by the SDK's TypeScript declarations.
|
|
154
|
+
*
|
|
155
|
+
* @param {object|string} env EnvJson object (or pre-serialized JSON string).
|
|
156
|
+
* @param {object} [opts]
|
|
157
|
+
* @param {URL|string} [opts.workerUrl] Override the worker URL (tests, custom hosting).
|
|
158
|
+
* @param {boolean} [opts.diagnosticTracing] Forwarded to dotnet.withDiagnosticTracing.
|
|
159
|
+
* @returns {Promise<object>} a proxy whose methods unwrap OperationResult<T> via ensureOk.
|
|
160
|
+
*/
|
|
161
|
+
export async function init(env, opts) {
|
|
162
|
+
if (typeof Worker === "undefined") {
|
|
163
|
+
throw new Error("PrefConfigurator SDK requires a browser/worker environment with Worker support.");
|
|
164
|
+
}
|
|
165
|
+
const options = opts ?? {};
|
|
166
|
+
const workerUrl = options.workerUrl ?? new URL("./sdk-worker.js", import.meta.url);
|
|
167
|
+
const worker = new Worker(workerUrl, { type: "module" });
|
|
168
|
+
const rpc = makeRpc(worker);
|
|
169
|
+
|
|
170
|
+
const envJson = typeof env === "string" ? env : JSON.stringify(env);
|
|
171
|
+
|
|
172
|
+
// Build a plain { name: string } map. We force each value through String(...) and
|
|
173
|
+
// copy with Object.assign-style key iteration so the postMessage clone never sees a
|
|
174
|
+
// URL instance, a webpack wrapper, a getter, or any non-cloneable native function.
|
|
175
|
+
const frameworkAssets = {};
|
|
176
|
+
for (const name of Object.keys(FRAMEWORK_ASSETS)) {
|
|
177
|
+
frameworkAssets[name] = String(FRAMEWORK_ASSETS[name]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await rpc.send({
|
|
182
|
+
type: "init",
|
|
183
|
+
envJson,
|
|
184
|
+
diagnosticTracing: !!options.diagnosticTracing,
|
|
185
|
+
frameworkAssets,
|
|
186
|
+
gltfStorageUrl: String(GLTF_STORAGE_URL),
|
|
187
|
+
gltfMergerUrl: String(GLTF_MERGER_URL),
|
|
188
|
+
prefGltfMergerWasmUrl: String(PREF_GLTF_MERGER_WASM_URL),
|
|
189
|
+
prefGltfMergerWasmBinUrl: String(PREF_GLTF_MERGER_WASM_BIN_URL),
|
|
190
|
+
});
|
|
191
|
+
} catch (err) {
|
|
192
|
+
rpc.dispose();
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const proxy = new Proxy(
|
|
197
|
+
{},
|
|
198
|
+
{
|
|
199
|
+
get(_target, prop) {
|
|
200
|
+
if (prop === "dispose" || prop === "terminate" || prop === Symbol.dispose) {
|
|
201
|
+
return () => rpc.dispose();
|
|
202
|
+
}
|
|
203
|
+
// Hide thenable / iterator / serialization probes from the Promise and
|
|
204
|
+
// host runtimes — see NON_REMOTE_KEYS for the rationale.
|
|
205
|
+
if (typeof prop === "symbol") return undefined;
|
|
206
|
+
if (NON_REMOTE_KEYS.has(prop)) return undefined;
|
|
207
|
+
if (typeof prop !== "string") return undefined;
|
|
208
|
+
if (HIDDEN_METHODS.has(prop)) {
|
|
209
|
+
return () => {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`${prop} is invoked implicitly by init() and is not callable on the SDK proxy.`,
|
|
212
|
+
);
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return async (...args) => {
|
|
217
|
+
const adapter = ARG_ADAPTERS[prop];
|
|
218
|
+
const finalArgs = adapter ? adapter(args) : args;
|
|
219
|
+
const res = await rpc.send({ type: "call", method: prop, args: finalArgs });
|
|
220
|
+
return ensureOk(res.value);
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return proxy;
|
|
227
|
+
}
|
package/js/sdk-worker.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// PrefConfigurator WASM SDK — Web Worker entry.
|
|
2
|
+
//
|
|
3
|
+
// This module is intentionally LEAF: it has no static imports of SDK assets. The main
|
|
4
|
+
// thread (sdk-init.js) resolves every bundler-emitted URL with new URL(literal,
|
|
5
|
+
// import.meta.url), pushes the resulting map down via the init message, and the worker
|
|
6
|
+
// dynamic-imports each one at runtime. Keeping the worker leaf lets bundlers (webpack 5,
|
|
7
|
+
// Vite, esbuild, Rollup) treat sdk-worker.js as a self-contained file: they don't have
|
|
8
|
+
// to follow imports across the worker boundary, which Next.js + webpack 5 in particular
|
|
9
|
+
// does not do reliably as of 16.x.
|
|
10
|
+
|
|
11
|
+
let api = null;
|
|
12
|
+
let bootPromise = null;
|
|
13
|
+
|
|
14
|
+
async function bootDotnet({ envJson, diagnosticTracing, frameworkAssets, gltfStorageUrl, gltfMergerUrl, prefGltfMergerWasmUrl, prefGltfMergerWasmBinUrl }) {
|
|
15
|
+
const dotnetUrl = frameworkAssets["dotnet.js"];
|
|
16
|
+
if (!dotnetUrl) throw new Error("Framework asset manifest missing 'dotnet.js'.");
|
|
17
|
+
|
|
18
|
+
// gltf-merger.js consults these globals to locate its Emscripten module at runtime:
|
|
19
|
+
// __PREF_GLTF_MERGER_WASM_PATH → URL of PrefGltfMergerWasm.js (the loader)
|
|
20
|
+
// __PREF_GLTF_MERGER_WASM_BIN_PATH → URL of PrefGltfMergerWasm.wasm (the binary)
|
|
21
|
+
// Both are needed because the consumer's bundler hashes them independently and
|
|
22
|
+
// Emscripten's default sibling-by-filename lookup fails when the hashes differ.
|
|
23
|
+
self.__PREF_GLTF_MERGER_WASM_PATH = prefGltfMergerWasmUrl;
|
|
24
|
+
self.__PREF_GLTF_MERGER_WASM_BIN_PATH = prefGltfMergerWasmBinUrl;
|
|
25
|
+
|
|
26
|
+
// All three URLs (dotnet, gltf-storage, gltf-merger) are already resolved by the
|
|
27
|
+
// consumer's bundler at build time. webpackIgnore / @vite-ignore keep both bundlers
|
|
28
|
+
// from re-analyzing these dynamic imports.
|
|
29
|
+
const dotnetMod = await import(/* @vite-ignore */ /* webpackIgnore: true */ dotnetUrl);
|
|
30
|
+
const dotnet = dotnetMod.dotnet ?? dotnetMod.default?.dotnet ?? dotnetMod.default;
|
|
31
|
+
if (!dotnet) throw new Error("dotnet.js loaded but its 'dotnet' export is missing.");
|
|
32
|
+
|
|
33
|
+
const builder = dotnet
|
|
34
|
+
.withResourceLoader((_type, name, defaultUri /*, _hash, _behavior */) => {
|
|
35
|
+
const url = frameworkAssets[name];
|
|
36
|
+
return url ?? defaultUri;
|
|
37
|
+
})
|
|
38
|
+
.withApplicationArguments("start");
|
|
39
|
+
|
|
40
|
+
if (diagnosticTracing) builder.withDiagnosticTracing(true);
|
|
41
|
+
|
|
42
|
+
const runtime = await builder.create();
|
|
43
|
+
const { getConfig, getAssemblyExports, setModuleImports, runMain } = runtime;
|
|
44
|
+
if (typeof setModuleImports !== "function") {
|
|
45
|
+
throw new Error("dotnet runtime does not expose setModuleImports — required by the SDK.");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Register [JSImport] target modules. Replaces JSHost.ImportAsync from the C# side.
|
|
49
|
+
const [gltfStorage, gltfMerger] = await Promise.all([
|
|
50
|
+
import(/* @vite-ignore */ /* webpackIgnore: true */ gltfStorageUrl),
|
|
51
|
+
import(/* @vite-ignore */ /* webpackIgnore: true */ gltfMergerUrl),
|
|
52
|
+
]);
|
|
53
|
+
setModuleImports("gltf-storage", gltfStorage);
|
|
54
|
+
setModuleImports("gltf-merger", gltfMerger);
|
|
55
|
+
|
|
56
|
+
const cfg = getConfig();
|
|
57
|
+
const mainAssembly = cfg.mainAssemblyName;
|
|
58
|
+
if (!mainAssembly) throw new Error("dotnet.js config missing mainAssemblyName.");
|
|
59
|
+
|
|
60
|
+
const exportsAsm = await getAssemblyExports(mainAssembly);
|
|
61
|
+
const ns = exportsAsm?.PrefConfigurator?.Wasm?.JsInteropApi;
|
|
62
|
+
if (!ns) throw new Error("PrefConfigurator.Wasm.JsInteropApi is not exported by the assembly.");
|
|
63
|
+
api = ns;
|
|
64
|
+
|
|
65
|
+
await api.InitializeFromEnvJson(envJson);
|
|
66
|
+
|
|
67
|
+
// Fire Program.Main() so JsInteropApi.Initialize(host.Services) sets _services and
|
|
68
|
+
// the long-running host.RunAsync stays alive. Yield once so its synchronous prologue
|
|
69
|
+
// completes before the main thread starts dispatching calls.
|
|
70
|
+
void runMain();
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function serializeError(err) {
|
|
75
|
+
if (err && typeof err === "object") {
|
|
76
|
+
return {
|
|
77
|
+
name: err.name ?? "Error",
|
|
78
|
+
message: err.message ?? String(err),
|
|
79
|
+
stack: err.stack,
|
|
80
|
+
// Custom fields populated by ensureOk on the main side; pass them through so
|
|
81
|
+
// the rebuilt Error keeps its diagnostic shape.
|
|
82
|
+
code: err.code,
|
|
83
|
+
source: err.source,
|
|
84
|
+
httpStatus: err.httpStatus,
|
|
85
|
+
requestId: err.requestId,
|
|
86
|
+
correlationId: err.correlationId,
|
|
87
|
+
traceParent: err.traceParent,
|
|
88
|
+
detail: err.detail,
|
|
89
|
+
callId: err.callId,
|
|
90
|
+
result: err.result,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return { name: "Error", message: String(err) };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
self.addEventListener("message", async (ev) => {
|
|
97
|
+
const msg = ev?.data;
|
|
98
|
+
if (!msg || typeof msg !== "object") return;
|
|
99
|
+
|
|
100
|
+
if (msg.type === "init") {
|
|
101
|
+
try {
|
|
102
|
+
if (!bootPromise) bootPromise = bootDotnet(msg);
|
|
103
|
+
await bootPromise;
|
|
104
|
+
const methods = Object.keys(api).filter((k) => typeof api[k] === "function");
|
|
105
|
+
self.postMessage({ id: msg.id, type: "init-result", methods });
|
|
106
|
+
} catch (err) {
|
|
107
|
+
bootPromise = null;
|
|
108
|
+
self.postMessage({ id: msg.id, type: "init-error", error: serializeError(err) });
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (msg.type === "call") {
|
|
114
|
+
if (!api) {
|
|
115
|
+
self.postMessage({
|
|
116
|
+
id: msg.id,
|
|
117
|
+
type: "error",
|
|
118
|
+
error: serializeError(new Error("PrefConfigurator SDK is not initialized.")),
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const fn = api[msg.method];
|
|
123
|
+
if (typeof fn !== "function") {
|
|
124
|
+
self.postMessage({
|
|
125
|
+
id: msg.id,
|
|
126
|
+
type: "error",
|
|
127
|
+
error: serializeError(new Error(`Unknown SDK method: ${msg.method}`)),
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const value = await fn(...(msg.args ?? []));
|
|
133
|
+
self.postMessage({ id: msg.id, type: "result", value });
|
|
134
|
+
} catch (err) {
|
|
135
|
+
self.postMessage({ id: msg.id, type: "error", error: serializeError(err) });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
+
"version": "0.2.0-beta.1",
|
|
3
|
+
"name": "@preference-sl/pref-configurator-sdk",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./js/prefconfigurator-api": {
|
|
8
|
+
"types": "./js/prefconfigurator-api.d.ts",
|
|
9
|
+
"default": "./js/prefconfigurator-api.js"
|
|
10
|
+
},
|
|
11
|
+
"./js/sdk-init": {
|
|
12
|
+
"types": "./js/sdk-init.d.ts",
|
|
13
|
+
"default": "./js/sdk-init.js"
|
|
14
|
+
},
|
|
15
|
+
"./js/sdk-worker": "./js/sdk-worker.js",
|
|
16
|
+
"./index.html": "./index.html",
|
|
17
|
+
"./js/*": {
|
|
18
|
+
"types": "./js/*.d.ts",
|
|
19
|
+
"default": "./js/*.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json",
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./index.d.ts",
|
|
24
|
+
"default": "./index.js"
|
|
25
|
+
},
|
|
26
|
+
"./_framework/*": "./_framework/*"
|
|
27
|
+
},
|
|
28
|
+
"private": false,
|
|
29
|
+
"types": "index.d.ts",
|
|
30
|
+
"module": "index.js",
|
|
2
31
|
"description": ".NET WebAssembly SDK that embeds Preference's business logic for building product configurators (windows, doors, joinery) in browser-based ecommerce platforms.",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
32
|
+
"author": "Alex Moreno Palacio <amoreno@preference.es>",
|
|
33
|
+
"sideEffects": false,
|
|
5
34
|
"files": [
|
|
6
35
|
"_framework/**",
|
|
7
36
|
"**/*.js",
|
|
@@ -10,24 +39,5 @@
|
|
|
10
39
|
"index.html",
|
|
11
40
|
"README.md"
|
|
12
41
|
],
|
|
13
|
-
"
|
|
14
|
-
"private": false,
|
|
15
|
-
"name": "@preference-sl/pref-configurator-sdk",
|
|
16
|
-
"scripts": {
|
|
17
|
-
"postinstall": "node scripts/postinstall.js"
|
|
18
|
-
},
|
|
19
|
-
"author": "Alex Moreno Palacio <amoreno@preference.es>",
|
|
20
|
-
"exports": {
|
|
21
|
-
".": {
|
|
22
|
-
"types": "./main.d.ts",
|
|
23
|
-
"default": "./main.js"
|
|
24
|
-
},
|
|
25
|
-
"./package.json": "./package.json",
|
|
26
|
-
"./index.html": "./index.html",
|
|
27
|
-
"./js/prefconfigurator-api": {
|
|
28
|
-
"types": "./js/prefconfigurator-api.d.ts",
|
|
29
|
-
"default": "./js/prefconfigurator-api.js"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"types": "main.d.ts"
|
|
42
|
+
"type": "module"
|
|
33
43
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/main.d.ts
DELETED
package/main.js
DELETED
package/scripts/postinstall.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable no-console */
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
/* ---------- utils ---------- */
|
|
7
|
-
function walk(root, cb) {
|
|
8
|
-
const st = [root];
|
|
9
|
-
while (st.length) {
|
|
10
|
-
const d = st.pop();
|
|
11
|
-
let entries;
|
|
12
|
-
try { entries = fs.readdirSync(d, { withFileTypes: true }); }
|
|
13
|
-
catch { continue; }
|
|
14
|
-
for (const e of entries) {
|
|
15
|
-
const abs = path.join(d, e.name);
|
|
16
|
-
if (e.isDirectory()) st.push(abs);
|
|
17
|
-
else if (e.isFile()) cb(abs);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function ensureDir(p) { fs.mkdirSync(p, { recursive: true }); }
|
|
23
|
-
function ensureDirForFile(filePath) { ensureDir(path.dirname(filePath)); }
|
|
24
|
-
|
|
25
|
-
function rmrf(p) {
|
|
26
|
-
try { fs.rmSync(p, { recursive: true, force: true }); } catch { }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function copyDirRecursive(src, dst) {
|
|
30
|
-
if (!fs.existsSync(src)) return;
|
|
31
|
-
ensureDir(dst);
|
|
32
|
-
for (const e of fs.readdirSync(src, { withFileTypes: true })) {
|
|
33
|
-
const s = path.join(src, e.name);
|
|
34
|
-
const d = path.join(dst, e.name);
|
|
35
|
-
if (e.isDirectory()) copyDirRecursive(s, d);
|
|
36
|
-
else if (e.isFile()) fs.copyFileSync(s, d);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function resolvePackageName(pkgRoot) {
|
|
41
|
-
if (process.env.npm_package_name) {
|
|
42
|
-
return String(process.env.npm_package_name).replace(/^@[^/]+\//, "").toLowerCase();
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, "package.json"), "utf8"));
|
|
46
|
-
if (pkg && pkg.name) return String(pkg.name).replace(/^@[^/]+\//, "").toLowerCase();
|
|
47
|
-
} catch { }
|
|
48
|
-
try { return path.basename(pkgRoot).toLowerCase(); } catch { }
|
|
49
|
-
return "package";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* ---------- main ---------- */
|
|
53
|
-
try {
|
|
54
|
-
// package root (we are in .../scripts)
|
|
55
|
-
const pkgRoot = path.resolve(__dirname, "..");
|
|
56
|
-
const pkgName = resolvePackageName(pkgRoot);
|
|
57
|
-
|
|
58
|
-
const srcFramework = path.join(pkgRoot, "_framework");
|
|
59
|
-
const srcMain = path.join(pkgRoot, "main.js");
|
|
60
|
-
const srcIndex = path.join(pkgRoot, "index.html");
|
|
61
|
-
|
|
62
|
-
// consumer public dir
|
|
63
|
-
const consumerRoot = process.env.INIT_CWD || process.env.npm_config_local_prefix || process.cwd();
|
|
64
|
-
const candidates = [process.env.PUBLIC_DIR, "public", "wwwroot", path.join("src", "assets")].filter(Boolean);
|
|
65
|
-
|
|
66
|
-
let targetPublic = null;
|
|
67
|
-
for (const c of candidates) {
|
|
68
|
-
const abs = path.isAbsolute(c) ? c : path.resolve(consumerRoot, c);
|
|
69
|
-
if (fs.existsSync(abs)) { targetPublic = abs; break; }
|
|
70
|
-
}
|
|
71
|
-
if (!targetPublic) targetPublic = path.resolve(consumerRoot, "public");
|
|
72
|
-
|
|
73
|
-
const ns = "preference-sl";
|
|
74
|
-
const destRoot = path.join(targetPublic, ns, pkgName);
|
|
75
|
-
|
|
76
|
-
// ALWAYS start fresh: remove destination completely
|
|
77
|
-
rmrf(destRoot);
|
|
78
|
-
ensureDir(destRoot);
|
|
79
|
-
|
|
80
|
-
// 1) Copy runtime (_framework) — overwrite
|
|
81
|
-
const wasmSubdir = process.env.WASM_SUBDIR || "_framework";
|
|
82
|
-
const destFramework = path.join(destRoot, wasmSubdir);
|
|
83
|
-
copyDirRecursive(srcFramework, destFramework);
|
|
84
|
-
console.log(`[${pkgName}] Copied WASM → ${destFramework}`);
|
|
85
|
-
|
|
86
|
-
// 2) main.js — overwrite
|
|
87
|
-
if (fs.existsSync(srcMain)) {
|
|
88
|
-
const destMain = path.join(destRoot, "main.js");
|
|
89
|
-
ensureDirForFile(destMain);
|
|
90
|
-
fs.copyFileSync(srcMain, destMain);
|
|
91
|
-
console.log(`[${pkgName}] Installed main.js at ${destMain}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 3) index.html → index.demo.html — overwrite
|
|
95
|
-
if (fs.existsSync(srcIndex)) {
|
|
96
|
-
const destDemo = path.join(destRoot, "index.demo.html");
|
|
97
|
-
ensureDirForFile(destDemo);
|
|
98
|
-
fs.copyFileSync(srcIndex, destDemo);
|
|
99
|
-
console.log(`[${pkgName}] Installed index.demo.html at ${destDemo}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 4) Extra JS / WASM / source maps outside _framework — overwrite
|
|
103
|
-
// Includes PrefGltfMergerWasm.js and .wasm so the consumer doesn't need
|
|
104
|
-
// a separate npm package for the GLTF merger.
|
|
105
|
-
console.log(`[${pkgName}] Copying extra JS/WASM → ${destRoot}`);
|
|
106
|
-
const frameworkRoot = path.resolve(path.join(pkgRoot, "_framework"));
|
|
107
|
-
const exclude = new Set([
|
|
108
|
-
path.resolve(path.join(pkgRoot, "scripts", "postinstall.js")),
|
|
109
|
-
]);
|
|
110
|
-
// Source maps and symbols are intentionally not shipped (see Pack-Wasm.ps1).
|
|
111
|
-
const copyableExtensions = [".js", ".d.ts", ".wasm"];
|
|
112
|
-
|
|
113
|
-
walk(pkgRoot, (abs0) => {
|
|
114
|
-
const abs = path.resolve(abs0);
|
|
115
|
-
const lower = abs.toLowerCase();
|
|
116
|
-
if (!copyableExtensions.some((ext) => lower.endsWith(ext))) return;
|
|
117
|
-
if (abs.startsWith(frameworkRoot)) return; // skip _framework
|
|
118
|
-
if (exclude.has(abs)) return; // skip postinstall itself
|
|
119
|
-
|
|
120
|
-
const rel = path.relative(pkgRoot, abs); // e.g., "js/foo.js" or "js/PrefGltfMergerWasm.wasm"
|
|
121
|
-
const dest = path.join(destRoot, rel);
|
|
122
|
-
ensureDirForFile(dest);
|
|
123
|
-
fs.copyFileSync(abs, dest); // OVERWRITE
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
} catch (err) {
|
|
127
|
-
console.warn(`[postinstall] Warning: copy failed: ${err && (err.message || err)}`);
|
|
128
|
-
}
|