@basis-theory-labs/cli 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/commands/reactors/update.js +113 -28
- package/dist/files.d.ts +2 -1
- package/dist/files.js +31 -1
- package/dist/runtime/state.js +6 -4
- package/oclif.manifest.json +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ $ npm install -g @basis-theory-labs/cli
|
|
|
19
19
|
$ bt COMMAND
|
|
20
20
|
running command...
|
|
21
21
|
$ bt (--version)
|
|
22
|
-
@basis-theory-labs/cli/4.
|
|
22
|
+
@basis-theory-labs/cli/4.1.0 linux-x64 node-v22.23.1
|
|
23
23
|
$ bt --help [COMMAND]
|
|
24
24
|
USAGE
|
|
25
25
|
$ bt COMMAND
|
|
@@ -62,7 +62,7 @@ EXAMPLES
|
|
|
62
62
|
$ bt applications
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
_See code: [dist/commands/applications/index.ts](https://github.com/Basis-Theory-Labs/basistheory-cli/blob/v4.
|
|
65
|
+
_See code: [dist/commands/applications/index.ts](https://github.com/Basis-Theory-Labs/basistheory-cli/blob/v4.1.0/dist/commands/applications/index.ts)_
|
|
66
66
|
|
|
67
67
|
## `bt applications create`
|
|
68
68
|
|
|
@@ -151,7 +151,7 @@ EXAMPLES
|
|
|
151
151
|
$ bt proxies
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
_See code: [dist/commands/proxies/index.ts](https://github.com/Basis-Theory-Labs/basistheory-cli/blob/v4.
|
|
154
|
+
_See code: [dist/commands/proxies/index.ts](https://github.com/Basis-Theory-Labs/basistheory-cli/blob/v4.1.0/dist/commands/proxies/index.ts)_
|
|
155
155
|
|
|
156
156
|
## `bt proxies create`
|
|
157
157
|
|
|
@@ -378,7 +378,7 @@ EXAMPLES
|
|
|
378
378
|
$ bt reactors
|
|
379
379
|
```
|
|
380
380
|
|
|
381
|
-
_See code: [dist/commands/reactors/index.ts](https://github.com/Basis-Theory-Labs/basistheory-cli/blob/v4.
|
|
381
|
+
_See code: [dist/commands/reactors/index.ts](https://github.com/Basis-Theory-Labs/basistheory-cli/blob/v4.1.0/dist/commands/reactors/index.ts)_
|
|
382
382
|
|
|
383
383
|
## `bt reactors create`
|
|
384
384
|
|
|
@@ -495,7 +495,7 @@ FLAGS
|
|
|
495
495
|
-l, --logs Start logs server after update
|
|
496
496
|
-n, --name=<value> name of the Reactor
|
|
497
497
|
-r, --code=<value> path to JavaScript file containing the Reactor code
|
|
498
|
-
-w, --watch Watch for changes in
|
|
498
|
+
-w, --watch Watch for changes in supplied code, configuration, and runtime package files
|
|
499
499
|
-x, --management-key=<value> (required) management key used for connecting with the reactor / proxy
|
|
500
500
|
--[no-]async execute Reactor invocations asynchronously (node22 only)
|
|
501
501
|
--image=<option> runtime image (node-bt|node22)
|
|
@@ -522,6 +522,11 @@ EXAMPLES
|
|
|
522
522
|
|
|
523
523
|
$ bt reactors update <reactor-id> --code ./reactor.js --image node22
|
|
524
524
|
|
|
525
|
+
Watch a node22 reactor for code, configuration, and dependency changes
|
|
526
|
+
|
|
527
|
+
$ bt reactors update <reactor-id> --code ./reactor.js --configuration ./config.env --package-json ./package.json \
|
|
528
|
+
--image node22 --watch
|
|
529
|
+
|
|
525
530
|
Update a reactor with node22 and all runtime options
|
|
526
531
|
|
|
527
532
|
$ bt reactors update <reactor-id> --name "My Reactor" --code ./reactor.js --configuration ./config.env --image \
|
|
@@ -13,13 +13,15 @@ class Update extends base_1.BaseCommand {
|
|
|
13
13
|
var _a, _b, _c, _d;
|
|
14
14
|
const { bt, args: { id }, flags, } = await this.parse(Update);
|
|
15
15
|
const { name, code, 'application-id': applicationId, configuration, image, 'package-json': packageJson, timeout, 'warm-concurrency': warmConcurrency, resources, permissions, 'no-wait': noWait, async: runtimeAsync, watch, logs, } = flags;
|
|
16
|
-
const existingReactor =
|
|
16
|
+
const existingReactor = watch ||
|
|
17
|
+
(0, runtime_1.hasReactorRuntimeFlags)(flags) ||
|
|
17
18
|
(applicationId !== undefined && image === undefined)
|
|
18
19
|
? await (0, management_1.getReactor)(bt, id)
|
|
19
20
|
: undefined;
|
|
20
21
|
const effectiveImage = image !== null && image !== void 0 ? image : (_a = existingReactor === null || existingReactor === void 0 ? void 0 : existingReactor.runtime) === null || _a === void 0 ? void 0 : _a.image;
|
|
21
22
|
const effectiveRuntimeAsync = (_c = runtimeAsync !== null && runtimeAsync !== void 0 ? runtimeAsync : (_b = existingReactor === null || existingReactor === void 0 ? void 0 : existingReactor.runtime) === null || _b === void 0 ? void 0 : _b.async) !== null && _c !== void 0 ? _c : false;
|
|
22
23
|
const effectiveTimeout = timeout !== null && timeout !== void 0 ? timeout : (_d = existingReactor === null || existingReactor === void 0 ? void 0 : existingReactor.runtime) === null || _d === void 0 ? void 0 : _d.timeout;
|
|
24
|
+
const isConfigurableRuntime = !(0, runtime_2.isLegacyRuntimeImage)(effectiveImage);
|
|
23
25
|
(0, runtime_1.validateReactorRuntimeFlags)(flags, effectiveImage);
|
|
24
26
|
(0, runtime_1.validateReactorApplicationId)(applicationId, effectiveImage);
|
|
25
27
|
if (timeout !== undefined || runtimeAsync !== undefined) {
|
|
@@ -42,36 +44,110 @@ class Update extends base_1.BaseCommand {
|
|
|
42
44
|
runtime,
|
|
43
45
|
});
|
|
44
46
|
await (0, management_1.patchReactor)(bt, id, model);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
await (0, logs_1.showReactorLogs)(bt, id);
|
|
53
|
-
}
|
|
54
|
-
if (watch && isConfigurableRuntime) {
|
|
55
|
-
this.warn(`--watch is not supported for configurable runtimes (${runtime_2.CONFIGURABLE_RUNTIME_IMAGES.join(' | ')}). Skipping watch.`);
|
|
56
|
-
}
|
|
57
|
-
else if (watch) {
|
|
58
|
-
const entries = Object.entries({
|
|
59
|
-
code,
|
|
60
|
-
configuration,
|
|
61
|
-
}).filter(([, value]) => Boolean(value));
|
|
62
|
-
const files = entries.reduce((arr, [, file]) => [...arr, file], []);
|
|
47
|
+
const watchedEntries = Object.entries({
|
|
48
|
+
code,
|
|
49
|
+
configuration,
|
|
50
|
+
...(isConfigurableRuntime ? { packageJson } : {}),
|
|
51
|
+
}).filter(([, value]) => Boolean(value));
|
|
52
|
+
const startWatching = (initialUpdateCompleted) => {
|
|
53
|
+
const files = watchedEntries.reduce((arr, [, file]) => [...arr, file], []);
|
|
63
54
|
if (files.length) {
|
|
64
55
|
this.log(`Watching files for changes: ${files.join(', ')} `);
|
|
65
56
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await (0, management_1.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
if (isConfigurableRuntime) {
|
|
58
|
+
const waitUntilReactorIsAvailable = async () => {
|
|
59
|
+
await initialUpdateCompleted;
|
|
60
|
+
const currentReactor = await (0, management_1.getReactor)(bt, id);
|
|
61
|
+
if (!(0, runtime_2.needsPolling)(currentReactor.state)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await (0, runtime_2.waitForResourceState)(bt, 'reactor', id, currentReactor.state);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const latestReactor = await (0, management_1.getReactor)(bt, id);
|
|
69
|
+
if (latestReactor.state !== 'failed' &&
|
|
70
|
+
latestReactor.state !== 'outdated') {
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const pushLatestChanges = (0, files_1.createCoalescingHandler)(async (markLatestStateCaptured) => {
|
|
76
|
+
await waitUntilReactorIsAvailable();
|
|
77
|
+
core_1.ux.action.start('Pushing latest watched changes');
|
|
78
|
+
try {
|
|
79
|
+
markLatestStateCaptured();
|
|
80
|
+
await (0, management_1.patchReactor)(bt, id, (0, utils_1.createModelFromFlags)({
|
|
81
|
+
code,
|
|
82
|
+
configuration,
|
|
83
|
+
runtime: (0, runtime_2.buildReactorRuntime)({ packageJson }),
|
|
84
|
+
}));
|
|
85
|
+
core_1.ux.action.stop('✅\t');
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
core_1.ux.action.stop('failed ❌');
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
const updatedReactor = await (0, management_1.getReactor)(bt, id);
|
|
92
|
+
await (0, runtime_2.waitForResourceState)(bt, 'reactor', id, updatedReactor.state);
|
|
93
|
+
}, (error) => {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
this.warn(`Failed to push watched Reactor changes: ${message}`);
|
|
73
96
|
});
|
|
74
|
-
|
|
97
|
+
watchedEntries.forEach(([, file]) => {
|
|
98
|
+
(0, files_1.watchForChanges)(file, () => {
|
|
99
|
+
this.log(`Detected change in ${file}. Queuing latest files`);
|
|
100
|
+
return pushLatestChanges();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
watchedEntries.forEach(([prop, file]) => {
|
|
106
|
+
(0, files_1.watchForChanges)(file, async () => {
|
|
107
|
+
core_1.ux.action.start(`Detected change in ${file}. Pushing changes`);
|
|
108
|
+
await (0, management_1.patchReactor)(bt, id, (0, utils_1.createModelFromFlags)({
|
|
109
|
+
[prop]: file,
|
|
110
|
+
}));
|
|
111
|
+
core_1.ux.action.stop('✅\t');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
let initialUpdateError;
|
|
117
|
+
const completeInitialUpdate = async () => {
|
|
118
|
+
if (!noWait) {
|
|
119
|
+
try {
|
|
120
|
+
const initialState = isConfigurableRuntime
|
|
121
|
+
? 'updating'
|
|
122
|
+
: (await (0, management_1.getReactor)(bt, id)).state;
|
|
123
|
+
await (0, runtime_2.waitForResourceState)(bt, 'reactor', id, initialState);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (!watch || !isConfigurableRuntime) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
initialUpdateError = error;
|
|
130
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
131
|
+
this.warn(`Initial Reactor update failed: ${message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (initialUpdateError) {
|
|
135
|
+
this.log('Watch remains active. Save a watched file to retry the latest snapshot.');
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
this.log('Reactor updated successfully!');
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const initialUpdateCompleted = completeInitialUpdate();
|
|
142
|
+
if (watch && isConfigurableRuntime) {
|
|
143
|
+
startWatching(initialUpdateCompleted);
|
|
144
|
+
}
|
|
145
|
+
await initialUpdateCompleted;
|
|
146
|
+
if (logs && !initialUpdateError) {
|
|
147
|
+
await (0, logs_1.showReactorLogs)(bt, id);
|
|
148
|
+
}
|
|
149
|
+
if (watch && !isConfigurableRuntime) {
|
|
150
|
+
startWatching();
|
|
75
151
|
}
|
|
76
152
|
}
|
|
77
153
|
}
|
|
@@ -86,6 +162,15 @@ Update.examples = [
|
|
|
86
162
|
description: 'Update a reactor with node22 runtime',
|
|
87
163
|
command: '<%= config.bin %> <%= command.id %> <reactor-id> --code ./reactor.js --image node22',
|
|
88
164
|
},
|
|
165
|
+
{
|
|
166
|
+
description: 'Watch a node22 reactor for code, configuration, and dependency changes',
|
|
167
|
+
command: '<%= config.bin %> <%= command.id %> <reactor-id> ' +
|
|
168
|
+
'--code ./reactor.js ' +
|
|
169
|
+
'--configuration ./config.env ' +
|
|
170
|
+
'--package-json ./package.json ' +
|
|
171
|
+
'--image node22 ' +
|
|
172
|
+
'--watch',
|
|
173
|
+
},
|
|
89
174
|
{
|
|
90
175
|
description: 'Update a reactor with node22 and all runtime options',
|
|
91
176
|
command: '<%= config.bin %> <%= command.id %> <reactor-id> ' +
|
|
@@ -106,7 +191,7 @@ Update.flags = {
|
|
|
106
191
|
...utils_1.REACTOR_FLAGS,
|
|
107
192
|
watch: core_1.Flags.boolean({
|
|
108
193
|
char: 'w',
|
|
109
|
-
description: 'Watch for changes in
|
|
194
|
+
description: 'Watch for changes in supplied code, configuration, and runtime package files',
|
|
110
195
|
default: false,
|
|
111
196
|
required: false,
|
|
112
197
|
}),
|
package/dist/files.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
declare const readFileContents: (filePath: string) => string;
|
|
3
|
+
declare const createCoalescingHandler: (handler: (markLatestStateCaptured: () => void) => Promise<void>, onError: (error: unknown) => void) => (() => Promise<void>);
|
|
3
4
|
declare const watchForChanges: (filePath: string, handler: (contents: string) => unknown) => fs.FSWatcher;
|
|
4
|
-
export { readFileContents, watchForChanges };
|
|
5
|
+
export { createCoalescingHandler, readFileContents, watchForChanges };
|
package/dist/files.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.watchForChanges = exports.readFileContents = void 0;
|
|
3
|
+
exports.watchForChanges = exports.readFileContents = exports.createCoalescingHandler = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("fs"));
|
|
6
6
|
const lodash_debounce_1 = tslib_1.__importDefault(require("lodash.debounce"));
|
|
@@ -8,6 +8,36 @@ const path = tslib_1.__importStar(require("path"));
|
|
|
8
8
|
const readFileContents = (filePath) => fs.readFileSync(path.resolve(process.cwd(), filePath)).toString();
|
|
9
9
|
exports.readFileContents = readFileContents;
|
|
10
10
|
const FILE_WATCH_DEBOUNCE_DELAY = 1000;
|
|
11
|
+
const createCoalescingHandler = (handler, onError) => {
|
|
12
|
+
let pending = false;
|
|
13
|
+
let running;
|
|
14
|
+
const markLatestStateCaptured = () => {
|
|
15
|
+
pending = false;
|
|
16
|
+
};
|
|
17
|
+
/* eslint-disable no-await-in-loop */
|
|
18
|
+
const drain = async () => {
|
|
19
|
+
while (pending) {
|
|
20
|
+
pending = false;
|
|
21
|
+
try {
|
|
22
|
+
await handler(markLatestStateCaptured);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
onError(error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/* eslint-enable no-await-in-loop */
|
|
30
|
+
return () => {
|
|
31
|
+
pending = true;
|
|
32
|
+
if (!running) {
|
|
33
|
+
running = drain().finally(() => {
|
|
34
|
+
running = undefined;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return running;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
exports.createCoalescingHandler = createCoalescingHandler;
|
|
11
41
|
const watchForChanges = (filePath, handler) => {
|
|
12
42
|
let lastContents = '';
|
|
13
43
|
const debounced = (0, lodash_debounce_1.default)(() => {
|
package/dist/runtime/state.js
CHANGED
|
@@ -64,9 +64,11 @@ const waitForResourceState = async (bt, resourceType, id, initialState) => {
|
|
|
64
64
|
}
|
|
65
65
|
const deadline = Date.now() + POLL_TIMEOUT;
|
|
66
66
|
const startTime = Date.now();
|
|
67
|
-
let currentState;
|
|
67
|
+
let currentState = initialState;
|
|
68
68
|
const timer = createTimer(startTime, () => currentState);
|
|
69
|
-
core_1.ux.action.start('Status'
|
|
69
|
+
core_1.ux.action.start('Status', currentState
|
|
70
|
+
? `${currentState} (${formatElapsedTime(startTime)})`
|
|
71
|
+
: undefined);
|
|
70
72
|
/* eslint-disable no-await-in-loop */
|
|
71
73
|
try {
|
|
72
74
|
while (Date.now() < deadline) {
|
|
@@ -83,11 +85,11 @@ const waitForResourceState = async (bt, resourceType, id, initialState) => {
|
|
|
83
85
|
currentState = resource.state;
|
|
84
86
|
core_1.ux.action.status = `${currentState} (${formatElapsedTime(startTime)})`;
|
|
85
87
|
if (currentState === 'active') {
|
|
86
|
-
core_1.ux.action.stop(`ready (${formatElapsedTime(startTime)})
|
|
88
|
+
core_1.ux.action.stop(`ready (${formatElapsedTime(startTime)}) ✅`);
|
|
87
89
|
return;
|
|
88
90
|
}
|
|
89
91
|
if (currentState === 'failed' || currentState === 'outdated') {
|
|
90
|
-
core_1.ux.action.stop(`failed (${formatElapsedTime(startTime)})
|
|
92
|
+
core_1.ux.action.stop(`failed (${formatElapsedTime(startTime)}) ❌`);
|
|
91
93
|
throw new Error(formatErrorMessage(resource, resourceType));
|
|
92
94
|
}
|
|
93
95
|
await new Promise((resolve) => {
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.1.0",
|
|
3
3
|
"commands": {
|
|
4
4
|
"applications:create": {
|
|
5
5
|
"id": "applications:create",
|
|
@@ -992,6 +992,10 @@
|
|
|
992
992
|
"description": "Update a reactor with node22 runtime",
|
|
993
993
|
"command": "<%= config.bin %> <%= command.id %> <reactor-id> --code ./reactor.js --image node22"
|
|
994
994
|
},
|
|
995
|
+
{
|
|
996
|
+
"description": "Watch a node22 reactor for code, configuration, and dependency changes",
|
|
997
|
+
"command": "<%= config.bin %> <%= command.id %> <reactor-id> --code ./reactor.js --configuration ./config.env --package-json ./package.json --image node22 --watch"
|
|
998
|
+
},
|
|
995
999
|
{
|
|
996
1000
|
"description": "Update a reactor with node22 and all runtime options",
|
|
997
1001
|
"command": "<%= config.bin %> <%= command.id %> <reactor-id> --name \"My Reactor\" --code ./reactor.js --configuration ./config.env --image node22 --async --timeout 10 --warm-concurrency 0 --resources standard --package-json ./package.json --permissions token:read --permissions token:create"
|
|
@@ -1102,7 +1106,7 @@
|
|
|
1102
1106
|
"name": "watch",
|
|
1103
1107
|
"type": "boolean",
|
|
1104
1108
|
"char": "w",
|
|
1105
|
-
"description": "Watch for changes in
|
|
1109
|
+
"description": "Watch for changes in supplied code, configuration, and runtime package files",
|
|
1106
1110
|
"required": false,
|
|
1107
1111
|
"allowNo": false
|
|
1108
1112
|
},
|