@swipt/cli 0.1.0-nightly.31 → 0.1.0-nightly.33
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/client.mjs +312 -0
- package/device.mjs +620 -0
- package/ignore.mjs +137 -0
- package/output.mjs +61 -0
- package/package.json +12 -5
- package/{cli.mjs → project.mjs} +109 -111
- package/proto/common_pb.js +98 -0
- package/proto/logs_pb.js +34 -0
- package/proto/repl_pb.js +55 -0
- package/proto/swiplets_pb.js +179 -0
- package/proto/system_pb.js +27 -0
- package/sourcemap.mjs +77 -0
- package/swipt.mjs +301 -0
package/ignore.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const excluded = name => name.startsWith(".") || name === "node_modules" || name === "dist";
|
|
5
|
+
|
|
6
|
+
const escape = text => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7
|
+
|
|
8
|
+
function parseRule(line) {
|
|
9
|
+
line = line.replace(/[ \t\r]+$/, "");
|
|
10
|
+
if (line === "" || line.startsWith("#")) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const rule = { negated: false, directory: false };
|
|
14
|
+
if (line.startsWith("!")) {
|
|
15
|
+
rule.negated = true;
|
|
16
|
+
line = line.slice(1);
|
|
17
|
+
} else if (line.startsWith("\\")) {
|
|
18
|
+
line = line.slice(1);
|
|
19
|
+
}
|
|
20
|
+
if (line.endsWith("/")) {
|
|
21
|
+
rule.directory = true;
|
|
22
|
+
line = line.replace(/\/+$/, "");
|
|
23
|
+
}
|
|
24
|
+
if (line === "") {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const anchored = line.includes("/");
|
|
28
|
+
line = line.replace(/^\//, "");
|
|
29
|
+
let expression = anchored ? "^" : "^(?:.*/)?";
|
|
30
|
+
for (let index = 0; index < line.length; index++) {
|
|
31
|
+
const rest = line.slice(index);
|
|
32
|
+
if (rest.startsWith("**/")) {
|
|
33
|
+
expression += "(?:.*/)?";
|
|
34
|
+
index += 2;
|
|
35
|
+
} else if (rest === "/**") {
|
|
36
|
+
expression += "/.*";
|
|
37
|
+
index += 2;
|
|
38
|
+
} else if (rest.startsWith("**")) {
|
|
39
|
+
expression += ".*";
|
|
40
|
+
index++;
|
|
41
|
+
} else if (rest[0] === "*") {
|
|
42
|
+
expression += "[^/]*";
|
|
43
|
+
} else if (rest[0] === "?") {
|
|
44
|
+
expression += "[^/]";
|
|
45
|
+
} else if (rest[0] === "[" && rest.indexOf("]", 1) > 1) {
|
|
46
|
+
const end = rest.indexOf("]", 1);
|
|
47
|
+
let cls = rest.slice(1, end);
|
|
48
|
+
if (cls.startsWith("!")) {
|
|
49
|
+
cls = `^${cls.slice(1)}`;
|
|
50
|
+
}
|
|
51
|
+
expression += `[${cls.replaceAll("\\", "\\\\")}]`;
|
|
52
|
+
index += end;
|
|
53
|
+
} else if (rest[0] === "\\" && rest.length > 1) {
|
|
54
|
+
expression += escape(rest[1]);
|
|
55
|
+
index++;
|
|
56
|
+
} else {
|
|
57
|
+
expression += escape(rest[0]);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
rule.pattern = new RegExp(`${expression}$`);
|
|
62
|
+
} catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return rule;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const relativePath = (root, name) => {
|
|
69
|
+
const relative = path.relative(root, name);
|
|
70
|
+
return relative === "" || relative.startsWith("..") || path.isAbsolute(relative) ? "" : relative.split(path.sep).join("/");
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export class Ignorer {
|
|
74
|
+
rules = [];
|
|
75
|
+
|
|
76
|
+
constructor(root) {
|
|
77
|
+
const walk = directory => {
|
|
78
|
+
const relative = relativePath(root, directory);
|
|
79
|
+
if (relative !== "" && this.ignored(relative, true)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.read(path.join(directory, ".gitignore"), relative);
|
|
83
|
+
let entries;
|
|
84
|
+
try {
|
|
85
|
+
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
86
|
+
} catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
walk(path.join(directory, entry.name));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
walk(root);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
read(name, base) {
|
|
99
|
+
let text;
|
|
100
|
+
try {
|
|
101
|
+
text = fs.readFileSync(name, "utf8");
|
|
102
|
+
} catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (const line of text.split("\n")) {
|
|
106
|
+
const rule = parseRule(line);
|
|
107
|
+
if (rule) {
|
|
108
|
+
this.rules.push({ ...rule, base });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ignored(relative, directory) {
|
|
114
|
+
const parts = relative.split("/");
|
|
115
|
+
return parts.some((part, index) => excluded(part) || this.matches(parts.slice(0, index + 1).join("/"), directory || index < parts.length - 1));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
matches(relative, directory) {
|
|
119
|
+
let ignored = false;
|
|
120
|
+
for (const rule of this.rules) {
|
|
121
|
+
if (rule.directory && !directory) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
let local = relative;
|
|
125
|
+
if (rule.base !== "") {
|
|
126
|
+
if (!relative.startsWith(`${rule.base}/`)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
local = relative.slice(rule.base.length + 1);
|
|
130
|
+
}
|
|
131
|
+
if (rule.pattern.test(local)) {
|
|
132
|
+
ignored = !rule.negated;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return ignored;
|
|
136
|
+
}
|
|
137
|
+
}
|
package/output.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
2
|
+
|
|
3
|
+
import { LogLevel, LogLevelSchema } from "./proto/common_pb.js";
|
|
4
|
+
import { ServiceStatus, ServiceStatusSchema } from "./proto/swiplets_pb.js";
|
|
5
|
+
|
|
6
|
+
export class FalseResult extends Error {}
|
|
7
|
+
|
|
8
|
+
export const enumText = (schema, value, prefix) => schema.value[value].name.slice(prefix.length).toLowerCase().replaceAll("_", " ");
|
|
9
|
+
|
|
10
|
+
const levelName = level => (level === LogLevel.UNSPECIFIED ? "info" : enumText(LogLevelSchema, level, "LOG_LEVEL_"));
|
|
11
|
+
|
|
12
|
+
const pad = (value, width) => String(value).padStart(width, "0");
|
|
13
|
+
|
|
14
|
+
export function clockTime(timestamp) {
|
|
15
|
+
const at = timestampDate(timestamp);
|
|
16
|
+
return `${pad(at.getHours(), 2)}:${pad(at.getMinutes(), 2)}:${pad(at.getSeconds(), 2)}.${pad(at.getMilliseconds(), 3)}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function dateTime(timestamp) {
|
|
20
|
+
const at = timestampDate(timestamp);
|
|
21
|
+
return `${at.getFullYear()}-${pad(at.getMonth() + 1, 2)}-${pad(at.getDate(), 2)} ${pad(at.getHours(), 2)}:${pad(at.getMinutes(), 2)}:${pad(at.getSeconds(), 2)}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function columns(rows) {
|
|
25
|
+
const widths = [];
|
|
26
|
+
for (const row of rows) {
|
|
27
|
+
row.forEach((cell, index) => {
|
|
28
|
+
widths[index] = Math.max(widths[index] ?? 0, cell.length);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return rows.map(row => row.map((cell, index) => (index === row.length - 1 ? cell : cell.padEnd(widths[index] + 2))).join("").trimEnd()).join("\n");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const frameText = frame => {
|
|
35
|
+
const position = `${frame.file}:${frame.line}:${frame.column}`;
|
|
36
|
+
return frame.function === "" ? position : `${frame.function} (${position})`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const failureText = (failure, frames) => [`${failure.name}: ${failure.message}`, ...failure.stack.map(frame => ` at ${frameText(frames(frame))}`)].join("\n");
|
|
40
|
+
|
|
41
|
+
export function printScriptError(failure, frames) {
|
|
42
|
+
console.error(`error: ${failureText(failure, frames)}`);
|
|
43
|
+
return new FalseResult();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function printLog(entry, toStdout) {
|
|
47
|
+
const message = entry.tag === "" ? entry.message : `${entry.tag}: ${entry.message}`;
|
|
48
|
+
(toStdout ? console.log : console.error)(clockTime(entry.time), levelName(entry.level).toUpperCase(), message);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function serviceText(id, state, frames) {
|
|
52
|
+
let text = state?.enabled ? "enabled" : "disabled";
|
|
53
|
+
text += `, ${state && state.status !== ServiceStatus.UNSPECIFIED ? enumText(ServiceStatusSchema, state.status, "SERVICE_STATUS_") : "stopped"}`;
|
|
54
|
+
if (state?.since) {
|
|
55
|
+
text += ` since ${clockTime(state.since)}`;
|
|
56
|
+
}
|
|
57
|
+
if (state?.error) {
|
|
58
|
+
text += `: ${failureText(state.error, frames(id))}`;
|
|
59
|
+
}
|
|
60
|
+
return text;
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swipt/cli",
|
|
3
|
-
"version": "0.1.0-nightly.
|
|
3
|
+
"version": "0.1.0-nightly.33",
|
|
4
4
|
"license": "GPL-3.0-or-later",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/swipt-automation/swipt.git",
|
|
8
|
-
"directory": "
|
|
8
|
+
"directory": "cli"
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
11
11
|
"bin": {
|
|
12
|
-
"
|
|
12
|
+
"swipt": "swipt.mjs"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"
|
|
15
|
+
"*.mjs",
|
|
16
|
+
"proto"
|
|
16
17
|
],
|
|
17
18
|
"publishConfig": {
|
|
18
19
|
"access": "public"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
22
|
+
"@bufbuild/protobuf": "^2.16.0",
|
|
23
|
+
"@jridgewell/trace-mapping": "^0.3.31",
|
|
24
|
+
"bonjour-service": "^1.4.4",
|
|
21
25
|
"esbuild": "^0.28.2"
|
|
22
26
|
},
|
|
23
27
|
"peerDependencies": {
|
|
24
|
-
"@swipt/api": "0.1.0-nightly.
|
|
28
|
+
"@swipt/api": "0.1.0-nightly.33"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@bufbuild/protoc-gen-es": "^2.16.0"
|
|
25
32
|
}
|
|
26
33
|
}
|
package/{cli.mjs → project.mjs}
RENAMED
|
@@ -40,7 +40,7 @@ const idPattern = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
|
40
40
|
|
|
41
41
|
const storedSuffixes = [".webp", ".jxl", ".avif", ".heic", ".heif", ".jpg", ".jpeg", ".png", ".gif", ".mp4", ".mov", ".m4a", ".mp3", ".aac", ".zip", ".gz", ".br", ".zst", ".xz"].join(":");
|
|
42
42
|
|
|
43
|
-
function loadProject(root) {
|
|
43
|
+
export function loadProject(root) {
|
|
44
44
|
const manifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
45
45
|
const config = manifest.swiplet ?? {};
|
|
46
46
|
if (typeof config.id !== "string" || !idPattern.test(config.id)) {
|
|
@@ -235,7 +235,7 @@ const sourceFiles = output => ({
|
|
|
235
235
|
...(output.extension ? { "extension.js": output.extension } : {}),
|
|
236
236
|
});
|
|
237
237
|
|
|
238
|
-
async function
|
|
238
|
+
export async function buildProject(root) {
|
|
239
239
|
const project = loadProject(root);
|
|
240
240
|
const output = await bundle(project);
|
|
241
241
|
const manifest = swipletManifest(project, output);
|
|
@@ -260,76 +260,127 @@ async function buildCommand(root) {
|
|
|
260
260
|
fs.rmSync(archive, { force: true });
|
|
261
261
|
fs.rmSync(path.join(dist, `${manifest.id}.zip`), { force: true });
|
|
262
262
|
execFileSync("zip", ["-qr", "-n", storedSuffixes, archive, manifest.id, "-x", "*.map"], { cwd: dist });
|
|
263
|
-
|
|
263
|
+
return { id: manifest.id, archive, digest: folderDigest(folder) };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function folderDigest(folder) {
|
|
267
|
+
const hash = createHash("sha256");
|
|
268
|
+
const walk = directory => {
|
|
269
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
270
|
+
const name = path.join(directory, entry.name);
|
|
271
|
+
if (entry.isDirectory()) {
|
|
272
|
+
walk(name);
|
|
273
|
+
} else if (!entry.name.endsWith(".map")) {
|
|
274
|
+
hash.update(`${path.relative(folder, name)}\0`);
|
|
275
|
+
hash.update(fs.readFileSync(name));
|
|
276
|
+
hash.update("\0");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
walk(folder);
|
|
281
|
+
return hash.digest("hex");
|
|
264
282
|
}
|
|
265
283
|
|
|
266
284
|
const manifestEntry = /^(?:[^/]+\/)?manifest\.json$/;
|
|
267
285
|
|
|
268
|
-
function
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (
|
|
273
|
-
throw new
|
|
286
|
+
function loadSource(root) {
|
|
287
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
288
|
+
const config = manifest.source ?? {};
|
|
289
|
+
for (const field of ["name", "image", "url"]) {
|
|
290
|
+
if (config[field] !== undefined && typeof config[field] !== "string") {
|
|
291
|
+
throw new Error(`"source.${field}" in package.json must be a string`);
|
|
274
292
|
}
|
|
275
|
-
names[language] = rest.join("=");
|
|
276
293
|
}
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
shownIcon = { color, symbol: symbol.join("/") };
|
|
294
|
+
const localizedNames = config.localizedNames ?? {};
|
|
295
|
+
if (localizedNames === null || typeof localizedNames !== "object" || !Object.values(localizedNames).every(name => typeof name === "string")) {
|
|
296
|
+
throw new Error('"source.localizedNames" in package.json must map languages to names');
|
|
297
|
+
}
|
|
298
|
+
if (config.icon !== undefined && (typeof config.icon?.symbol !== "string" || (config.icon.color !== undefined && typeof config.icon.color !== "string"))) {
|
|
299
|
+
throw new Error('"source.icon" in package.json must have a symbol and may have a color');
|
|
284
300
|
}
|
|
285
301
|
let prefix;
|
|
286
|
-
if (
|
|
302
|
+
if (config.url !== undefined) {
|
|
287
303
|
try {
|
|
288
|
-
prefix = new URL(
|
|
304
|
+
prefix = new URL(config.url.endsWith("/") ? config.url : `${config.url}/`);
|
|
289
305
|
} catch {
|
|
290
|
-
throw new
|
|
306
|
+
throw new Error('"source.url" in package.json is not an absolute URL');
|
|
291
307
|
}
|
|
292
308
|
}
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
309
|
+
const workspaces = Array.isArray(manifest.workspaces) ? manifest.workspaces : manifest.workspaces?.packages ?? [];
|
|
310
|
+
return { root, config, localizedNames, prefix, workspaces };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function workspaceSwiplets(source) {
|
|
314
|
+
const files = [];
|
|
315
|
+
for (const pattern of source.workspaces) {
|
|
316
|
+
for (const directory of fs.globSync(pattern, { cwd: source.root }).sort()) {
|
|
317
|
+
const project = path.join(source.root, directory, "package.json");
|
|
318
|
+
if (!fs.existsSync(project)) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const id = JSON.parse(fs.readFileSync(project, "utf8")).swiplet?.id;
|
|
322
|
+
if (typeof id !== "string") {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const built = path.join(source.root, directory, "dist", `${id}.swiplet`);
|
|
326
|
+
if (!fs.existsSync(built)) {
|
|
327
|
+
throw new Error(`${path.relative(source.root, built)} is missing; build ${directory} first`);
|
|
328
|
+
}
|
|
329
|
+
files.push(built);
|
|
302
330
|
}
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
}
|
|
332
|
+
return files;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function readSwiplet(file) {
|
|
336
|
+
const entry = execFileSync("unzip", ["-Z1", file], { encoding: "utf8" }).split("\n").filter(name => manifestEntry.test(name)).sort((a, b) => a.length - b.length)[0];
|
|
337
|
+
if (entry === undefined) {
|
|
338
|
+
throw new Error(`${file} has no manifest.json`);
|
|
339
|
+
}
|
|
340
|
+
const manifest = JSON.parse(execFileSync("unzip", ["-p", file, entry], { encoding: "utf8" }));
|
|
341
|
+
if (typeof manifest.id !== "string" || !idPattern.test(manifest.id)) {
|
|
342
|
+
throw new Error(`${file} has no valid swiplet id in its manifest`);
|
|
343
|
+
}
|
|
344
|
+
return { file, manifest, data: fs.readFileSync(file) };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function sourceCommand(root, given) {
|
|
348
|
+
const source = loadSource(root);
|
|
349
|
+
const files = given.length > 0 ? given.map(file => path.resolve(file)) : workspaceSwiplets(source);
|
|
350
|
+
if (files.length === 0) {
|
|
351
|
+
throw new Error("package.json lists no workspaces with swiplets; name the swiplets to list");
|
|
352
|
+
}
|
|
353
|
+
const swiplets = new Map();
|
|
354
|
+
for (const read of files.map(readSwiplet)) {
|
|
355
|
+
const { id } = read.manifest;
|
|
356
|
+
if (swiplets.has(id)) {
|
|
357
|
+
throw new Error(`${id} is both ${swiplets.get(id).file} and ${read.file}`);
|
|
305
358
|
}
|
|
306
|
-
|
|
307
|
-
swiplets.set(manifest.id, {
|
|
308
|
-
file,
|
|
309
|
-
entry: {
|
|
310
|
-
...manifest,
|
|
311
|
-
url: prefix ? new URL(path.basename(file), prefix).href : path.basename(file),
|
|
312
|
-
size: data.length,
|
|
313
|
-
sha256: createHash("sha256").update(data).digest("hex"),
|
|
314
|
-
},
|
|
315
|
-
});
|
|
359
|
+
swiplets.set(id, read);
|
|
316
360
|
}
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
)
|
|
328
|
-
if (
|
|
329
|
-
fs.writeFileSync(
|
|
330
|
-
} else {
|
|
331
|
-
process.stdout.write(listed);
|
|
361
|
+
const image = source.config.image === undefined ? undefined : { name: path.basename(source.config.image), data: fs.readFileSync(path.resolve(root, source.config.image)) };
|
|
362
|
+
const located = name => (source.prefix ? new URL(name, source.prefix).href : name);
|
|
363
|
+
const dist = path.join(root, "dist");
|
|
364
|
+
fs.rmSync(dist, { recursive: true, force: true });
|
|
365
|
+
fs.mkdirSync(dist, { recursive: true });
|
|
366
|
+
const entries = [...swiplets.keys()].sort().map(id => {
|
|
367
|
+
const { manifest, data } = swiplets.get(id);
|
|
368
|
+
const name = `${id}.swiplet`;
|
|
369
|
+
fs.writeFileSync(path.join(dist, name), data);
|
|
370
|
+
return { ...manifest, url: located(name), size: data.length, sha256: createHash("sha256").update(data).digest("hex") };
|
|
371
|
+
});
|
|
372
|
+
if (image) {
|
|
373
|
+
fs.writeFileSync(path.join(dist, image.name), image.data);
|
|
332
374
|
}
|
|
375
|
+
const index = {
|
|
376
|
+
...(source.config.name ? { name: source.config.name } : {}),
|
|
377
|
+
...(Object.keys(source.localizedNames).length > 0 ? { localizedNames: source.localizedNames } : {}),
|
|
378
|
+
...(source.config.icon ? { icon: { symbol: source.config.icon.symbol, ...(source.config.icon.color ? { color: source.config.icon.color } : {}) } } : {}),
|
|
379
|
+
...(image ? { image: located(image.name) } : {}),
|
|
380
|
+
swiplets: entries,
|
|
381
|
+
};
|
|
382
|
+
fs.writeFileSync(path.join(dist, "swiplets.json"), `${JSON.stringify(index, null, 2)}\n`);
|
|
383
|
+
console.log(path.relative(process.cwd(), path.join(dist, "swiplets.json")));
|
|
333
384
|
}
|
|
334
385
|
|
|
335
386
|
const idOf = name => name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+/, "").slice(0, 128).replace(/[._-]+$/, "") || "swiplet";
|
|
@@ -349,7 +400,7 @@ const templates = {
|
|
|
349
400
|
app: "./src/App.tsx",
|
|
350
401
|
extension: "./src/extension.ts",
|
|
351
402
|
},
|
|
352
|
-
scripts: { build: "
|
|
403
|
+
scripts: { build: "swipt build", check: "tsc" },
|
|
353
404
|
dependencies: { zod: "^4.6.7" },
|
|
354
405
|
devDependencies: {
|
|
355
406
|
"@swipt/api": version,
|
|
@@ -411,7 +462,7 @@ export default function App() {
|
|
|
411
462
|
|
|
412
463
|
const quote = value => (/^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`);
|
|
413
464
|
|
|
414
|
-
function createCommand(directory) {
|
|
465
|
+
export function createCommand(directory) {
|
|
415
466
|
const root = path.resolve(directory);
|
|
416
467
|
if (fs.existsSync(root) && fs.readdirSync(root).length > 0) {
|
|
417
468
|
throw new Error(`${directory} is not empty`);
|
|
@@ -423,56 +474,3 @@ function createCommand(directory) {
|
|
|
423
474
|
const manager = process.env.npm_config_user_agent?.split("/")[0] ?? "npm";
|
|
424
475
|
console.log(`cd ${quote(directory)} && ${manager} install && ${manager} run build`);
|
|
425
476
|
}
|
|
426
|
-
|
|
427
|
-
const usage = `usage: swiplet create <directory>
|
|
428
|
-
swiplet build
|
|
429
|
-
swiplet source [--name <name>] [--localized-name <language>=<name>]... [--icon <color>/<symbol>] [--image <file>]
|
|
430
|
-
[--url <address>] [--output <file>] <swiplet>...
|
|
431
|
-
|
|
432
|
-
create make a swiplet project in a new or empty directory
|
|
433
|
-
build bundle the project in this directory into dist/<swiplet.id>.swiplet
|
|
434
|
-
source list built swiplets in a source; files are expected next to it unless --url says where they are
|
|
435
|
-
|
|
436
|
-
Type-check a project with its "check" script.`;
|
|
437
|
-
|
|
438
|
-
const commands = { create: 1, build: 0, source: -1 };
|
|
439
|
-
|
|
440
|
-
class UsageError extends Error {}
|
|
441
|
-
|
|
442
|
-
try {
|
|
443
|
-
let parsed;
|
|
444
|
-
try {
|
|
445
|
-
parsed = parseArgs({ allowPositionals: true, options: {
|
|
446
|
-
help: { type: "boolean", short: "h" }, name: { type: "string" }, "localized-name": { type: "string", multiple: true }, icon: { type: "string" },
|
|
447
|
-
image: { type: "string" }, url: { type: "string" }, output: { type: "string" },
|
|
448
|
-
} });
|
|
449
|
-
} catch (error) {
|
|
450
|
-
throw new UsageError(error.message);
|
|
451
|
-
}
|
|
452
|
-
const [command, ...rest] = parsed.positionals;
|
|
453
|
-
if (parsed.values.help) {
|
|
454
|
-
console.log(usage);
|
|
455
|
-
} else if (command === undefined) {
|
|
456
|
-
throw new UsageError("a command is missing");
|
|
457
|
-
} else if (!Object.hasOwn(commands, command)) {
|
|
458
|
-
throw new UsageError(`unknown command ${JSON.stringify(command)}`);
|
|
459
|
-
} else if (commands[command] < 0 ? rest.length === 0 : rest.length !== commands[command]) {
|
|
460
|
-
throw new UsageError(
|
|
461
|
-
commands[command] === 0 ? `${command} takes no arguments` : commands[command] < 0 ? `${command} takes one or more swiplets` : `${command} takes one directory`,
|
|
462
|
-
);
|
|
463
|
-
} else if (command === "create") {
|
|
464
|
-
createCommand(rest[0]);
|
|
465
|
-
} else if (command === "source") {
|
|
466
|
-
sourceCommand(rest, parsed.values.url, parsed.values.output, parsed.values.name, parsed.values["localized-name"], parsed.values.icon, parsed.values.image);
|
|
467
|
-
} else {
|
|
468
|
-
await buildCommand(process.cwd());
|
|
469
|
-
}
|
|
470
|
-
} catch (error) {
|
|
471
|
-
if (error instanceof UsageError) {
|
|
472
|
-
console.error(`error: ${error.message}\n\n${usage}`);
|
|
473
|
-
process.exitCode = 2;
|
|
474
|
-
} else {
|
|
475
|
-
console.error(error.message);
|
|
476
|
-
process.exitCode = 1;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// @generated by protoc-gen-es v2.16.0 with parameter "target=js,import_extension=.js"
|
|
2
|
+
// @generated from file common.proto (package swipt, syntax proto3)
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
|
|
5
|
+
import { enumDesc, fileDesc, messageDesc, tsEnum } from "@bufbuild/protobuf/codegenv2";
|
|
6
|
+
import { file_google_protobuf_struct, file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Describes the file common.proto.
|
|
10
|
+
*/
|
|
11
|
+
export const file_common = /*@__PURE__*/
|
|
12
|
+
fileDesc("Cgxjb21tb24ucHJvdG8SBXN3aXB0InIKCExvZ0VudHJ5EigKBHRpbWUYASABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEh4KBWxldmVsGAIgASgOMg8uc3dpcHQuTG9nTGV2ZWwSCwoDdGFnGAMgASgJEg8KB21lc3NhZ2UYBCABKAkiZwoIRmlsZUluZm8SDAoEcGF0aBgBIAEoCRIRCglkaXJlY3RvcnkYAiABKAgSDAoEc2l6ZRgDIAEoAxIsCghtb2RpZmllZBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiNwoJRmlsZUNodW5rEgwKBHBhdGgYASABKAkSDgoGb2Zmc2V0GAIgASgDEgwKBGRhdGEYAyABKAwiSgoKU3RhY2tGcmFtZRIQCghmdW5jdGlvbhgBIAEoCRIMCgRmaWxlGAIgASgJEgwKBGxpbmUYAyABKA0SDgoGY29sdW1uGAQgASgNIlQKC1NjcmlwdEVycm9yEgwKBG5hbWUYASABKAkSDwoHbWVzc2FnZRgCIAEoCRIgCgVzdGFjaxgEIAMoCzIRLnN3aXB0LlN0YWNrRnJhbWVKBAgDEAQiaAoMQWN0aW9uUmVzdWx0EigKBm91dHB1dBgBIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5WYWx1ZUgAEiMKBWVycm9yGAIgASgLMhIuc3dpcHQuU2NyaXB0RXJyb3JIAEIJCgdvdXRjb21lIoEBChBEZXZpY2VDb25uZWN0aW9uEisKBXN0YXRlGAEgASgOMhwuc3dpcHQuRGV2aWNlQ29ubmVjdGlvblN0YXRlEi8KB3Byb2JsZW0YAiABKA4yHi5zd2lwdC5EZXZpY2VDb25uZWN0aW9uUHJvYmxlbRIPCgdtZXNzYWdlGAMgASgJKncKCExvZ0xldmVsEhkKFUxPR19MRVZFTF9VTlNQRUNJRklFRBAAEhMKD0xPR19MRVZFTF9FUlJPUhABEhIKDkxPR19MRVZFTF9XQVJOEAISEgoOTE9HX0xFVkVMX0lORk8QAxITCg9MT0dfTEVWRUxfREVCVUcQBCr9AQoVRGV2aWNlQ29ubmVjdGlvblN0YXRlEicKI0RFVklDRV9DT05ORUNUSU9OX1NUQVRFX1VOU1BFQ0lGSUVEEAASIwofREVWSUNFX0NPTk5FQ1RJT05fU1RBVEVfU1RPUFBFRBABEiYKIkRFVklDRV9DT05ORUNUSU9OX1NUQVRFX0NPTk5FQ1RJTkcQAhIlCiFERVZJQ0VfQ09OTkVDVElPTl9TVEFURV9QUkVQQVJJTkcQAxIjCh9ERVZJQ0VfQ09OTkVDVElPTl9TVEFURV9SVU5OSU5HEAQSIgoeREVWSUNFX0NPTk5FQ1RJT05fU1RBVEVfRkFJTEVEEAUq9gMKF0RldmljZUNvbm5lY3Rpb25Qcm9ibGVtEikKJURFVklDRV9DT05ORUNUSU9OX1BST0JMRU1fVU5TUEVDSUZJRUQQABIpCiVERVZJQ0VfQ09OTkVDVElPTl9QUk9CTEVNX1VOUkVBQ0hBQkxFEAESLgoqREVWSUNFX0NPTk5FQ1RJT05fUFJPQkxFTV9QQUlSSU5HX1JFSkVDVEVEEAISMAosREVWSUNFX0NPTk5FQ1RJT05fUFJPQkxFTV9ERVZFTE9QRVJfTU9ERV9PRkYQAxIwCixERVZJQ0VfQ09OTkVDVElPTl9QUk9CTEVNX0RJU0tfSU1BR0VfTUlTU0lORxAEEjUKMURFVklDRV9DT05ORUNUSU9OX1BST0JMRU1fRElTS19JTUFHRV9NT1VOVF9GQUlMRUQQBRIwCixERVZJQ0VfQ09OTkVDVElPTl9QUk9CTEVNX0RFVklDRV9VTlNVUFBPUlRFRBAGEisKJ0RFVklDRV9DT05ORUNUSU9OX1BST0JMRU1fU0NSRUVOX0lOX1VTRRAHEjYKMkRFVklDRV9DT05ORUNUSU9OX1BST0JMRU1fVlBOX1BFRVJfQUREUkVTU19NSVNTSU5HEAgSIwofREVWSUNFX0NPTk5FQ1RJT05fUFJPQkxFTV9PVEhFUhAJQjJaMGdpdGh1Yi5jb20vc3dpcHQtYXV0b21hdGlvbi9zd2lwdC9pbnRlcm5hbC9wcm90b2IGcHJvdG8z", [file_google_protobuf_struct, file_google_protobuf_timestamp]);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Describes the message swipt.LogEntry.
|
|
16
|
+
* Use `create(LogEntrySchema)` to create a new message.
|
|
17
|
+
*/
|
|
18
|
+
export const LogEntrySchema = /*@__PURE__*/
|
|
19
|
+
messageDesc(file_common, 0);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Describes the message swipt.FileInfo.
|
|
23
|
+
* Use `create(FileInfoSchema)` to create a new message.
|
|
24
|
+
*/
|
|
25
|
+
export const FileInfoSchema = /*@__PURE__*/
|
|
26
|
+
messageDesc(file_common, 1);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Describes the message swipt.FileChunk.
|
|
30
|
+
* Use `create(FileChunkSchema)` to create a new message.
|
|
31
|
+
*/
|
|
32
|
+
export const FileChunkSchema = /*@__PURE__*/
|
|
33
|
+
messageDesc(file_common, 2);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Describes the message swipt.StackFrame.
|
|
37
|
+
* Use `create(StackFrameSchema)` to create a new message.
|
|
38
|
+
*/
|
|
39
|
+
export const StackFrameSchema = /*@__PURE__*/
|
|
40
|
+
messageDesc(file_common, 3);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Describes the message swipt.ScriptError.
|
|
44
|
+
* Use `create(ScriptErrorSchema)` to create a new message.
|
|
45
|
+
*/
|
|
46
|
+
export const ScriptErrorSchema = /*@__PURE__*/
|
|
47
|
+
messageDesc(file_common, 4);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Describes the message swipt.ActionResult.
|
|
51
|
+
* Use `create(ActionResultSchema)` to create a new message.
|
|
52
|
+
*/
|
|
53
|
+
export const ActionResultSchema = /*@__PURE__*/
|
|
54
|
+
messageDesc(file_common, 5);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Describes the message swipt.DeviceConnection.
|
|
58
|
+
* Use `create(DeviceConnectionSchema)` to create a new message.
|
|
59
|
+
*/
|
|
60
|
+
export const DeviceConnectionSchema = /*@__PURE__*/
|
|
61
|
+
messageDesc(file_common, 6);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Describes the enum swipt.LogLevel.
|
|
65
|
+
*/
|
|
66
|
+
export const LogLevelSchema = /*@__PURE__*/
|
|
67
|
+
enumDesc(file_common, 0);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @generated from enum swipt.LogLevel
|
|
71
|
+
*/
|
|
72
|
+
export const LogLevel = /*@__PURE__*/
|
|
73
|
+
tsEnum(LogLevelSchema);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Describes the enum swipt.DeviceConnectionState.
|
|
77
|
+
*/
|
|
78
|
+
export const DeviceConnectionStateSchema = /*@__PURE__*/
|
|
79
|
+
enumDesc(file_common, 1);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @generated from enum swipt.DeviceConnectionState
|
|
83
|
+
*/
|
|
84
|
+
export const DeviceConnectionState = /*@__PURE__*/
|
|
85
|
+
tsEnum(DeviceConnectionStateSchema);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Describes the enum swipt.DeviceConnectionProblem.
|
|
89
|
+
*/
|
|
90
|
+
export const DeviceConnectionProblemSchema = /*@__PURE__*/
|
|
91
|
+
enumDesc(file_common, 2);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @generated from enum swipt.DeviceConnectionProblem
|
|
95
|
+
*/
|
|
96
|
+
export const DeviceConnectionProblem = /*@__PURE__*/
|
|
97
|
+
tsEnum(DeviceConnectionProblemSchema);
|
|
98
|
+
|
package/proto/logs_pb.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @generated by protoc-gen-es v2.16.0 with parameter "target=js,import_extension=.js"
|
|
2
|
+
// @generated from file logs.proto (package swipt, syntax proto3)
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
|
|
5
|
+
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
|
|
6
|
+
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
|
|
7
|
+
import { file_common } from "./common_pb.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Describes the file logs.proto.
|
|
11
|
+
*/
|
|
12
|
+
export const file_logs = /*@__PURE__*/
|
|
13
|
+
fileDesc("Cgpsb2dzLnByb3RvEgVzd2lwdCKOAQoPUmVhZExvZ3NSZXF1ZXN0Eh4KBWxldmVsGAEgASgOMg8uc3dpcHQuTG9nTGV2ZWwSEgoKc3dpcGxldF9pZBgCIAEoCRIpCgVzaW5jZRgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDAoEdGFpbBgEIAEoDRIOCgZmb2xsb3cYBSABKAgiMgoQUmVhZExvZ3NSZXNwb25zZRIeCgVlbnRyeRgBIAEoCzIPLnN3aXB0LkxvZ0VudHJ5MksKCkxvZ1NlcnZpY2USPQoIUmVhZExvZ3MSFi5zd2lwdC5SZWFkTG9nc1JlcXVlc3QaFy5zd2lwdC5SZWFkTG9nc1Jlc3BvbnNlMAFCMlowZ2l0aHViLmNvbS9zd2lwdC1hdXRvbWF0aW9uL3N3aXB0L2ludGVybmFsL3Byb3RvYgZwcm90bzM", [file_google_protobuf_timestamp, file_common]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Describes the message swipt.ReadLogsRequest.
|
|
17
|
+
* Use `create(ReadLogsRequestSchema)` to create a new message.
|
|
18
|
+
*/
|
|
19
|
+
export const ReadLogsRequestSchema = /*@__PURE__*/
|
|
20
|
+
messageDesc(file_logs, 0);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Describes the message swipt.ReadLogsResponse.
|
|
24
|
+
* Use `create(ReadLogsResponseSchema)` to create a new message.
|
|
25
|
+
*/
|
|
26
|
+
export const ReadLogsResponseSchema = /*@__PURE__*/
|
|
27
|
+
messageDesc(file_logs, 1);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @generated from service swipt.LogService
|
|
31
|
+
*/
|
|
32
|
+
export const LogService = /*@__PURE__*/
|
|
33
|
+
serviceDesc(file_logs, 0);
|
|
34
|
+
|