@learncard/cli 3.4.5 → 3.4.6
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/CHANGELOG.md +19 -0
- package/dist/index.js +345 -25
- package/package.json +52 -50
- package/project.json +1 -1
- package/src/index.tsx +433 -34
- package/LICENSE +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @learncard/cli
|
|
2
2
|
|
|
3
|
+
## 3.4.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1303](https://github.com/learningeconomy/LearnCard/pull/1303) [`59d79e9c2aed145284d6cc3de4c53ef0d3415299`](https://github.com/learningeconomy/LearnCard/commit/59d79e9c2aed145284d6cc3de4c53ef0d3415299) Thanks [@TaylorBeeston](https://github.com/TaylorBeeston)! - Switch workspace development to Bun source-mode resolution while preserving package build outputs for npm publishing.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`59d79e9c2aed145284d6cc3de4c53ef0d3415299`](https://github.com/learningeconomy/LearnCard/commit/59d79e9c2aed145284d6cc3de4c53ef0d3415299), [`8bcccce23f919e9bcd0d22d87e7d33242b557930`](https://github.com/learningeconomy/LearnCard/commit/8bcccce23f919e9bcd0d22d87e7d33242b557930), [`8bcccce23f919e9bcd0d22d87e7d33242b557930`](https://github.com/learningeconomy/LearnCard/commit/8bcccce23f919e9bcd0d22d87e7d33242b557930), [`8bcccce23f919e9bcd0d22d87e7d33242b557930`](https://github.com/learningeconomy/LearnCard/commit/8bcccce23f919e9bcd0d22d87e7d33242b557930)]:
|
|
10
|
+
- @learncard/holder-continuity@0.2.6
|
|
11
|
+
- @learncard/core@9.4.25
|
|
12
|
+
- @learncard/init@2.4.4
|
|
13
|
+
- @learncard/types@5.17.5
|
|
14
|
+
- @learncard/didkit-plugin@1.9.5
|
|
15
|
+
- @learncard/learn-cloud-plugin@2.3.30
|
|
16
|
+
- @learncard/ler-rs-plugin@0.1.16
|
|
17
|
+
- @learncard/linked-claims-plugin@0.2.25
|
|
18
|
+
- @learncard/open-badge-v2-plugin@1.1.26
|
|
19
|
+
- @learncard/render-method-plugin@3.0.5
|
|
20
|
+
- @learncard/simple-signing-plugin@1.1.29
|
|
21
|
+
|
|
3
22
|
## 3.4.5
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
var fs = require('fs/promises');
|
|
5
5
|
var dns = require('node:dns');
|
|
6
|
+
var node_readline = require('node:readline');
|
|
6
7
|
var promises = require('node:readline/promises');
|
|
7
8
|
var node_stream = require('node:stream');
|
|
8
|
-
var repl = require('pretty-repl');
|
|
9
9
|
var core = require('@learncard/core');
|
|
10
10
|
var init = require('@learncard/init');
|
|
11
11
|
var simpleSigningPlugin = require('@learncard/simple-signing-plugin');
|
|
@@ -23,10 +23,27 @@ var holderContinuity = require('@learncard/holder-continuity');
|
|
|
23
23
|
|
|
24
24
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
25
25
|
|
|
26
|
+
function _interopNamespace(e) {
|
|
27
|
+
if (e && e.__esModule) return e;
|
|
28
|
+
var n = Object.create(null);
|
|
29
|
+
if (e) {
|
|
30
|
+
Object.keys(e).forEach(function (k) {
|
|
31
|
+
if (k !== 'default') {
|
|
32
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
33
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
get: function () { return e[k]; }
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
n["default"] = e;
|
|
41
|
+
return Object.freeze(n);
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
27
45
|
var dns__default = /*#__PURE__*/_interopDefaultLegacy(dns);
|
|
28
|
-
var
|
|
29
|
-
var types__default = /*#__PURE__*/_interopDefaultLegacy(types);
|
|
46
|
+
var types__namespace = /*#__PURE__*/_interopNamespace(types);
|
|
30
47
|
var gradient__default = /*#__PURE__*/_interopDefaultLegacy(gradient);
|
|
31
48
|
var figlet__default = /*#__PURE__*/_interopDefaultLegacy(figlet);
|
|
32
49
|
var clipboard__default = /*#__PURE__*/_interopDefaultLegacy(clipboard);
|
|
@@ -70,12 +87,14 @@ const createRestoreLearnCardFromBundleHelper = (restoreBundle, defaultInit) => {
|
|
|
70
87
|
};
|
|
71
88
|
|
|
72
89
|
var name = "@learncard/cli";
|
|
73
|
-
var version = "3.4.
|
|
90
|
+
var version = "3.4.6";
|
|
74
91
|
var description = "Command Line Interface for LearnCard";
|
|
75
92
|
var main = "dist/index.js";
|
|
76
93
|
var bin = "dist/index.js";
|
|
77
94
|
var scripts = {
|
|
78
|
-
start: "
|
|
95
|
+
start: "bun --conditions=development src/index.tsx",
|
|
96
|
+
"start:built": "rollup -c && node --enable-source-maps dist/index.js",
|
|
97
|
+
dev: "bun --conditions=development src/index.tsx",
|
|
79
98
|
build: "rollup -c",
|
|
80
99
|
test: "vitest run"
|
|
81
100
|
};
|
|
@@ -134,22 +153,56 @@ var packageJson = {
|
|
|
134
153
|
bugs: bugs
|
|
135
154
|
};
|
|
136
155
|
|
|
156
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
157
|
+
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
|
|
137
158
|
dns__default["default"].setDefaultResultOrder("ipv4first");
|
|
159
|
+
const replGlobals = [
|
|
160
|
+
"learnCard",
|
|
161
|
+
"emptyLearnCard",
|
|
162
|
+
"learnCardFromSeed",
|
|
163
|
+
"initLearnCard",
|
|
164
|
+
"seed",
|
|
165
|
+
"generateRandomSeed",
|
|
166
|
+
"types",
|
|
167
|
+
"getTestCache",
|
|
168
|
+
"copy",
|
|
169
|
+
"getLearnCardBundlePassword",
|
|
170
|
+
"exportLearnCardBundle",
|
|
171
|
+
"importLearnCardBundle",
|
|
172
|
+
"createLearnCardBundle",
|
|
173
|
+
"readLearnCardBundle",
|
|
174
|
+
"restoreLearnCardFromBundle"
|
|
175
|
+
];
|
|
176
|
+
const replCommands = [".exit"];
|
|
177
|
+
const identifierPattern = /^[A-Za-z_$][\w$]*$/;
|
|
178
|
+
const colorizeName = (name) => {
|
|
179
|
+
const colorizedName = gradient__default["default"](["cyan", "green"])(name);
|
|
180
|
+
if (colorizedName !== name || !process.stdout.isTTY) return colorizedName;
|
|
181
|
+
return `\x1B[36m${name}\x1B[39m`;
|
|
182
|
+
};
|
|
138
183
|
const g = {
|
|
139
|
-
learnCard:
|
|
140
|
-
emptyLearnCard:
|
|
141
|
-
|
|
142
|
-
initLearnCard:
|
|
143
|
-
seed:
|
|
144
|
-
generateRandomSeed:
|
|
145
|
-
types:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
184
|
+
learnCard: colorizeName("learnCard"),
|
|
185
|
+
emptyLearnCard: colorizeName("emptyLearnCard"),
|
|
186
|
+
learnCardFromSeed: colorizeName("learnCardFromSeed"),
|
|
187
|
+
initLearnCard: colorizeName("initLearnCard"),
|
|
188
|
+
seed: colorizeName("seed"),
|
|
189
|
+
generateRandomSeed: colorizeName("generateRandomSeed"),
|
|
190
|
+
types: colorizeName("types"),
|
|
191
|
+
getTestCache: colorizeName("getTestCache"),
|
|
192
|
+
getLearnCardBundlePassword: colorizeName("getLearnCardBundlePassword"),
|
|
193
|
+
copy: colorizeName("copy"),
|
|
194
|
+
exportLearnCardBundle: colorizeName("exportLearnCardBundle"),
|
|
195
|
+
importLearnCardBundle: colorizeName("importLearnCardBundle"),
|
|
196
|
+
createLearnCardBundle: colorizeName("createLearnCardBundle"),
|
|
197
|
+
readLearnCardBundle: colorizeName("readLearnCardBundle"),
|
|
198
|
+
restoreLearnCardFromBundle: colorizeName("restoreLearnCardFromBundle")
|
|
199
|
+
};
|
|
200
|
+
const colorizeReplInput = (input) => {
|
|
201
|
+
return [...replGlobals].sort((a, b) => b.length - a.length).reduce((output, name) => {
|
|
202
|
+
const colorizedName = g[name];
|
|
203
|
+
if (!colorizedName) return output;
|
|
204
|
+
return output.replace(new RegExp(`\\b${name}\\b`, "g"), colorizedName);
|
|
205
|
+
}, input);
|
|
153
206
|
};
|
|
154
207
|
const copyFunction = (text) => {
|
|
155
208
|
if (typeof text === "object") {
|
|
@@ -183,6 +236,277 @@ const getLearnCardBundlePassword = async (prompt = "Bundle password: ") => {
|
|
|
183
236
|
process.stdout.write("\n");
|
|
184
237
|
}
|
|
185
238
|
};
|
|
239
|
+
const globalAssignmentPattern = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([\s\S]*?);?\s*$/;
|
|
240
|
+
const parseGlobalAssignment = (source) => {
|
|
241
|
+
const match = source.match(globalAssignmentPattern);
|
|
242
|
+
if (!match) return;
|
|
243
|
+
const [, name, initializer] = match;
|
|
244
|
+
return { name, initializer };
|
|
245
|
+
};
|
|
246
|
+
const getCurrentToken = (line) => {
|
|
247
|
+
var _a, _b;
|
|
248
|
+
return (_b = (_a = line.match(/(?:[A-Za-z_$][\w$]*\.)*[A-Za-z_$][\w$]*\.?$/)) == null ? void 0 : _a[0]) != null ? _b : "";
|
|
249
|
+
};
|
|
250
|
+
const getCompletionBase = (token) => {
|
|
251
|
+
const lastDotIndex = token.lastIndexOf(".");
|
|
252
|
+
if (lastDotIndex === -1) return;
|
|
253
|
+
return token.slice(0, lastDotIndex);
|
|
254
|
+
};
|
|
255
|
+
const getCompletionPrefix = (token) => {
|
|
256
|
+
const lastDotIndex = token.lastIndexOf(".");
|
|
257
|
+
if (lastDotIndex === -1) return token;
|
|
258
|
+
return token.slice(lastDotIndex + 1);
|
|
259
|
+
};
|
|
260
|
+
const getOwnPropertyNames = (value) => {
|
|
261
|
+
if (value === null || value === void 0) return [];
|
|
262
|
+
const names = /* @__PURE__ */ new Set();
|
|
263
|
+
let current = Object(value);
|
|
264
|
+
while (current && current !== Object.prototype) {
|
|
265
|
+
for (const name of Object.getOwnPropertyNames(current)) names.add(name);
|
|
266
|
+
current = Object.getPrototypeOf(current);
|
|
267
|
+
}
|
|
268
|
+
return [...names];
|
|
269
|
+
};
|
|
270
|
+
const completeBunReplInput = (line) => {
|
|
271
|
+
const token = getCurrentToken(line);
|
|
272
|
+
if (!token) return [replCommands, ""];
|
|
273
|
+
const base = getCompletionBase(token);
|
|
274
|
+
const prefix = getCompletionPrefix(token);
|
|
275
|
+
if (!base) {
|
|
276
|
+
const completions = [...replCommands, ...replGlobals].filter(
|
|
277
|
+
(name) => name.startsWith(prefix)
|
|
278
|
+
);
|
|
279
|
+
return [completions.length ? completions : replGlobals, token];
|
|
280
|
+
}
|
|
281
|
+
try {
|
|
282
|
+
const target = globalThis.eval(base);
|
|
283
|
+
const completions = getOwnPropertyNames(target).filter((name) => identifierPattern.test(name)).filter((name) => name.startsWith(prefix)).map((name) => `${base}.${name}`);
|
|
284
|
+
return [completions, token];
|
|
285
|
+
} catch (e) {
|
|
286
|
+
return [[], token];
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const evaluateBunReplInput = async (source) => {
|
|
290
|
+
try {
|
|
291
|
+
return await globalThis.eval(`(async () => (${source}))()`);
|
|
292
|
+
} catch (e) {
|
|
293
|
+
const globalAssignment = parseGlobalAssignment(source);
|
|
294
|
+
if (globalAssignment) {
|
|
295
|
+
const value = await globalThis.eval(
|
|
296
|
+
`(async () => (${globalAssignment.initializer}))()`
|
|
297
|
+
);
|
|
298
|
+
globalThis[globalAssignment.name] = value;
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
return await globalThis.eval(`(async () => { ${source} })()`);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const getSharedCompletion = (matches) => {
|
|
305
|
+
if (!matches.length) return;
|
|
306
|
+
return matches.reduce((prefix, match) => {
|
|
307
|
+
let index = 0;
|
|
308
|
+
while (index < prefix.length && prefix[index] === match[index]) index += 1;
|
|
309
|
+
return prefix.slice(0, index);
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
const renderBunReplLine = (prompt, line, cursor, colorize) => {
|
|
313
|
+
process.stdout.clearLine(0);
|
|
314
|
+
process.stdout.cursorTo(0);
|
|
315
|
+
process.stdout.write(prompt + colorize(line));
|
|
316
|
+
const trailingLength = line.length - cursor;
|
|
317
|
+
if (trailingLength > 0) process.stdout.write(`\x1B[${trailingLength}D`);
|
|
318
|
+
};
|
|
319
|
+
const startReadlineRepl = async (colorize) => {
|
|
320
|
+
const rl = promises.createInterface({
|
|
321
|
+
input: process.stdin,
|
|
322
|
+
output: process.stdout,
|
|
323
|
+
prompt: "> ",
|
|
324
|
+
completer: completeBunReplInput
|
|
325
|
+
});
|
|
326
|
+
rl.prompt();
|
|
327
|
+
try {
|
|
328
|
+
for (var iter = __forAwait(rl), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
329
|
+
const line = temp.value;
|
|
330
|
+
const source = line.trim();
|
|
331
|
+
if (source === ".exit") break;
|
|
332
|
+
if (!source) {
|
|
333
|
+
rl.prompt();
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
const result = await evaluateBunReplInput(source);
|
|
338
|
+
if (result !== void 0) console.dir(result, { depth: 6 });
|
|
339
|
+
} catch (error2) {
|
|
340
|
+
console.error(error2);
|
|
341
|
+
}
|
|
342
|
+
if (colorize) process.stdout.write(colorize(""));
|
|
343
|
+
rl.prompt();
|
|
344
|
+
}
|
|
345
|
+
} catch (temp) {
|
|
346
|
+
error = [temp];
|
|
347
|
+
} finally {
|
|
348
|
+
try {
|
|
349
|
+
more && (temp = iter.return) && await temp.call(iter);
|
|
350
|
+
} finally {
|
|
351
|
+
if (error)
|
|
352
|
+
throw error[0];
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
rl.close();
|
|
356
|
+
};
|
|
357
|
+
const startBunRepl = async (colorize) => {
|
|
358
|
+
if (!process.stdin.isTTY || !process.stdin.setRawMode) {
|
|
359
|
+
await startReadlineRepl(colorize);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const prompt = "> ";
|
|
363
|
+
const history = [];
|
|
364
|
+
let line = "";
|
|
365
|
+
let cursor = 0;
|
|
366
|
+
let historyIndex = 0;
|
|
367
|
+
let draft = "";
|
|
368
|
+
let pending = Promise.resolve();
|
|
369
|
+
const resetHistory = () => {
|
|
370
|
+
historyIndex = history.length;
|
|
371
|
+
draft = "";
|
|
372
|
+
};
|
|
373
|
+
const setLine = (nextLine) => {
|
|
374
|
+
line = nextLine;
|
|
375
|
+
cursor = line.length;
|
|
376
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
377
|
+
};
|
|
378
|
+
const insertText = (text) => {
|
|
379
|
+
line = line.slice(0, cursor) + text + line.slice(cursor);
|
|
380
|
+
cursor += text.length;
|
|
381
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
382
|
+
};
|
|
383
|
+
const applyCompletion = () => {
|
|
384
|
+
const linePrefix = line.slice(0, cursor);
|
|
385
|
+
const [matches, token] = completeBunReplInput(linePrefix);
|
|
386
|
+
const completion = matches.length === 1 ? matches[0] : getSharedCompletion(matches);
|
|
387
|
+
if (completion && completion !== token) {
|
|
388
|
+
line = line.slice(0, cursor - token.length) + completion + line.slice(cursor);
|
|
389
|
+
cursor += completion.length - token.length;
|
|
390
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (matches.length > 1) {
|
|
394
|
+
process.stdout.write(`
|
|
395
|
+
${matches.join(" ")}
|
|
396
|
+
`);
|
|
397
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const evaluateLine = async () => {
|
|
401
|
+
process.stdout.write("\n");
|
|
402
|
+
const source = line.trim();
|
|
403
|
+
if (source === ".exit") return false;
|
|
404
|
+
if (source) {
|
|
405
|
+
history.push(source);
|
|
406
|
+
try {
|
|
407
|
+
const result = await evaluateBunReplInput(source);
|
|
408
|
+
if (result !== void 0) console.dir(result, { depth: 6 });
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error(error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
line = "";
|
|
414
|
+
cursor = 0;
|
|
415
|
+
resetHistory();
|
|
416
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
417
|
+
return true;
|
|
418
|
+
};
|
|
419
|
+
const handleKey = async (input, key) => {
|
|
420
|
+
var _a, _b;
|
|
421
|
+
if (key.ctrl && key.name === "c") return false;
|
|
422
|
+
if (key.name === "return" || key.name === "enter") return await evaluateLine();
|
|
423
|
+
if (key.ctrl && key.name === "d" && !line) return false;
|
|
424
|
+
if (key.name === "tab") {
|
|
425
|
+
applyCompletion();
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
if (key.name === "backspace") {
|
|
429
|
+
if (cursor === 0) return true;
|
|
430
|
+
line = line.slice(0, cursor - 1) + line.slice(cursor);
|
|
431
|
+
cursor -= 1;
|
|
432
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
if (key.name === "delete") {
|
|
436
|
+
if (cursor >= line.length) return true;
|
|
437
|
+
line = line.slice(0, cursor) + line.slice(cursor + 1);
|
|
438
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
if (key.name === "left") {
|
|
442
|
+
if (cursor > 0) cursor -= 1;
|
|
443
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
if (key.name === "right") {
|
|
447
|
+
if (cursor < line.length) cursor += 1;
|
|
448
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
if (key.name === "home") {
|
|
452
|
+
cursor = 0;
|
|
453
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
if (key.name === "end") {
|
|
457
|
+
cursor = line.length;
|
|
458
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
if (key.name === "up") {
|
|
462
|
+
if (historyIndex === history.length) draft = line;
|
|
463
|
+
if (historyIndex > 0) historyIndex -= 1;
|
|
464
|
+
setLine((_a = history[historyIndex]) != null ? _a : draft);
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
if (key.name === "down") {
|
|
468
|
+
if (historyIndex < history.length) historyIndex += 1;
|
|
469
|
+
setLine(historyIndex === history.length ? draft : (_b = history[historyIndex]) != null ? _b : "");
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
if (input && !key.ctrl && input >= " ") {
|
|
473
|
+
insertText(input);
|
|
474
|
+
resetHistory();
|
|
475
|
+
}
|
|
476
|
+
return true;
|
|
477
|
+
};
|
|
478
|
+
await new Promise((resolve) => {
|
|
479
|
+
var _a, _b;
|
|
480
|
+
const onKeypress = (input, key) => {
|
|
481
|
+
pending = pending.then(async () => {
|
|
482
|
+
const keepRunning = await handleKey(input, key);
|
|
483
|
+
if (!keepRunning) cleanup();
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
const cleanup = () => {
|
|
487
|
+
var _a2, _b2;
|
|
488
|
+
process.stdin.off("keypress", onKeypress);
|
|
489
|
+
(_b2 = (_a2 = process.stdin).setRawMode) == null ? void 0 : _b2.call(_a2, false);
|
|
490
|
+
process.stdin.pause();
|
|
491
|
+
process.stdout.write("\n");
|
|
492
|
+
resolve();
|
|
493
|
+
};
|
|
494
|
+
node_readline.emitKeypressEvents(process.stdin);
|
|
495
|
+
(_b = (_a = process.stdin).setRawMode) == null ? void 0 : _b.call(_a, true);
|
|
496
|
+
process.stdin.resume();
|
|
497
|
+
console.log("Bun dev REPL ready. Tab completes globals/properties. Type .exit to quit.");
|
|
498
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
499
|
+
process.stdin.on("keypress", onKeypress);
|
|
500
|
+
});
|
|
501
|
+
};
|
|
502
|
+
const startCliRepl = async (colorize) => {
|
|
503
|
+
if (!("Bun" in globalThis)) {
|
|
504
|
+
const repl = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('pretty-repl')); });
|
|
505
|
+
repl.default.start({ colorize });
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
await startBunRepl(colorize);
|
|
509
|
+
};
|
|
186
510
|
commander.program.version(packageJson.version).argument("[seed]").action(async (_seed = generateRandomSeed()) => {
|
|
187
511
|
var _a, _b, _c, _d;
|
|
188
512
|
console.clear();
|
|
@@ -219,7 +543,7 @@ commander.program.version(packageJson.version).argument("[seed]").action(async (
|
|
|
219
543
|
globalThis.learnCard = await globalThis.learnCard.addPlugin(
|
|
220
544
|
renderMethodPlugin.getRenderMethodPlugin(globalThis.learnCard)
|
|
221
545
|
);
|
|
222
|
-
globalThis.types =
|
|
546
|
+
globalThis.types = types__namespace;
|
|
223
547
|
globalThis.getTestCache = core.getTestCache;
|
|
224
548
|
globalThis.copy = copyFunction;
|
|
225
549
|
globalThis.getLearnCardBundlePassword = getLearnCardBundlePassword;
|
|
@@ -303,9 +627,5 @@ commander.program.version(packageJson.version).argument("[seed]").action(async (
|
|
|
303
627
|
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
|
|
304
628
|
);
|
|
305
629
|
console.log("");
|
|
306
|
-
|
|
307
|
-
colorize: (input) => {
|
|
308
|
-
return input.replace("emptyLearnCard", g.emptyLearnCard).replace("learnCardFromKey", g.learnCardFromKey).replace("initLearnCard", g.initLearnCard).replace("learnCard", g.learnCard).replace("seed", g.seed).replace("generateRandomSeed", g.generateRandomSeed).replace("copy", g.copy).replace("getLearnCardBundlePassword", g.getLearnCardBundlePassword).replace("exportLearnCardBundle", g.exportLearnCardBundle).replace("importLearnCardBundle", g.importLearnCardBundle).replace("createLearnCardBundle", g.createLearnCardBundle).replace("readLearnCardBundle", g.readLearnCardBundle).replace("restoreLearnCardFromBundle", g.restoreLearnCardFromBundle);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
630
|
+
await startCliRepl(colorizeReplInput);
|
|
311
631
|
}).parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,51 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
2
|
+
"name": "@learncard/cli",
|
|
3
|
+
"version": "3.4.6",
|
|
4
|
+
"description": "Command Line Interface for LearnCard",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "bun --conditions=development src/index.tsx",
|
|
9
|
+
"start:built": "rollup -c && node --enable-source-maps dist/index.js",
|
|
10
|
+
"dev": "bun --conditions=development src/index.tsx",
|
|
11
|
+
"build": "rollup -c",
|
|
12
|
+
"test": "vitest run"
|
|
13
|
+
},
|
|
14
|
+
"author": "Learning Economy Foundation (www.learningeconomy.io)",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@learncard/core": "workspace:*",
|
|
18
|
+
"@learncard/didkit-plugin": "workspace:^",
|
|
19
|
+
"@learncard/init": "workspace:^",
|
|
20
|
+
"@learncard/learn-cloud-plugin": "workspace:*",
|
|
21
|
+
"@learncard/ler-rs-plugin": "workspace:*",
|
|
22
|
+
"@learncard/linked-claims-plugin": "workspace:*",
|
|
23
|
+
"@learncard/open-badge-v2-plugin": "workspace:*",
|
|
24
|
+
"@learncard/render-method-plugin": "workspace:*",
|
|
25
|
+
"@learncard/simple-signing-plugin": "workspace:*",
|
|
26
|
+
"@learncard/holder-continuity": "workspace:*",
|
|
27
|
+
"@learncard/types": "workspace:*",
|
|
28
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
29
|
+
"clipboardy": "^4.0.0",
|
|
30
|
+
"commander": "^9.3.0",
|
|
31
|
+
"figlet": "^1.5.2",
|
|
32
|
+
"gradient-string": "^2.0.1",
|
|
33
|
+
"pretty-repl": "^3.1.1",
|
|
34
|
+
"rollup": "^2.80.0",
|
|
35
|
+
"rollup-plugin-esbuild": "^4.9.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/cors": "^2.8.12",
|
|
39
|
+
"@types/figlet": "^1.5.4",
|
|
40
|
+
"@types/node": "^18.0.0",
|
|
41
|
+
"nodemon": "^2.0.16",
|
|
42
|
+
"ts-node": "^10.8.1",
|
|
43
|
+
"vitest": "^1.6.0"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/learningeconomy/LearnCard"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/learningeconomy/LearnCard/tree/main/packages/learn-card-cli/README.md",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/learningeconomy/LearnCard/issues"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/project.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import dns from 'node:dns';
|
|
3
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
3
4
|
import { createInterface } from 'node:readline/promises';
|
|
4
5
|
import { Writable } from 'node:stream';
|
|
5
6
|
|
|
6
|
-
import repl from 'pretty-repl';
|
|
7
7
|
import { getTestCache } from '@learncard/core';
|
|
8
8
|
import { initLearnCard, emptyLearnCard, learnCardFromSeed } from '@learncard/init';
|
|
9
9
|
import { getSimpleSigningPlugin } from '@learncard/simple-signing-plugin';
|
|
10
10
|
import { openBadgeV2Plugin } from '@learncard/open-badge-v2-plugin';
|
|
11
|
-
import types from '@learncard/types';
|
|
11
|
+
import * as types from '@learncard/types';
|
|
12
12
|
import { getLinkedClaimsPlugin } from '@learncard/linked-claims-plugin';
|
|
13
13
|
import gradient from 'gradient-string';
|
|
14
14
|
import figlet from 'figlet';
|
|
@@ -35,21 +35,71 @@ import packageJson from '../package.json';
|
|
|
35
35
|
|
|
36
36
|
dns.setDefaultResultOrder('ipv4first');
|
|
37
37
|
|
|
38
|
+
type ReplCompletion = [string[], string];
|
|
39
|
+
type ReplKey = {
|
|
40
|
+
ctrl?: boolean;
|
|
41
|
+
name?: string;
|
|
42
|
+
sequence?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const replGlobals = [
|
|
46
|
+
'learnCard',
|
|
47
|
+
'emptyLearnCard',
|
|
48
|
+
'learnCardFromSeed',
|
|
49
|
+
'initLearnCard',
|
|
50
|
+
'seed',
|
|
51
|
+
'generateRandomSeed',
|
|
52
|
+
'types',
|
|
53
|
+
'getTestCache',
|
|
54
|
+
'copy',
|
|
55
|
+
'getLearnCardBundlePassword',
|
|
56
|
+
'exportLearnCardBundle',
|
|
57
|
+
'importLearnCardBundle',
|
|
58
|
+
'createLearnCardBundle',
|
|
59
|
+
'readLearnCardBundle',
|
|
60
|
+
'restoreLearnCardFromBundle',
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const replCommands = ['.exit'];
|
|
64
|
+
|
|
65
|
+
const identifierPattern = /^[A-Za-z_$][\w$]*$/;
|
|
66
|
+
|
|
67
|
+
const colorizeName = (name: string): string => {
|
|
68
|
+
const colorizedName = gradient(['cyan', 'green'])(name);
|
|
69
|
+
|
|
70
|
+
if (colorizedName !== name || !process.stdout.isTTY) return colorizedName;
|
|
71
|
+
|
|
72
|
+
return `\x1b[36m${name}\x1b[39m`;
|
|
73
|
+
};
|
|
74
|
+
|
|
38
75
|
const g = {
|
|
39
|
-
learnCard:
|
|
40
|
-
emptyLearnCard:
|
|
41
|
-
|
|
42
|
-
initLearnCard:
|
|
43
|
-
seed:
|
|
44
|
-
generateRandomSeed:
|
|
45
|
-
types:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
learnCard: colorizeName('learnCard'),
|
|
77
|
+
emptyLearnCard: colorizeName('emptyLearnCard'),
|
|
78
|
+
learnCardFromSeed: colorizeName('learnCardFromSeed'),
|
|
79
|
+
initLearnCard: colorizeName('initLearnCard'),
|
|
80
|
+
seed: colorizeName('seed'),
|
|
81
|
+
generateRandomSeed: colorizeName('generateRandomSeed'),
|
|
82
|
+
types: colorizeName('types'),
|
|
83
|
+
getTestCache: colorizeName('getTestCache'),
|
|
84
|
+
getLearnCardBundlePassword: colorizeName('getLearnCardBundlePassword'),
|
|
85
|
+
copy: colorizeName('copy'),
|
|
86
|
+
exportLearnCardBundle: colorizeName('exportLearnCardBundle'),
|
|
87
|
+
importLearnCardBundle: colorizeName('importLearnCardBundle'),
|
|
88
|
+
createLearnCardBundle: colorizeName('createLearnCardBundle'),
|
|
89
|
+
readLearnCardBundle: colorizeName('readLearnCardBundle'),
|
|
90
|
+
restoreLearnCardFromBundle: colorizeName('restoreLearnCardFromBundle'),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const colorizeReplInput = (input: string): string => {
|
|
94
|
+
return [...replGlobals]
|
|
95
|
+
.sort((a, b) => b.length - a.length)
|
|
96
|
+
.reduce((output, name) => {
|
|
97
|
+
const colorizedName = g[name as keyof typeof g];
|
|
98
|
+
|
|
99
|
+
if (!colorizedName) return output;
|
|
100
|
+
|
|
101
|
+
return output.replace(new RegExp(`\\b${name}\\b`, 'g'), colorizedName);
|
|
102
|
+
}, input);
|
|
53
103
|
};
|
|
54
104
|
|
|
55
105
|
const copyFunction = (text: string | object | number) => {
|
|
@@ -88,6 +138,372 @@ const getLearnCardBundlePassword = async (prompt = 'Bundle password: '): Promise
|
|
|
88
138
|
}
|
|
89
139
|
};
|
|
90
140
|
|
|
141
|
+
const globalAssignmentPattern = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([\s\S]*?);?\s*$/;
|
|
142
|
+
|
|
143
|
+
const parseGlobalAssignment = (
|
|
144
|
+
source: string
|
|
145
|
+
): { name: string; initializer: string } | undefined => {
|
|
146
|
+
const match = source.match(globalAssignmentPattern);
|
|
147
|
+
|
|
148
|
+
if (!match) return;
|
|
149
|
+
|
|
150
|
+
const [, name, initializer] = match;
|
|
151
|
+
|
|
152
|
+
return { name, initializer };
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const getCurrentToken = (line: string): string => {
|
|
156
|
+
return line.match(/(?:[A-Za-z_$][\w$]*\.)*[A-Za-z_$][\w$]*\.?$/)?.[0] ?? '';
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const getCompletionBase = (token: string): string | undefined => {
|
|
160
|
+
const lastDotIndex = token.lastIndexOf('.');
|
|
161
|
+
|
|
162
|
+
if (lastDotIndex === -1) return;
|
|
163
|
+
|
|
164
|
+
return token.slice(0, lastDotIndex);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const getCompletionPrefix = (token: string): string => {
|
|
168
|
+
const lastDotIndex = token.lastIndexOf('.');
|
|
169
|
+
|
|
170
|
+
if (lastDotIndex === -1) return token;
|
|
171
|
+
|
|
172
|
+
return token.slice(lastDotIndex + 1);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const getOwnPropertyNames = (value: unknown): string[] => {
|
|
176
|
+
if (value === null || value === undefined) return [];
|
|
177
|
+
|
|
178
|
+
const names = new Set<string>();
|
|
179
|
+
|
|
180
|
+
let current: unknown = Object(value);
|
|
181
|
+
|
|
182
|
+
while (current && current !== Object.prototype) {
|
|
183
|
+
for (const name of Object.getOwnPropertyNames(current)) names.add(name);
|
|
184
|
+
|
|
185
|
+
current = Object.getPrototypeOf(current);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return [...names];
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const completeBunReplInput = (line: string): ReplCompletion => {
|
|
192
|
+
const token = getCurrentToken(line);
|
|
193
|
+
|
|
194
|
+
if (!token) return [replCommands, ''];
|
|
195
|
+
|
|
196
|
+
const base = getCompletionBase(token);
|
|
197
|
+
const prefix = getCompletionPrefix(token);
|
|
198
|
+
|
|
199
|
+
if (!base) {
|
|
200
|
+
const completions = [...replCommands, ...replGlobals].filter(name =>
|
|
201
|
+
name.startsWith(prefix)
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return [completions.length ? completions : replGlobals, token];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const target = globalThis.eval(base) as unknown;
|
|
209
|
+
const completions = getOwnPropertyNames(target)
|
|
210
|
+
.filter(name => identifierPattern.test(name))
|
|
211
|
+
.filter(name => name.startsWith(prefix))
|
|
212
|
+
.map(name => `${base}.${name}`);
|
|
213
|
+
|
|
214
|
+
return [completions, token];
|
|
215
|
+
} catch {
|
|
216
|
+
return [[], token];
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const evaluateBunReplInput = async (source: string): Promise<unknown> => {
|
|
221
|
+
try {
|
|
222
|
+
return await globalThis.eval(`(async () => (${source}))()`);
|
|
223
|
+
} catch {
|
|
224
|
+
const globalAssignment = parseGlobalAssignment(source);
|
|
225
|
+
|
|
226
|
+
if (globalAssignment) {
|
|
227
|
+
const value = await globalThis.eval(
|
|
228
|
+
`(async () => (${globalAssignment.initializer}))()`
|
|
229
|
+
);
|
|
230
|
+
(globalThis as Record<string, unknown>)[globalAssignment.name] = value;
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return await globalThis.eval(`(async () => { ${source} })()`);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const getSharedCompletion = (matches: string[]): string | undefined => {
|
|
239
|
+
if (!matches.length) return;
|
|
240
|
+
|
|
241
|
+
return matches.reduce((prefix, match) => {
|
|
242
|
+
let index = 0;
|
|
243
|
+
|
|
244
|
+
while (index < prefix.length && prefix[index] === match[index]) index += 1;
|
|
245
|
+
|
|
246
|
+
return prefix.slice(0, index);
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const renderBunReplLine = (
|
|
251
|
+
prompt: string,
|
|
252
|
+
line: string,
|
|
253
|
+
cursor: number,
|
|
254
|
+
colorize: (input: string) => string
|
|
255
|
+
): void => {
|
|
256
|
+
process.stdout.clearLine(0);
|
|
257
|
+
process.stdout.cursorTo(0);
|
|
258
|
+
process.stdout.write(prompt + colorize(line));
|
|
259
|
+
|
|
260
|
+
const trailingLength = line.length - cursor;
|
|
261
|
+
|
|
262
|
+
if (trailingLength > 0) process.stdout.write(`\x1b[${trailingLength}D`);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const startReadlineRepl = async (colorize?: (input: string) => string): Promise<void> => {
|
|
266
|
+
const rl = createInterface({
|
|
267
|
+
input: process.stdin,
|
|
268
|
+
output: process.stdout,
|
|
269
|
+
prompt: '> ',
|
|
270
|
+
completer: completeBunReplInput,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
rl.prompt();
|
|
274
|
+
|
|
275
|
+
for await (const line of rl) {
|
|
276
|
+
const source = line.trim();
|
|
277
|
+
|
|
278
|
+
if (source === '.exit') break;
|
|
279
|
+
|
|
280
|
+
if (!source) {
|
|
281
|
+
rl.prompt();
|
|
282
|
+
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const result = await evaluateBunReplInput(source);
|
|
288
|
+
|
|
289
|
+
if (result !== undefined) console.dir(result, { depth: 6 });
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error(error);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (colorize) process.stdout.write(colorize(''));
|
|
295
|
+
|
|
296
|
+
rl.prompt();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
rl.close();
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const startBunRepl = async (colorize: (input: string) => string): Promise<void> => {
|
|
303
|
+
if (!process.stdin.isTTY || !process.stdin.setRawMode) {
|
|
304
|
+
await startReadlineRepl(colorize);
|
|
305
|
+
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const prompt = '> ';
|
|
310
|
+
const history: string[] = [];
|
|
311
|
+
|
|
312
|
+
let line = '';
|
|
313
|
+
let cursor = 0;
|
|
314
|
+
let historyIndex = 0;
|
|
315
|
+
let draft = '';
|
|
316
|
+
let pending = Promise.resolve();
|
|
317
|
+
|
|
318
|
+
const resetHistory = (): void => {
|
|
319
|
+
historyIndex = history.length;
|
|
320
|
+
draft = '';
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const setLine = (nextLine: string): void => {
|
|
324
|
+
line = nextLine;
|
|
325
|
+
cursor = line.length;
|
|
326
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const insertText = (text: string): void => {
|
|
330
|
+
line = line.slice(0, cursor) + text + line.slice(cursor);
|
|
331
|
+
cursor += text.length;
|
|
332
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const applyCompletion = (): void => {
|
|
336
|
+
const linePrefix = line.slice(0, cursor);
|
|
337
|
+
const [matches, token] = completeBunReplInput(linePrefix);
|
|
338
|
+
const completion = matches.length === 1 ? matches[0] : getSharedCompletion(matches);
|
|
339
|
+
|
|
340
|
+
if (completion && completion !== token) {
|
|
341
|
+
line = line.slice(0, cursor - token.length) + completion + line.slice(cursor);
|
|
342
|
+
cursor += completion.length - token.length;
|
|
343
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
344
|
+
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (matches.length > 1) {
|
|
349
|
+
process.stdout.write(`\n${matches.join(' ')}\n`);
|
|
350
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const evaluateLine = async (): Promise<boolean> => {
|
|
355
|
+
process.stdout.write('\n');
|
|
356
|
+
|
|
357
|
+
const source = line.trim();
|
|
358
|
+
|
|
359
|
+
if (source === '.exit') return false;
|
|
360
|
+
|
|
361
|
+
if (source) {
|
|
362
|
+
history.push(source);
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
const result = await evaluateBunReplInput(source);
|
|
366
|
+
|
|
367
|
+
if (result !== undefined) console.dir(result, { depth: 6 });
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.error(error);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
line = '';
|
|
374
|
+
cursor = 0;
|
|
375
|
+
resetHistory();
|
|
376
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
377
|
+
|
|
378
|
+
return true;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const handleKey = async (input: string, key: ReplKey): Promise<boolean> => {
|
|
382
|
+
if (key.ctrl && key.name === 'c') return false;
|
|
383
|
+
|
|
384
|
+
if (key.name === 'return' || key.name === 'enter') return await evaluateLine();
|
|
385
|
+
|
|
386
|
+
if (key.ctrl && key.name === 'd' && !line) return false;
|
|
387
|
+
|
|
388
|
+
if (key.name === 'tab') {
|
|
389
|
+
applyCompletion();
|
|
390
|
+
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (key.name === 'backspace') {
|
|
395
|
+
if (cursor === 0) return true;
|
|
396
|
+
|
|
397
|
+
line = line.slice(0, cursor - 1) + line.slice(cursor);
|
|
398
|
+
cursor -= 1;
|
|
399
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
400
|
+
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (key.name === 'delete') {
|
|
405
|
+
if (cursor >= line.length) return true;
|
|
406
|
+
|
|
407
|
+
line = line.slice(0, cursor) + line.slice(cursor + 1);
|
|
408
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
409
|
+
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (key.name === 'left') {
|
|
414
|
+
if (cursor > 0) cursor -= 1;
|
|
415
|
+
|
|
416
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
417
|
+
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (key.name === 'right') {
|
|
422
|
+
if (cursor < line.length) cursor += 1;
|
|
423
|
+
|
|
424
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
425
|
+
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (key.name === 'home') {
|
|
430
|
+
cursor = 0;
|
|
431
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
432
|
+
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (key.name === 'end') {
|
|
437
|
+
cursor = line.length;
|
|
438
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
439
|
+
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (key.name === 'up') {
|
|
444
|
+
if (historyIndex === history.length) draft = line;
|
|
445
|
+
if (historyIndex > 0) historyIndex -= 1;
|
|
446
|
+
|
|
447
|
+
setLine(history[historyIndex] ?? draft);
|
|
448
|
+
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (key.name === 'down') {
|
|
453
|
+
if (historyIndex < history.length) historyIndex += 1;
|
|
454
|
+
|
|
455
|
+
setLine(historyIndex === history.length ? draft : history[historyIndex] ?? '');
|
|
456
|
+
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (input && !key.ctrl && input >= ' ') {
|
|
461
|
+
insertText(input);
|
|
462
|
+
resetHistory();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return true;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
await new Promise<void>(resolve => {
|
|
469
|
+
const onKeypress = (input: string, key: ReplKey): void => {
|
|
470
|
+
pending = pending.then(async () => {
|
|
471
|
+
const keepRunning = await handleKey(input, key);
|
|
472
|
+
|
|
473
|
+
if (!keepRunning) cleanup();
|
|
474
|
+
});
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
const cleanup = (): void => {
|
|
478
|
+
process.stdin.off('keypress', onKeypress);
|
|
479
|
+
process.stdin.setRawMode?.(false);
|
|
480
|
+
process.stdin.pause();
|
|
481
|
+
process.stdout.write('\n');
|
|
482
|
+
resolve();
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
emitKeypressEvents(process.stdin);
|
|
486
|
+
process.stdin.setRawMode?.(true);
|
|
487
|
+
process.stdin.resume();
|
|
488
|
+
|
|
489
|
+
console.log('Bun dev REPL ready. Tab completes globals/properties. Type .exit to quit.');
|
|
490
|
+
renderBunReplLine(prompt, line, cursor, colorize);
|
|
491
|
+
process.stdin.on('keypress', onKeypress);
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const startCliRepl = async (colorize: (input: string) => string): Promise<void> => {
|
|
496
|
+
if (!('Bun' in globalThis)) {
|
|
497
|
+
const repl = await import('pretty-repl');
|
|
498
|
+
|
|
499
|
+
repl.default.start({ colorize });
|
|
500
|
+
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
await startBunRepl(colorize);
|
|
505
|
+
};
|
|
506
|
+
|
|
91
507
|
program
|
|
92
508
|
.version(packageJson.version)
|
|
93
509
|
.argument('[seed]')
|
|
@@ -237,23 +653,6 @@ program
|
|
|
237
653
|
|
|
238
654
|
console.log('');
|
|
239
655
|
|
|
240
|
-
|
|
241
|
-
colorize: (input: string) => {
|
|
242
|
-
return input
|
|
243
|
-
.replace('emptyLearnCard', g.emptyLearnCard)
|
|
244
|
-
.replace('learnCardFromKey', g.learnCardFromKey)
|
|
245
|
-
.replace('initLearnCard', g.initLearnCard)
|
|
246
|
-
.replace('learnCard', g.learnCard)
|
|
247
|
-
.replace('seed', g.seed)
|
|
248
|
-
.replace('generateRandomSeed', g.generateRandomSeed)
|
|
249
|
-
.replace('copy', g.copy)
|
|
250
|
-
.replace('getLearnCardBundlePassword', g.getLearnCardBundlePassword)
|
|
251
|
-
.replace('exportLearnCardBundle', g.exportLearnCardBundle)
|
|
252
|
-
.replace('importLearnCardBundle', g.importLearnCardBundle)
|
|
253
|
-
.replace('createLearnCardBundle', g.createLearnCardBundle)
|
|
254
|
-
.replace('readLearnCardBundle', g.readLearnCardBundle)
|
|
255
|
-
.replace('restoreLearnCardFromBundle', g.restoreLearnCardFromBundle);
|
|
256
|
-
},
|
|
257
|
-
});
|
|
656
|
+
await startCliRepl(colorizeReplInput);
|
|
258
657
|
})
|
|
259
658
|
.parse(process.argv);
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Learning Economy Foundation <sdk@learningeconomy.io>
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|