@ptkl/toolkit 0.8.0 → 0.8.12
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/bin/toolkit.js +3 -2
- package/dist/commands/apiUsers.js +1 -1
- package/dist/commands/apps.js +5 -2
- package/dist/commands/component.js +11 -12
- package/dist/commands/forge.js +103 -25
- package/dist/commands/functions.js +1 -1
- package/dist/commands/generate-types.js +38 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/profile.js +1 -1
- package/dist/commands/role.js +1 -1
- package/dist/commands/users.js +1 -1
- package/dist/commands/validate-idl.js +115 -0
- package/dist/lib/idlToDts.js +242 -0
- package/dist/lib/util.js +2 -2
- package/package.json +2 -2
- package/dist/builder/sdk5/webcomponents.js +0 -38
- package/dist/lib/importMapGenerator.js +0 -96
package/dist/bin/toolkit.js
CHANGED
|
@@ -14,12 +14,13 @@ program.hook('preAction', (thisCommand, actionCommand) => {
|
|
|
14
14
|
});
|
|
15
15
|
commands.forEach(c => program.addCommand(c));
|
|
16
16
|
program.parseAsync(process.argv).catch((err) => {
|
|
17
|
+
console.log("An error occurred while executing the command.", err);
|
|
17
18
|
const { response } = err;
|
|
18
19
|
if (response && response.data) {
|
|
19
|
-
console.error(response.data.message);
|
|
20
|
+
console.error(response.data.message || "An error occurred with the API request.");
|
|
20
21
|
}
|
|
21
22
|
else {
|
|
22
|
-
console.error(err.message || err);
|
|
23
|
+
console.error(err.message || err || "An unknown error occurred.");
|
|
23
24
|
}
|
|
24
25
|
process.exit(1);
|
|
25
26
|
});
|
package/dist/commands/apps.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
2
|
import util from "../lib/util.js";
|
|
3
|
-
import Api from "@ptkl/sdk";
|
|
3
|
+
import { Platform as Api } from "@ptkl/sdk";
|
|
4
4
|
import { build, createServer } from 'vite';
|
|
5
5
|
import { c } from 'tar';
|
|
6
6
|
import { Writable } from "stream";
|
|
@@ -129,7 +129,7 @@ class AppsCommand {
|
|
|
129
129
|
async bundle(options) {
|
|
130
130
|
const { path, upload } = options;
|
|
131
131
|
const module = await import(`${path}/ptkl.config.js`);
|
|
132
|
-
const { views, name, version, distPath, icon, label, permissions, scripts, } = module.default ?? {};
|
|
132
|
+
const { views, name, version, distPath, icon, label, permissions, install_permissions, runtime_permissions, requires, scripts, } = module.default ?? {};
|
|
133
133
|
// build manifest file
|
|
134
134
|
const manifest = {
|
|
135
135
|
name,
|
|
@@ -138,6 +138,9 @@ class AppsCommand {
|
|
|
138
138
|
label,
|
|
139
139
|
icon,
|
|
140
140
|
permissions,
|
|
141
|
+
install_permissions: install_permissions ?? [],
|
|
142
|
+
runtime_permissions: runtime_permissions ?? [],
|
|
143
|
+
requires: requires ?? null,
|
|
141
144
|
scripts,
|
|
142
145
|
};
|
|
143
146
|
const profile = Util.getCurrentProfile();
|
|
@@ -4,7 +4,7 @@ import { WebSocketServer } from "ws";
|
|
|
4
4
|
import { resolve, join, dirname } from "path";
|
|
5
5
|
import { rollup } from "rollup";
|
|
6
6
|
import util from "../lib/util.js";
|
|
7
|
-
import Api from "@ptkl/sdk";
|
|
7
|
+
import { Platform as Api } from "@ptkl/sdk";
|
|
8
8
|
import { mkdirSync, writeFileSync, rmSync, readdirSync, existsSync, readFileSync } from "fs";
|
|
9
9
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
10
10
|
// @ts-ignore
|
|
@@ -114,7 +114,7 @@ class ComponentCommand {
|
|
|
114
114
|
// Merge user packages with internal dependencies
|
|
115
115
|
const userPackages = componentConfig.packages || {};
|
|
116
116
|
const dependencies = {
|
|
117
|
-
'@ptkl/sdk': '^0.
|
|
117
|
+
'@ptkl/sdk': '^1.0.0',
|
|
118
118
|
// Lit is bundled into the component, not provided by platform
|
|
119
119
|
...(framework === 'lit' ? {
|
|
120
120
|
'lit': frameworkVersion ? `^${frameworkVersion}` : '^3.0.0'
|
|
@@ -301,16 +301,15 @@ class ComponentCommand {
|
|
|
301
301
|
console.error(`❌ Build failed: ${error.message}`);
|
|
302
302
|
socket.send(JSON.stringify({ error: error.message }));
|
|
303
303
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// }
|
|
304
|
+
finally {
|
|
305
|
+
try {
|
|
306
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
307
|
+
console.log(`Cleaned up temporary directory: ${tempDir}`);
|
|
308
|
+
}
|
|
309
|
+
catch (cleanupError) {
|
|
310
|
+
console.error(`Failed to clean up temporary directory: ${cleanupError}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
314
313
|
}
|
|
315
314
|
async buildLegacy(nodes, version, engine, socket) {
|
|
316
315
|
try {
|
package/dist/commands/forge.js
CHANGED
|
@@ -2,7 +2,8 @@ import { Command } from "commander";
|
|
|
2
2
|
import { build, createServer } from 'vite';
|
|
3
3
|
import { c } from 'tar';
|
|
4
4
|
import { Writable } from "stream";
|
|
5
|
-
import { writeFileSync } from "fs";
|
|
5
|
+
import { writeFileSync, readFileSync } from "fs";
|
|
6
|
+
import axios from 'axios';
|
|
6
7
|
import Util from "../lib/util.js";
|
|
7
8
|
import { join } from 'path';
|
|
8
9
|
class ForgeCommand {
|
|
@@ -26,7 +27,19 @@ class ForgeCommand {
|
|
|
26
27
|
.action(this.runDev))
|
|
27
28
|
.addCommand(new Command("list")
|
|
28
29
|
.description("List all available apps")
|
|
29
|
-
.action(this.listApps))
|
|
30
|
+
.action(this.listApps))
|
|
31
|
+
.addCommand(new Command("install")
|
|
32
|
+
.description("Dry-run the app install script locally against the active profile (no actual installation). Runs for both dev and live by default.")
|
|
33
|
+
.requiredOption("-p, --path <path>", "Path to the app directory (where ptkl.config.js is located)")
|
|
34
|
+
.option("--env <env>", "Run for a specific env only (dev or live). Omit to run for both.")
|
|
35
|
+
.option("-b, --bundle", "Build the bundle before running the install script")
|
|
36
|
+
.action((options) => this.install(options)))
|
|
37
|
+
.addCommand(new Command("uninstall")
|
|
38
|
+
.description("Dry-run the app uninstall script locally against the active profile (no actual uninstallation). Runs for both dev and live by default.")
|
|
39
|
+
.requiredOption("-p, --path <path>", "Path to the app directory (where ptkl.config.js is located)")
|
|
40
|
+
.option("--env <env>", "Run for a specific env only (dev or live). Omit to run for both.")
|
|
41
|
+
.option("-b, --bundle", "Build the bundle before running the uninstall script")
|
|
42
|
+
.action((options) => this.uninstall(options)));
|
|
30
43
|
}
|
|
31
44
|
async bundle(options) {
|
|
32
45
|
const { path, upload } = options;
|
|
@@ -36,7 +49,13 @@ class ForgeCommand {
|
|
|
36
49
|
// Change to the app directory
|
|
37
50
|
process.chdir(path);
|
|
38
51
|
const module = await import(`${path}/ptkl.config.js`);
|
|
39
|
-
const { views, name, version, distPath, icon, type, label, permissions, scripts, ssrRenderer, } = module.default ?? {};
|
|
52
|
+
const { views, name, version, distPath, icon, type, label, permissions, install_permissions, runtime_permissions, entitlements, requires, scripts, ssrRenderer, } = module.default ?? {};
|
|
53
|
+
// Validate combined permissions limit
|
|
54
|
+
const rtPerms = runtime_permissions ?? [];
|
|
55
|
+
const ents = entitlements ?? [];
|
|
56
|
+
if (rtPerms.length + ents.length > 100) {
|
|
57
|
+
throw new Error(`Combined runtime_permissions (${rtPerms.length}) and entitlements (${ents.length}) must not exceed 100 entries`);
|
|
58
|
+
}
|
|
40
59
|
// build manifest file
|
|
41
60
|
const manifest = {
|
|
42
61
|
name,
|
|
@@ -45,6 +64,10 @@ class ForgeCommand {
|
|
|
45
64
|
label,
|
|
46
65
|
icon,
|
|
47
66
|
permissions,
|
|
67
|
+
install_permissions: install_permissions ?? [],
|
|
68
|
+
runtime_permissions: runtime_permissions ?? [],
|
|
69
|
+
entitlements: entitlements ?? [],
|
|
70
|
+
requires: requires ?? null,
|
|
48
71
|
scripts: {},
|
|
49
72
|
type: type || 'platform', // default to 'platform' if not specified
|
|
50
73
|
ssrRenderer,
|
|
@@ -169,10 +192,7 @@ class ForgeCommand {
|
|
|
169
192
|
format: 'esm',
|
|
170
193
|
entryFileNames: `[name].script.js`,
|
|
171
194
|
assetFileNames: (assetInfo) => {
|
|
172
|
-
return '[name].[ext]';
|
|
173
|
-
},
|
|
174
|
-
globals: {
|
|
175
|
-
axios: 'axiosAdapter'
|
|
195
|
+
return '[name].[ext]';
|
|
176
196
|
},
|
|
177
197
|
manualChunks: undefined,
|
|
178
198
|
inlineDynamicImports: true,
|
|
@@ -186,24 +206,17 @@ class ForgeCommand {
|
|
|
186
206
|
for (const fileName in bundle) {
|
|
187
207
|
const chunk = bundle[fileName];
|
|
188
208
|
if (chunk.type === 'chunk' && chunk.code) {
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
return `const ${alias}=axiosAdapter.${original};`;
|
|
202
|
-
}
|
|
203
|
-
return `const ${part}=axiosAdapter.${part};`;
|
|
204
|
-
}).join('');
|
|
205
|
-
});
|
|
206
|
-
// Wrap in async function (minified)
|
|
209
|
+
let code = chunk.code;
|
|
210
|
+
// Replace `import X from 'axios'` → `const X = axiosAdapter`
|
|
211
|
+
// ([\w$]+ handles both regular names and $ from minification)
|
|
212
|
+
// \s* instead of \s+ because minifier removes space before the quote
|
|
213
|
+
code = code.replace(/import\s+([\w$]+)\s+from\s*['"]axios['"]\s*;?\n?/g, 'const $1=axiosAdapter;\n');
|
|
214
|
+
// Replace `import { foo, bar as baz } from 'axios'`
|
|
215
|
+
code = code.replace(/import\s*\{([^}]+)\}\s*from\s*['"]axios['"]\s*;?\n?/g, (_, imports) => imports.split(',').map((part) => {
|
|
216
|
+
const [orig, alias] = part.trim().split(/\s+as\s+/).map((s) => s.trim());
|
|
217
|
+
return `const ${alias || orig}=axiosAdapter${orig === 'default' || !alias ? '' : `.${orig}`};\n`;
|
|
218
|
+
}).join(''));
|
|
219
|
+
// Wrap in async function so sandbox (isolate-vm / new Function) gets a callable back
|
|
207
220
|
chunk.code = `return async()=>{try{${code}}catch(err){const errorObj={_error_:true,message:err.message,name:err.name||'Error',stack:err.stack};Object.keys(err).forEach(key=>{errorObj[key]=err[key]});if(err.data)errorObj.data=err.data;if(err.statusCode)errorObj.statusCode=err.statusCode;if(err.response)errorObj.response=err.response;if(err.code)errorObj.code=err.code;return errorObj}}`;
|
|
208
221
|
}
|
|
209
222
|
}
|
|
@@ -322,5 +335,70 @@ class ForgeCommand {
|
|
|
322
335
|
const { data } = await forge.list();
|
|
323
336
|
console.log(data);
|
|
324
337
|
}
|
|
338
|
+
async install(options) {
|
|
339
|
+
const { path, env, bundle } = options;
|
|
340
|
+
if (bundle)
|
|
341
|
+
await this.bundle({ path, upload: false });
|
|
342
|
+
const envs = env ? [env] : ['dev', 'live'];
|
|
343
|
+
for (const e of envs) {
|
|
344
|
+
await ForgeCommand._runScript(path, 'install', e);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async uninstall(options) {
|
|
348
|
+
const { path, env, bundle } = options;
|
|
349
|
+
if (bundle)
|
|
350
|
+
await this.bundle({ path, upload: false });
|
|
351
|
+
const envs = env ? [env] : ['dev', 'live'];
|
|
352
|
+
for (const e of envs) {
|
|
353
|
+
await ForgeCommand._runScript(path, 'uninstall', e);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
static async _runScript(appPath, scriptType, env = 'dev') {
|
|
357
|
+
const module = await import(`${appPath}/ptkl.config.js`);
|
|
358
|
+
const { name, distPath, scripts } = module.default ?? {};
|
|
359
|
+
if (!name)
|
|
360
|
+
throw new Error(`Could not read app name from ${appPath}/ptkl.config.js`);
|
|
361
|
+
if (!scripts?.[scriptType]) {
|
|
362
|
+
console.log(`No '${scriptType}' script defined in ptkl.config.js`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const scriptFile = join(distPath, `${scriptType}.script.js`);
|
|
366
|
+
let scriptCode;
|
|
367
|
+
try {
|
|
368
|
+
scriptCode = readFileSync(scriptFile, 'utf-8');
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
throw new Error(`Script file not found: ${scriptFile}\nRun 'forge bundle' first.`);
|
|
372
|
+
}
|
|
373
|
+
const profile = Util.getCurrentProfile();
|
|
374
|
+
global.window = {
|
|
375
|
+
__ENV_VARIABLES__: {
|
|
376
|
+
API_HOST: profile.host,
|
|
377
|
+
INTEGRATION_API: `${profile.host}/luma/integrations`,
|
|
378
|
+
PROJECT_API_TOKEN: profile.token,
|
|
379
|
+
PROJECT_ENV: env,
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
global.axiosAdapter = axios;
|
|
383
|
+
console.log(`Running ${scriptType} script for '${name}' (profile: ${profile.name}, env: ${env})...\n`);
|
|
384
|
+
// The bundle is: `return async()=>{try{ ...code... }catch(err){return {_error_:true,...}}}`
|
|
385
|
+
// new Function(scriptCode) creates a function whose body is that code.
|
|
386
|
+
// Calling it returns the async function, which we then await.
|
|
387
|
+
const scriptFn = new Function(scriptCode)();
|
|
388
|
+
const result = await scriptFn();
|
|
389
|
+
// Cleanup globals
|
|
390
|
+
delete global.window;
|
|
391
|
+
delete global.axiosAdapter;
|
|
392
|
+
if (result && result._error_) {
|
|
393
|
+
console.error(`❌ Script error: ${result.message}`);
|
|
394
|
+
if (result.stack)
|
|
395
|
+
console.error(result.stack);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
console.log(`✅ ${scriptType} script completed`);
|
|
399
|
+
if (result !== undefined && result !== null) {
|
|
400
|
+
console.log('Result:', JSON.stringify(result, null, 2));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
325
403
|
}
|
|
326
404
|
export default new ForgeCommand();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import util from "../lib/util.js";
|
|
3
|
+
import { Platform as Api } from "@ptkl/sdk/beta";
|
|
4
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { idlToModuleAugmentation } from "../lib/idlToDts.js";
|
|
7
|
+
class GenerateTypesCommand {
|
|
8
|
+
register() {
|
|
9
|
+
return new Command('generate-types')
|
|
10
|
+
.description('Fetch IDL definitions from the platform and emit a TypeScript ' +
|
|
11
|
+
'module-augmentation .d.ts file that gives your project typed ' +
|
|
12
|
+
'component models, component functions, and platform functions.')
|
|
13
|
+
.option('--output <path>', 'Path where the .d.ts file will be written', './types/ptkl.d.ts')
|
|
14
|
+
.option('--env <env>', 'Environment to fetch IDL from (uses current profile default when omitted)')
|
|
15
|
+
.action(this.generate);
|
|
16
|
+
}
|
|
17
|
+
async generate(options) {
|
|
18
|
+
const profile = util.getCurrentProfile();
|
|
19
|
+
const client = new Api({ token: profile.token, host: profile.host, env: options.env });
|
|
20
|
+
console.log(`⏳ Fetching IDL…`);
|
|
21
|
+
const { data } = await client.system().idl();
|
|
22
|
+
console.log(`✅ IDL fetched: ${Object.keys(data.components ?? {}).length} component(s), ${Object.keys(data.functions ?? {}).length} platform function(s)`);
|
|
23
|
+
const dts = idlToModuleAugmentation(data);
|
|
24
|
+
const outputPath = resolve(process.cwd(), options.output);
|
|
25
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
26
|
+
writeFileSync(outputPath, dts, 'utf8');
|
|
27
|
+
const componentCount = Object.keys(data.components ?? {}).length;
|
|
28
|
+
const functionCount = Object.keys(data.functions ?? {}).length;
|
|
29
|
+
console.log(`✅ Types generated: ${outputPath}\n` +
|
|
30
|
+
` ${componentCount} component(s) • ${functionCount} platform function(s)`);
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('Next steps:');
|
|
33
|
+
console.log(' 1. Include the file in your tsconfig.json "include" array, or');
|
|
34
|
+
console.log(` add a triple-slash reference: /// <reference path="${options.output}" />`);
|
|
35
|
+
console.log(' 2. Enjoy typed component models and functions!');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export default new GenerateTypesCommand();
|
package/dist/commands/index.js
CHANGED
|
@@ -8,6 +8,8 @@ import apps from "./apps.js";
|
|
|
8
8
|
import role from "./role.js";
|
|
9
9
|
import forge from "./forge.js";
|
|
10
10
|
import component from "./component.js";
|
|
11
|
+
import generateTypes from "./generate-types.js";
|
|
12
|
+
import validateIDL from "./validate-idl.js";
|
|
11
13
|
export const commands = [
|
|
12
14
|
profile.register(),
|
|
13
15
|
users.register(),
|
|
@@ -16,6 +18,8 @@ export const commands = [
|
|
|
16
18
|
role.register(),
|
|
17
19
|
forge.register(),
|
|
18
20
|
component.register(),
|
|
21
|
+
generateTypes.register(),
|
|
22
|
+
validateIDL.register(),
|
|
19
23
|
new Command('init')
|
|
20
24
|
.description("Init protokol toolkit")
|
|
21
25
|
.action(Util.init)
|
package/dist/commands/profile.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import util from "../lib/util.js";
|
|
3
3
|
import cli from "../lib/cli.js";
|
|
4
|
-
import { APIUser, User } from "@ptkl/sdk";
|
|
4
|
+
import { APIUser, Users as User } from "@ptkl/sdk";
|
|
5
5
|
import password from '@inquirer/password';
|
|
6
6
|
class ProfileCommand {
|
|
7
7
|
register() {
|
package/dist/commands/role.js
CHANGED
package/dist/commands/users.js
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import util from "../lib/util.js";
|
|
5
|
+
import { Platform as Api } from "@ptkl/sdk/beta";
|
|
6
|
+
class ValidateIDLCommand {
|
|
7
|
+
register() {
|
|
8
|
+
return new Command('validate-idl')
|
|
9
|
+
.description('Validate a value against a component, extension, or platform-function IDL. ' +
|
|
10
|
+
'Pass either --ref to identify the target by ref, or --idl-file to supply an ' +
|
|
11
|
+
'inline IDL. The value to validate can be given as a JSON string (--value) or ' +
|
|
12
|
+
'read from a file (--value-file).')
|
|
13
|
+
.option('--ref <ref>', 'Compound ref identifying the IDL target.\n' +
|
|
14
|
+
' component:ns::name — base component, default schema\n' +
|
|
15
|
+
' component:ns::name.schema — base component, named schema\n' +
|
|
16
|
+
' extension:ns::name/extName — extension IDL on a component\n' +
|
|
17
|
+
' pfn:functionName — platform function input')
|
|
18
|
+
.option('--idl-file <path>', 'Path to a JSON file containing an inline EntityIDL object. Mutually exclusive with --ref.')
|
|
19
|
+
.option('--field <field>', 'Field key to validate against (required for component/extension refs; ignored for pfn).')
|
|
20
|
+
.option('--value <json>', 'JSON-encoded value to validate. Mutually exclusive with --value-file.')
|
|
21
|
+
.option('--value-file <path>', 'Path to a JSON file whose contents are the value to validate. Mutually exclusive with --value.')
|
|
22
|
+
.option('--env <env>', 'Environment to run validation in (uses current profile default when omitted).')
|
|
23
|
+
.action(this.run);
|
|
24
|
+
}
|
|
25
|
+
async run(options) {
|
|
26
|
+
// ── mutual-exclusion guards ────────────────────────────────────────────
|
|
27
|
+
if (options.ref && options.idlFile) {
|
|
28
|
+
console.error('❌ Provide either --ref or --idl-file, not both.');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
if (!options.ref && !options.idlFile) {
|
|
32
|
+
console.error('❌ Provide either --ref or --idl-file.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (options.value !== undefined && options.valueFile) {
|
|
36
|
+
console.error('❌ Provide either --value or --value-file, not both.');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (options.value === undefined && !options.valueFile) {
|
|
40
|
+
console.error('❌ Provide --value or --value-file.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
// ── resolve value ──────────────────────────────────────────────────────
|
|
44
|
+
let rawValue;
|
|
45
|
+
if (options.valueFile) {
|
|
46
|
+
const path = resolve(process.cwd(), options.valueFile);
|
|
47
|
+
if (!existsSync(path)) {
|
|
48
|
+
console.error(`❌ Value file not found: ${path}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
rawValue = readFileSync(path, 'utf8');
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
rawValue = options.value;
|
|
55
|
+
}
|
|
56
|
+
let value;
|
|
57
|
+
try {
|
|
58
|
+
value = JSON.parse(rawValue);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
console.error('❌ Could not parse value as JSON.');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
// ── resolve inline IDL ─────────────────────────────────────────────────
|
|
65
|
+
let inlineIDL;
|
|
66
|
+
if (options.idlFile) {
|
|
67
|
+
const path = resolve(process.cwd(), options.idlFile);
|
|
68
|
+
if (!existsSync(path)) {
|
|
69
|
+
console.error(`❌ IDL file not found: ${path}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
inlineIDL = JSON.parse(readFileSync(path, 'utf8'));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
console.error('❌ Could not parse IDL file as JSON.');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const profile = util.getCurrentProfile();
|
|
81
|
+
const client = new Api({ token: profile.token, host: profile.host, env: options.env });
|
|
82
|
+
const target = options.ref ? `ref: ${options.ref}` : `inline IDL (${options.idlFile})`;
|
|
83
|
+
console.log(`⏳ Validating value against ${target}…`);
|
|
84
|
+
let result;
|
|
85
|
+
try {
|
|
86
|
+
const req = { value };
|
|
87
|
+
if (options.ref)
|
|
88
|
+
req.ref = options.ref;
|
|
89
|
+
if (inlineIDL)
|
|
90
|
+
req.idl = inlineIDL;
|
|
91
|
+
if (options.field)
|
|
92
|
+
req.field = options.field;
|
|
93
|
+
const { data } = await client.system().idlValidate(req);
|
|
94
|
+
result = data;
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
const msg = err?.response?.data?.message ?? err?.message ?? String(err);
|
|
98
|
+
console.error(`❌ Request failed: ${msg}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
if (result.valid) {
|
|
102
|
+
console.log('✅ Valid');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.error('✗ Invalid');
|
|
106
|
+
if (result.errors?.length) {
|
|
107
|
+
for (const e of result.errors) {
|
|
108
|
+
const loc = e.field ? ` (${e.field})` : '';
|
|
109
|
+
console.error(` •${loc} ${e.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export default new ValidateIDLCommand();
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDL → TypeScript module-augmentation generator.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of the runtime idlToDts.js used by the Monaco editor, but output
|
|
5
|
+
* targets a `declare module "@ptkl/sdk"` block so the generated `.d.ts` file
|
|
6
|
+
* extends the SDK's open interfaces at build time.
|
|
7
|
+
*/
|
|
8
|
+
// ── Primitive type conversion ─────────────────────────────────────────────────
|
|
9
|
+
function idlTypeNodeToTs(node, indent = 0) {
|
|
10
|
+
if (!node)
|
|
11
|
+
return 'any';
|
|
12
|
+
switch (node.type) {
|
|
13
|
+
case 'string': return 'string';
|
|
14
|
+
case 'number': return 'number';
|
|
15
|
+
case 'boolean': return 'boolean';
|
|
16
|
+
case 'any': return 'any';
|
|
17
|
+
case 'array':
|
|
18
|
+
return node.items ? `Array<${idlTypeNodeToTs(node.items, indent)}>` : 'any[]';
|
|
19
|
+
case 'object':
|
|
20
|
+
if (node.fields && Object.keys(node.fields).length > 0) {
|
|
21
|
+
return objectShape(node.fields, indent);
|
|
22
|
+
}
|
|
23
|
+
return 'Record<string, any>';
|
|
24
|
+
case 'relation':
|
|
25
|
+
// When used as a standalone type (not as a field entry),
|
|
26
|
+
// a relation is a UUID string.
|
|
27
|
+
return 'string';
|
|
28
|
+
default:
|
|
29
|
+
// NamedRef or unknown — treat as any
|
|
30
|
+
return 'any';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const I = ' '; // indent unit
|
|
34
|
+
function objectShape(fields, indent = 0) {
|
|
35
|
+
const entries = Object.entries(fields);
|
|
36
|
+
// Keep short objects (≤3 simple fields) inline
|
|
37
|
+
if (entries.length <= 3 && entries.every(([, n]) => !n.fields && n.type !== 'object')) {
|
|
38
|
+
const props = entries.map(([key, node]) => {
|
|
39
|
+
const opt = node.required === false || node.required === undefined;
|
|
40
|
+
return `${key}${opt ? '?' : ''}: ${idlTypeNodeToTs(node, indent)}`;
|
|
41
|
+
});
|
|
42
|
+
return `{ ${props.join('; ')} }`;
|
|
43
|
+
}
|
|
44
|
+
const pad = I.repeat(indent + 1);
|
|
45
|
+
const closePad = I.repeat(indent);
|
|
46
|
+
const lines = entries.map(([key, node]) => {
|
|
47
|
+
const opt = node.required === false || node.required === undefined;
|
|
48
|
+
return `${pad}${key}${opt ? '?' : ''}: ${idlTypeNodeToTs(node, indent + 1)};`;
|
|
49
|
+
});
|
|
50
|
+
return `{\n${lines.join('\n')}\n${closePad}}`;
|
|
51
|
+
}
|
|
52
|
+
// ── Relation expansion ────────────────────────────────────────────────────────
|
|
53
|
+
/**
|
|
54
|
+
* Expands a field map into TypeScript property lines. Relation fields produce
|
|
55
|
+
* two entries: the UUID field (`field: string`) and the hydrated reference
|
|
56
|
+
* (`field_RefObj: T` or `field_RefObj: Array<T>`).
|
|
57
|
+
*/
|
|
58
|
+
function expandFieldLines(fields, pad, indent) {
|
|
59
|
+
const lines = [];
|
|
60
|
+
for (const [k, node] of Object.entries(fields)) {
|
|
61
|
+
const opt = node.required === false || node.required === undefined;
|
|
62
|
+
if (node.type === 'relation') {
|
|
63
|
+
// UUID string field
|
|
64
|
+
lines.push(`${pad}${k}${opt ? '?' : ''}: string;`);
|
|
65
|
+
// Hydrated _RefObj field
|
|
66
|
+
const itemTs = node.items ? idlTypeNodeToTs(node.items, indent) : 'any';
|
|
67
|
+
const refType = node.cardinality === 'many' ? `Array<${itemTs}>` : itemTs;
|
|
68
|
+
lines.push(`${pad}${k}_RefObj?: ${refType};`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
lines.push(`${pad}${k}${opt ? '?' : ''}: ${idlTypeNodeToTs(node, indent)};`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return lines;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Like objectShape but expands relation fields into two properties.
|
|
78
|
+
*/
|
|
79
|
+
function objectShapeWithRelations(fields, indent = 0) {
|
|
80
|
+
const entries = Object.entries(fields);
|
|
81
|
+
const hasRelation = entries.some(([, n]) => n.type === 'relation');
|
|
82
|
+
if (!hasRelation)
|
|
83
|
+
return objectShape(fields, indent); // fast path
|
|
84
|
+
const pad = I.repeat(indent + 1);
|
|
85
|
+
const closePad = I.repeat(indent);
|
|
86
|
+
const lines = expandFieldLines(fields, pad, indent + 1);
|
|
87
|
+
return `{\n${lines.join('\n')}\n${closePad}}`;
|
|
88
|
+
}
|
|
89
|
+
// ── Top-level generators ──────────────────────────────────────────────────────
|
|
90
|
+
function buildComponentModels(components, baseIndent) {
|
|
91
|
+
const entries = [];
|
|
92
|
+
const d = baseIndent;
|
|
93
|
+
const pad = I.repeat(d);
|
|
94
|
+
for (const [ref, comp] of Object.entries(components)) {
|
|
95
|
+
if (!comp)
|
|
96
|
+
continue;
|
|
97
|
+
// Collect all schema names from base + extensions.
|
|
98
|
+
const schemaNames = new Set();
|
|
99
|
+
for (const s of Object.keys(comp.schemas ?? {}))
|
|
100
|
+
schemaNames.add(s);
|
|
101
|
+
for (const ext of Object.values(comp.extensions ?? {})) {
|
|
102
|
+
if (!ext)
|
|
103
|
+
continue;
|
|
104
|
+
for (const s of Object.keys(ext.schemas ?? {}))
|
|
105
|
+
schemaNames.add(s);
|
|
106
|
+
}
|
|
107
|
+
for (const schema of schemaNames) {
|
|
108
|
+
const key = schema === 'default' ? ref : `${ref}.${schema}`;
|
|
109
|
+
const baseFields = comp.schemas?.[schema]?.fields;
|
|
110
|
+
// Collect extension entries for this schema.
|
|
111
|
+
const extEntries = [];
|
|
112
|
+
for (const [extName, ext] of Object.entries(comp.extensions ?? {})) {
|
|
113
|
+
if (!ext)
|
|
114
|
+
continue;
|
|
115
|
+
const extSchemaFields = ext.schemas?.[schema]?.fields;
|
|
116
|
+
if (extSchemaFields && Object.keys(extSchemaFields).length > 0) {
|
|
117
|
+
extEntries.push([extName, extSchemaFields]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const hasBase = baseFields && Object.keys(baseFields).length > 0;
|
|
121
|
+
const hasExt = extEntries.length > 0;
|
|
122
|
+
if (!hasBase && !hasExt) {
|
|
123
|
+
entries.push(`${pad}${JSON.stringify(key)}: Record<string, any>;`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const propLines = [];
|
|
127
|
+
if (hasBase) {
|
|
128
|
+
propLines.push(...expandFieldLines(baseFields, `${pad}${I}`, d + 1));
|
|
129
|
+
}
|
|
130
|
+
if (hasExt) {
|
|
131
|
+
const extInner = [];
|
|
132
|
+
for (const [extName, extFields] of extEntries) {
|
|
133
|
+
extInner.push(`${pad}${I}${I}${extName}: ${objectShapeWithRelations(extFields, d + 2)};`);
|
|
134
|
+
}
|
|
135
|
+
propLines.push(`${pad}${I}extensions: {\n${extInner.join('\n')}\n${pad}${I}};`);
|
|
136
|
+
}
|
|
137
|
+
entries.push(`${pad}${JSON.stringify(key)}: {\n${propLines.join('\n')}\n${pad}};`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (entries.length === 0)
|
|
141
|
+
return '';
|
|
142
|
+
return entries.join('\n');
|
|
143
|
+
}
|
|
144
|
+
function buildComponentFunctions(components, baseIndent) {
|
|
145
|
+
const entries = [];
|
|
146
|
+
const d = baseIndent;
|
|
147
|
+
const pad = I.repeat(d);
|
|
148
|
+
for (const [ref, comp] of Object.entries(components)) {
|
|
149
|
+
if (!comp)
|
|
150
|
+
continue;
|
|
151
|
+
const allFunctions = [
|
|
152
|
+
...(comp.functions ?? []),
|
|
153
|
+
...Object.values(comp.extensions ?? {}).flatMap(ext => ext?.functions ?? []),
|
|
154
|
+
];
|
|
155
|
+
if (allFunctions.length === 0)
|
|
156
|
+
continue;
|
|
157
|
+
const fnLines = [];
|
|
158
|
+
for (const fn of allFunctions) {
|
|
159
|
+
const parts = [];
|
|
160
|
+
if (fn.input && Object.keys(fn.input).length > 0)
|
|
161
|
+
parts.push(`input: ${objectShape(fn.input, d + 2)}`);
|
|
162
|
+
if (fn.output)
|
|
163
|
+
parts.push(`output: ${idlTypeNodeToTs(fn.output, d + 2)}`);
|
|
164
|
+
if (parts.length === 0)
|
|
165
|
+
continue;
|
|
166
|
+
fnLines.push(`${pad}${I}${JSON.stringify(fn.name)}: { ${parts.join('; ')} };`);
|
|
167
|
+
}
|
|
168
|
+
if (fnLines.length > 0)
|
|
169
|
+
entries.push(`${pad}${JSON.stringify(ref)}: {\n${fnLines.join('\n')}\n${pad}};`);
|
|
170
|
+
}
|
|
171
|
+
if (entries.length === 0)
|
|
172
|
+
return '';
|
|
173
|
+
return entries.join('\n');
|
|
174
|
+
}
|
|
175
|
+
function buildPlatformFunctions(functions, baseIndent) {
|
|
176
|
+
const entries = [];
|
|
177
|
+
const d = baseIndent;
|
|
178
|
+
const pad = I.repeat(d);
|
|
179
|
+
for (const [name, sig] of Object.entries(functions)) {
|
|
180
|
+
if (!sig)
|
|
181
|
+
continue;
|
|
182
|
+
const parts = [];
|
|
183
|
+
if (sig.input && Object.keys(sig.input).length > 0)
|
|
184
|
+
parts.push(`input: ${objectShape(sig.input, d + 1)}`);
|
|
185
|
+
if (sig.output)
|
|
186
|
+
parts.push(`output: ${idlTypeNodeToTs(sig.output, d + 1)}`);
|
|
187
|
+
if (parts.length === 0)
|
|
188
|
+
continue;
|
|
189
|
+
entries.push(`${pad}${JSON.stringify(name)}: { ${parts.join('; ')} };`);
|
|
190
|
+
}
|
|
191
|
+
if (entries.length === 0)
|
|
192
|
+
return '';
|
|
193
|
+
return entries.join('\n');
|
|
194
|
+
}
|
|
195
|
+
// ── Main export ───────────────────────────────────────────────────────────────
|
|
196
|
+
/**
|
|
197
|
+
* Converts an IDL response into a `declare module "@ptkl/sdk"` augmentation
|
|
198
|
+
* block. Write the output to a `.d.ts` file and include it in your project
|
|
199
|
+
* (e.g., via `tsconfig.json` `"include"` or a triple-slash reference).
|
|
200
|
+
*/
|
|
201
|
+
export function idlToModuleAugmentation(idl) {
|
|
202
|
+
const hasComponents = idl.components && Object.keys(idl.components).length > 0;
|
|
203
|
+
const hasFunctions = idl.functions && Object.keys(idl.functions).length > 0;
|
|
204
|
+
if (!hasComponents && !hasFunctions) {
|
|
205
|
+
return `// No IDL definitions found — nothing to generate.\n`;
|
|
206
|
+
}
|
|
207
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
208
|
+
const lines = [
|
|
209
|
+
`// Generated by ptkl generate-types on ${date}`,
|
|
210
|
+
`// Do not edit manually — re-run \`ptkl generate-types\` to refresh.`,
|
|
211
|
+
``,
|
|
212
|
+
`import "@ptkl/sdk";`,
|
|
213
|
+
`import "@ptkl/sdk/beta";`,
|
|
214
|
+
];
|
|
215
|
+
// Declare interfaces once globally, then extend into both modules.
|
|
216
|
+
const ifaceNames = [];
|
|
217
|
+
if (hasComponents) {
|
|
218
|
+
const models = buildComponentModels(idl.components, 1);
|
|
219
|
+
if (models) {
|
|
220
|
+
lines.push(``, `interface _CM {\n${models}\n}`);
|
|
221
|
+
ifaceNames.push('ComponentModels extends _CM');
|
|
222
|
+
}
|
|
223
|
+
const fns = buildComponentFunctions(idl.components, 1);
|
|
224
|
+
if (fns) {
|
|
225
|
+
lines.push(``, `interface _CF {\n${fns}\n}`);
|
|
226
|
+
ifaceNames.push('ComponentFunctions extends _CF');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (hasFunctions) {
|
|
230
|
+
const platFns = buildPlatformFunctions(idl.functions, 1);
|
|
231
|
+
if (platFns) {
|
|
232
|
+
lines.push(``, `interface _PF {\n${platFns}\n}`);
|
|
233
|
+
ifaceNames.push('PlatformFunctions extends _PF');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (ifaceNames.length > 0) {
|
|
237
|
+
const augBody = ifaceNames.map(n => ` interface ${n} {}`).join('\n');
|
|
238
|
+
lines.push(``, `declare module "@ptkl/sdk" {\n${augBody}\n}`, ``, `declare module "@ptkl/sdk/beta" {\n${augBody}\n}`);
|
|
239
|
+
}
|
|
240
|
+
lines.push(``);
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
package/dist/lib/util.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import
|
|
2
|
+
import { Platform } from '@ptkl/sdk';
|
|
3
3
|
export default class Util {
|
|
4
4
|
static profilePath = `${process.env.HOME}/.ptkl`;
|
|
5
5
|
static fileName = "profiles.json";
|
|
@@ -69,6 +69,6 @@ export default class Util {
|
|
|
69
69
|
}
|
|
70
70
|
static getClientForProfile() {
|
|
71
71
|
const profile = Util.getCurrentProfile();
|
|
72
|
-
return new
|
|
72
|
+
return new Platform({ project: profile.project, token: profile.token, host: profile.host });
|
|
73
73
|
}
|
|
74
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ptkl/toolkit",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.12",
|
|
4
4
|
"description": "A command-line toolkit for managing Protokol platform applications, profiles, functions, and components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"protokol",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@babel/preset-typescript": "^7.28.5",
|
|
36
36
|
"@babel/standalone": "^7.26.10",
|
|
37
37
|
"@inquirer/password": "^4.0.21",
|
|
38
|
-
"@ptkl/sdk": "^
|
|
38
|
+
"@ptkl/sdk": "^1.5.1",
|
|
39
39
|
"@rollup/plugin-babel": "^6.1.0",
|
|
40
40
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
41
41
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import * as Babel from '@babel/standalone';
|
|
2
|
-
import less from 'less';
|
|
3
|
-
export default class Compiler {
|
|
4
|
-
async compileBabel(expression) {
|
|
5
|
-
let code = Babel.transform(expression, {
|
|
6
|
-
sourceType: "module",
|
|
7
|
-
presets: ["env"]
|
|
8
|
-
});
|
|
9
|
-
return code.code;
|
|
10
|
-
}
|
|
11
|
-
async compileCSS(scope, lang, expression) {
|
|
12
|
-
switch (lang) {
|
|
13
|
-
case 'less':
|
|
14
|
-
if (scope) {
|
|
15
|
-
return await less.render(`.${scope} { ${expression} }`);
|
|
16
|
-
}
|
|
17
|
-
const { css } = await less.render(`${expression}`);
|
|
18
|
-
return css;
|
|
19
|
-
default:
|
|
20
|
-
return expression;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
async compile(ext, content) {
|
|
24
|
-
switch (ext) {
|
|
25
|
-
case 'js':
|
|
26
|
-
return await this.compileBabel(content);
|
|
27
|
-
case 'css':
|
|
28
|
-
return await this.compileCSS(null, "css", content);
|
|
29
|
-
case 'less':
|
|
30
|
-
return await this.compileCSS(null, "less", content);
|
|
31
|
-
default:
|
|
32
|
-
return await Promise.resolve(content);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
getSupportedExt() {
|
|
36
|
-
return ["js", "css", "less", "json", "svg", "html"];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility to generate import maps for component frameworks
|
|
3
|
-
* This should be used on the frontend to dynamically create import maps
|
|
4
|
-
* based on sdk_version and sdk_engine
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Generate an import map configuration for a given framework
|
|
8
|
-
*
|
|
9
|
-
* @param sdkEngine - The engine string (e.g., "react@18", "lit@3", "vue3")
|
|
10
|
-
* @returns Import map configuration object
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const importMap = generateImportMap("react@18");
|
|
15
|
-
* // Inject into HTML:
|
|
16
|
-
* const script = document.createElement('script');
|
|
17
|
-
* script.type = 'importmap';
|
|
18
|
-
* script.textContent = JSON.stringify(importMap);
|
|
19
|
-
* document.head.appendChild(script);
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export function generateImportMap(sdkEngine) {
|
|
23
|
-
// Parse engine to extract framework and version (e.g., "lit@3" -> { framework: "lit", version: "3" })
|
|
24
|
-
const engineMatch = sdkEngine.match(/^([^@]+)(?:@(.+))?$/);
|
|
25
|
-
const framework = engineMatch ? engineMatch[1] : sdkEngine;
|
|
26
|
-
const frameworkVersion = engineMatch ? engineMatch[2] : null;
|
|
27
|
-
const imports = {};
|
|
28
|
-
if (framework === 'react') {
|
|
29
|
-
const version = frameworkVersion || '18';
|
|
30
|
-
imports['react'] = `https://esm.sh/react@${version}`;
|
|
31
|
-
imports['react-dom'] = `https://esm.sh/react-dom@${version}`;
|
|
32
|
-
imports['react/jsx-runtime'] = `https://esm.sh/react@${version}/jsx-runtime`;
|
|
33
|
-
imports['react/jsx-dev-runtime'] = `https://esm.sh/react@${version}/jsx-dev-runtime`;
|
|
34
|
-
}
|
|
35
|
-
else if (framework === 'vue3' || framework === 'vue') {
|
|
36
|
-
const version = frameworkVersion || '3';
|
|
37
|
-
imports['vue'] = `https://esm.sh/vue@${version}`;
|
|
38
|
-
}
|
|
39
|
-
else if (framework === 'lit') {
|
|
40
|
-
const version = frameworkVersion || '3';
|
|
41
|
-
imports['lit'] = `https://esm.sh/lit@${version}`;
|
|
42
|
-
imports['lit/decorators.js'] = `https://esm.sh/lit@${version}/decorators.js`;
|
|
43
|
-
imports['lit/directive.js'] = `https://esm.sh/lit@${version}/directive.js`;
|
|
44
|
-
imports['lit/directives/class-map.js'] = `https://esm.sh/lit@${version}/directives/class-map.js`;
|
|
45
|
-
imports['lit/directives/style-map.js'] = `https://esm.sh/lit@${version}/directives/style-map.js`;
|
|
46
|
-
imports['lit/directives/if-defined.js'] = `https://esm.sh/lit@${version}/directives/if-defined.js`;
|
|
47
|
-
imports['lit/directives/repeat.js'] = `https://esm.sh/lit@${version}/directives/repeat.js`;
|
|
48
|
-
imports['lit/directives/unsafe-html.js'] = `https://esm.sh/lit@${version}/directives/unsafe-html.js`;
|
|
49
|
-
}
|
|
50
|
-
else if (framework === 'webcomponents') {
|
|
51
|
-
// For vanilla web components, no external framework needed
|
|
52
|
-
return { imports: {} };
|
|
53
|
-
}
|
|
54
|
-
return { imports };
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Generate the import map HTML script tag as a string
|
|
58
|
-
*
|
|
59
|
-
* @param sdkEngine - The engine string (e.g., "react@18", "lit@3", "vue3")
|
|
60
|
-
* @returns HTML string for the import map script tag
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```typescript
|
|
64
|
-
* const scriptTag = generateImportMapHTML("react@18");
|
|
65
|
-
* document.head.insertAdjacentHTML('beforeend', scriptTag);
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
export function generateImportMapHTML(sdkEngine) {
|
|
69
|
-
const importMap = generateImportMap(sdkEngine);
|
|
70
|
-
if (Object.keys(importMap.imports).length === 0) {
|
|
71
|
-
return ''; // No import map needed
|
|
72
|
-
}
|
|
73
|
-
return `<script type="importmap">\n${JSON.stringify(importMap, null, 2)}\n</script>`;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Inject import map into the document dynamically
|
|
77
|
-
*
|
|
78
|
-
* @param sdkEngine - The engine string (e.g., "react@18", "lit@3", "vue3")
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```typescript
|
|
82
|
-
* // In your frontend code:
|
|
83
|
-
* injectImportMap("react@18");
|
|
84
|
-
* // Now you can load component modules
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
export function injectImportMap(sdkEngine) {
|
|
88
|
-
const importMap = generateImportMap(sdkEngine);
|
|
89
|
-
if (Object.keys(importMap.imports).length === 0) {
|
|
90
|
-
return; // No import map needed
|
|
91
|
-
}
|
|
92
|
-
const script = document.createElement('script');
|
|
93
|
-
script.type = 'importmap';
|
|
94
|
-
script.textContent = JSON.stringify(importMap, null, 2);
|
|
95
|
-
document.head.appendChild(script);
|
|
96
|
-
}
|