@arcote.tech/arc-cli 0.7.7 → 0.7.8
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/index.js +108 -38
- package/package.json +9 -9
- package/src/platform/startup.ts +32 -10
package/dist/index.js
CHANGED
|
@@ -14860,10 +14860,11 @@ class ArcFunction {
|
|
|
14860
14860
|
params: schema instanceof ArcObject ? schema : new ArcObject(schema)
|
|
14861
14861
|
});
|
|
14862
14862
|
}
|
|
14863
|
-
withResult(
|
|
14863
|
+
withResult(...schemas) {
|
|
14864
|
+
const results = schemas.map((s) => s instanceof ArcObject ? s : new ArcObject(s));
|
|
14864
14865
|
return new ArcFunction({
|
|
14865
14866
|
...this.data,
|
|
14866
|
-
|
|
14867
|
+
results
|
|
14867
14868
|
});
|
|
14868
14869
|
}
|
|
14869
14870
|
query(elements) {
|
|
@@ -14891,6 +14892,12 @@ class ArcFunction {
|
|
|
14891
14892
|
description: desc
|
|
14892
14893
|
});
|
|
14893
14894
|
}
|
|
14895
|
+
private() {
|
|
14896
|
+
return new ArcFunction({
|
|
14897
|
+
...this.data,
|
|
14898
|
+
isPrivate: true
|
|
14899
|
+
});
|
|
14900
|
+
}
|
|
14894
14901
|
handle(handler) {
|
|
14895
14902
|
return new ArcFunction({
|
|
14896
14903
|
...this.data,
|
|
@@ -14912,8 +14919,8 @@ class ArcFunction {
|
|
|
14912
14919
|
get params() {
|
|
14913
14920
|
return this.data.params;
|
|
14914
14921
|
}
|
|
14915
|
-
get
|
|
14916
|
-
return this.data.
|
|
14922
|
+
get results() {
|
|
14923
|
+
return this.data.results;
|
|
14917
14924
|
}
|
|
14918
14925
|
async verifyProtections(tokens) {
|
|
14919
14926
|
if (!this.data.protections || this.data.protections.length === 0) {
|
|
@@ -14937,7 +14944,7 @@ class ArcFunction {
|
|
|
14937
14944
|
toJsonSchema() {
|
|
14938
14945
|
return {
|
|
14939
14946
|
params: this.data.params?.toJsonSchema?.() ?? null,
|
|
14940
|
-
|
|
14947
|
+
results: this.data.results.map((r2) => r2.toJsonSchema())
|
|
14941
14948
|
};
|
|
14942
14949
|
}
|
|
14943
14950
|
}
|
|
@@ -15294,19 +15301,26 @@ function buildContextAccessor(context2, adapters, contextMethod, onCall) {
|
|
|
15294
15301
|
if (typeof ctxFn !== "function")
|
|
15295
15302
|
continue;
|
|
15296
15303
|
const methods = ctxFn.call(element2, adapters);
|
|
15297
|
-
if (!methods
|
|
15304
|
+
if (!methods)
|
|
15305
|
+
continue;
|
|
15306
|
+
const elementName = element2.name;
|
|
15307
|
+
if (typeof methods === "function") {
|
|
15308
|
+
result[elementName] = (...args) => onCall({ element: elementName, method: "", args }, () => methods(...args));
|
|
15309
|
+
continue;
|
|
15310
|
+
}
|
|
15311
|
+
if (typeof methods !== "object")
|
|
15298
15312
|
continue;
|
|
15299
15313
|
const wrapped = {};
|
|
15300
15314
|
for (const [methodName, method] of Object.entries(methods)) {
|
|
15301
15315
|
if (typeof method !== "function")
|
|
15302
15316
|
continue;
|
|
15303
|
-
wrapped[methodName] = (...args) => onCall({ element:
|
|
15317
|
+
wrapped[methodName] = (...args) => onCall({ element: elementName, method: methodName, args }, () => method(...args));
|
|
15304
15318
|
}
|
|
15305
|
-
result[
|
|
15319
|
+
result[elementName] = wrapped;
|
|
15306
15320
|
}
|
|
15307
15321
|
return result;
|
|
15308
15322
|
}
|
|
15309
|
-
function executeDescriptor(descriptor, context2, adapters, contextMethod) {
|
|
15323
|
+
function executeDescriptor(descriptor, context2, adapters, contextMethod, options) {
|
|
15310
15324
|
const element2 = context2.get(descriptor.element);
|
|
15311
15325
|
if (!element2) {
|
|
15312
15326
|
throw new Error(`Element '${descriptor.element}' not found in context`);
|
|
@@ -15316,10 +15330,19 @@ function executeDescriptor(descriptor, context2, adapters, contextMethod) {
|
|
|
15316
15330
|
throw new Error(`Element '${descriptor.element}' has no ${contextMethod}`);
|
|
15317
15331
|
}
|
|
15318
15332
|
const methods = ctxFn.call(element2, adapters);
|
|
15333
|
+
if (typeof methods === "function") {
|
|
15334
|
+
if (options?.fromWire && methods.__isPrivate) {
|
|
15335
|
+
throw new Error(`Element "${descriptor.element}" is private and not callable from a client.`);
|
|
15336
|
+
}
|
|
15337
|
+
return methods(...descriptor.args);
|
|
15338
|
+
}
|
|
15319
15339
|
const method = methods?.[descriptor.method];
|
|
15320
15340
|
if (typeof method !== "function") {
|
|
15321
15341
|
throw new Error(`Method '${descriptor.method}' not found on '${descriptor.element}'`);
|
|
15322
15342
|
}
|
|
15343
|
+
if (options?.fromWire && method.__isPrivate) {
|
|
15344
|
+
throw new Error(`Method "${descriptor.element}.${descriptor.method}" is private and not callable from a client.`);
|
|
15345
|
+
}
|
|
15323
15346
|
return method(...descriptor.args);
|
|
15324
15347
|
}
|
|
15325
15348
|
|
|
@@ -19202,7 +19225,7 @@ var init_dist = __esm(() => {
|
|
|
19202
19225
|
this.data = data;
|
|
19203
19226
|
this.#fn = fn ?? new ArcFunction({
|
|
19204
19227
|
params: data.params,
|
|
19205
|
-
|
|
19228
|
+
results: data.results,
|
|
19206
19229
|
queryElements: data.queryElements,
|
|
19207
19230
|
mutationElements: data.mutationElements,
|
|
19208
19231
|
protections: data.protections || [],
|
|
@@ -19233,7 +19256,8 @@ var init_dist = __esm(() => {
|
|
|
19233
19256
|
}, newFn);
|
|
19234
19257
|
}
|
|
19235
19258
|
withResult(...schemas) {
|
|
19236
|
-
|
|
19259
|
+
const newFn = this.#fn.withResult(...schemas);
|
|
19260
|
+
return new ArcCommand({ ...this.data, results: newFn.data.results }, newFn);
|
|
19237
19261
|
}
|
|
19238
19262
|
handle(handler) {
|
|
19239
19263
|
const newFn = new ArcFunction({
|
|
@@ -19324,7 +19348,7 @@ var init_dist = __esm(() => {
|
|
|
19324
19348
|
this.data = data;
|
|
19325
19349
|
this.#fn = fn ?? new ArcFunction({
|
|
19326
19350
|
params: null,
|
|
19327
|
-
|
|
19351
|
+
results: [],
|
|
19328
19352
|
queryElements: data.queryElements,
|
|
19329
19353
|
mutationElements: data.mutationElements,
|
|
19330
19354
|
protections: [],
|
|
@@ -19430,7 +19454,7 @@ var init_dist = __esm(() => {
|
|
|
19430
19454
|
this.data = data;
|
|
19431
19455
|
this.#fn = fn ?? new ArcFunction({
|
|
19432
19456
|
params: null,
|
|
19433
|
-
|
|
19457
|
+
results: [],
|
|
19434
19458
|
queryElements: data.queryElements,
|
|
19435
19459
|
mutationElements: data.mutationElements,
|
|
19436
19460
|
protections: data.protections || [],
|
|
@@ -22193,10 +22217,11 @@ class ArcFunction2 {
|
|
|
22193
22217
|
params: schema instanceof ArcObject2 ? schema : new ArcObject2(schema)
|
|
22194
22218
|
});
|
|
22195
22219
|
}
|
|
22196
|
-
withResult(
|
|
22220
|
+
withResult(...schemas) {
|
|
22221
|
+
const results = schemas.map((s) => s instanceof ArcObject2 ? s : new ArcObject2(s));
|
|
22197
22222
|
return new ArcFunction2({
|
|
22198
22223
|
...this.data,
|
|
22199
|
-
|
|
22224
|
+
results
|
|
22200
22225
|
});
|
|
22201
22226
|
}
|
|
22202
22227
|
query(elements) {
|
|
@@ -22224,6 +22249,12 @@ class ArcFunction2 {
|
|
|
22224
22249
|
description: desc
|
|
22225
22250
|
});
|
|
22226
22251
|
}
|
|
22252
|
+
private() {
|
|
22253
|
+
return new ArcFunction2({
|
|
22254
|
+
...this.data,
|
|
22255
|
+
isPrivate: true
|
|
22256
|
+
});
|
|
22257
|
+
}
|
|
22227
22258
|
handle(handler) {
|
|
22228
22259
|
return new ArcFunction2({
|
|
22229
22260
|
...this.data,
|
|
@@ -22245,8 +22276,8 @@ class ArcFunction2 {
|
|
|
22245
22276
|
get params() {
|
|
22246
22277
|
return this.data.params;
|
|
22247
22278
|
}
|
|
22248
|
-
get
|
|
22249
|
-
return this.data.
|
|
22279
|
+
get results() {
|
|
22280
|
+
return this.data.results;
|
|
22250
22281
|
}
|
|
22251
22282
|
async verifyProtections(tokens) {
|
|
22252
22283
|
if (!this.data.protections || this.data.protections.length === 0) {
|
|
@@ -22270,7 +22301,7 @@ class ArcFunction2 {
|
|
|
22270
22301
|
toJsonSchema() {
|
|
22271
22302
|
return {
|
|
22272
22303
|
params: this.data.params?.toJsonSchema?.() ?? null,
|
|
22273
|
-
|
|
22304
|
+
results: this.data.results.map((r2) => r2.toJsonSchema())
|
|
22274
22305
|
};
|
|
22275
22306
|
}
|
|
22276
22307
|
}
|
|
@@ -22627,19 +22658,26 @@ function buildContextAccessor2(context2, adapters, contextMethod, onCall) {
|
|
|
22627
22658
|
if (typeof ctxFn !== "function")
|
|
22628
22659
|
continue;
|
|
22629
22660
|
const methods = ctxFn.call(element2, adapters);
|
|
22630
|
-
if (!methods
|
|
22661
|
+
if (!methods)
|
|
22662
|
+
continue;
|
|
22663
|
+
const elementName = element2.name;
|
|
22664
|
+
if (typeof methods === "function") {
|
|
22665
|
+
result[elementName] = (...args) => onCall({ element: elementName, method: "", args }, () => methods(...args));
|
|
22666
|
+
continue;
|
|
22667
|
+
}
|
|
22668
|
+
if (typeof methods !== "object")
|
|
22631
22669
|
continue;
|
|
22632
22670
|
const wrapped = {};
|
|
22633
22671
|
for (const [methodName, method] of Object.entries(methods)) {
|
|
22634
22672
|
if (typeof method !== "function")
|
|
22635
22673
|
continue;
|
|
22636
|
-
wrapped[methodName] = (...args) => onCall({ element:
|
|
22674
|
+
wrapped[methodName] = (...args) => onCall({ element: elementName, method: methodName, args }, () => method(...args));
|
|
22637
22675
|
}
|
|
22638
|
-
result[
|
|
22676
|
+
result[elementName] = wrapped;
|
|
22639
22677
|
}
|
|
22640
22678
|
return result;
|
|
22641
22679
|
}
|
|
22642
|
-
function executeDescriptor2(descriptor, context2, adapters, contextMethod) {
|
|
22680
|
+
function executeDescriptor2(descriptor, context2, adapters, contextMethod, options) {
|
|
22643
22681
|
const element2 = context2.get(descriptor.element);
|
|
22644
22682
|
if (!element2) {
|
|
22645
22683
|
throw new Error(`Element '${descriptor.element}' not found in context`);
|
|
@@ -22649,10 +22687,19 @@ function executeDescriptor2(descriptor, context2, adapters, contextMethod) {
|
|
|
22649
22687
|
throw new Error(`Element '${descriptor.element}' has no ${contextMethod}`);
|
|
22650
22688
|
}
|
|
22651
22689
|
const methods = ctxFn.call(element2, adapters);
|
|
22690
|
+
if (typeof methods === "function") {
|
|
22691
|
+
if (options?.fromWire && methods.__isPrivate) {
|
|
22692
|
+
throw new Error(`Element "${descriptor.element}" is private and not callable from a client.`);
|
|
22693
|
+
}
|
|
22694
|
+
return methods(...descriptor.args);
|
|
22695
|
+
}
|
|
22652
22696
|
const method = methods?.[descriptor.method];
|
|
22653
22697
|
if (typeof method !== "function") {
|
|
22654
22698
|
throw new Error(`Method '${descriptor.method}' not found on '${descriptor.element}'`);
|
|
22655
22699
|
}
|
|
22700
|
+
if (options?.fromWire && method.__isPrivate) {
|
|
22701
|
+
throw new Error(`Method "${descriptor.element}.${descriptor.method}" is private and not callable from a client.`);
|
|
22702
|
+
}
|
|
22656
22703
|
return method(...descriptor.args);
|
|
22657
22704
|
}
|
|
22658
22705
|
|
|
@@ -24970,7 +25017,7 @@ var init_dist2 = __esm(() => {
|
|
|
24970
25017
|
this.data = data;
|
|
24971
25018
|
this.#fn = fn ?? new ArcFunction2({
|
|
24972
25019
|
params: data.params,
|
|
24973
|
-
|
|
25020
|
+
results: data.results,
|
|
24974
25021
|
queryElements: data.queryElements,
|
|
24975
25022
|
mutationElements: data.mutationElements,
|
|
24976
25023
|
protections: data.protections || [],
|
|
@@ -25001,7 +25048,8 @@ var init_dist2 = __esm(() => {
|
|
|
25001
25048
|
}, newFn);
|
|
25002
25049
|
}
|
|
25003
25050
|
withResult(...schemas) {
|
|
25004
|
-
|
|
25051
|
+
const newFn = this.#fn.withResult(...schemas);
|
|
25052
|
+
return new ArcCommand2({ ...this.data, results: newFn.data.results }, newFn);
|
|
25005
25053
|
}
|
|
25006
25054
|
handle(handler) {
|
|
25007
25055
|
const newFn = new ArcFunction2({
|
|
@@ -25092,7 +25140,7 @@ var init_dist2 = __esm(() => {
|
|
|
25092
25140
|
this.data = data;
|
|
25093
25141
|
this.#fn = fn ?? new ArcFunction2({
|
|
25094
25142
|
params: null,
|
|
25095
|
-
|
|
25143
|
+
results: [],
|
|
25096
25144
|
queryElements: data.queryElements,
|
|
25097
25145
|
mutationElements: data.mutationElements,
|
|
25098
25146
|
protections: [],
|
|
@@ -25198,7 +25246,7 @@ var init_dist2 = __esm(() => {
|
|
|
25198
25246
|
this.data = data;
|
|
25199
25247
|
this.#fn = fn ?? new ArcFunction2({
|
|
25200
25248
|
params: null,
|
|
25201
|
-
|
|
25249
|
+
results: [],
|
|
25202
25250
|
queryElements: data.queryElements,
|
|
25203
25251
|
mutationElements: data.mutationElements,
|
|
25204
25252
|
protections: data.protections || [],
|
|
@@ -39069,7 +39117,7 @@ class ContextHandler {
|
|
|
39069
39117
|
console.log(`[ARC:Seed] Seeded ${data.length} row(s) into ${tableName}`);
|
|
39070
39118
|
}
|
|
39071
39119
|
}
|
|
39072
|
-
async executeCommand(commandName, params, rawToken) {
|
|
39120
|
+
async executeCommand(commandName, params, rawToken, options) {
|
|
39073
39121
|
const includePayloads = this.telemetry?.shouldIncludePayloads() ?? false;
|
|
39074
39122
|
const baseAttrs = {
|
|
39075
39123
|
"rpc.system": "arc",
|
|
@@ -39094,6 +39142,9 @@ class ContextHandler {
|
|
|
39094
39142
|
if (!command) {
|
|
39095
39143
|
throw new Error(`Command '${commandName}' not found`);
|
|
39096
39144
|
}
|
|
39145
|
+
if (!options?.internal && command.__isPrivate) {
|
|
39146
|
+
throw new Error(`Command '${commandName}' is private and not callable from a client.`);
|
|
39147
|
+
}
|
|
39097
39148
|
try {
|
|
39098
39149
|
return await command(params);
|
|
39099
39150
|
} catch (error) {
|
|
@@ -39701,7 +39752,7 @@ class CronScheduler {
|
|
|
39701
39752
|
console.log(`[ARC:Cron] ${commandName}: executing for ${instances.length} instance(s)...`);
|
|
39702
39753
|
for (const instance of instances) {
|
|
39703
39754
|
try {
|
|
39704
|
-
await this.contextHandler.executeCommand(commandName, { _id: instance._id }, null);
|
|
39755
|
+
await this.contextHandler.executeCommand(commandName, { _id: instance._id }, null, { internal: true });
|
|
39705
39756
|
} catch (error) {
|
|
39706
39757
|
console.error(`[ARC:Cron] ${commandName} failed for instance ${instance._id}:`, error);
|
|
39707
39758
|
}
|
|
@@ -40788,16 +40839,35 @@ async function startPlatform(opts) {
|
|
|
40788
40839
|
} else {
|
|
40789
40840
|
log2("No context \u2014 server endpoints skipped");
|
|
40790
40841
|
}
|
|
40791
|
-
const
|
|
40792
|
-
|
|
40793
|
-
|
|
40794
|
-
|
|
40795
|
-
|
|
40796
|
-
|
|
40797
|
-
|
|
40798
|
-
|
|
40799
|
-
|
|
40800
|
-
|
|
40842
|
+
const MAX_PORT_ATTEMPTS = devMode ? 20 : 1;
|
|
40843
|
+
let platform3 = null;
|
|
40844
|
+
let actualPort = port;
|
|
40845
|
+
for (let i = 0;i < MAX_PORT_ATTEMPTS; i++) {
|
|
40846
|
+
try {
|
|
40847
|
+
platform3 = await startPlatformServer({
|
|
40848
|
+
ws,
|
|
40849
|
+
port: actualPort,
|
|
40850
|
+
manifest,
|
|
40851
|
+
context: context2,
|
|
40852
|
+
moduleAccess,
|
|
40853
|
+
dbPath,
|
|
40854
|
+
devMode
|
|
40855
|
+
});
|
|
40856
|
+
break;
|
|
40857
|
+
} catch (e2) {
|
|
40858
|
+
const msg = e2.message || "";
|
|
40859
|
+
const inUse = msg.includes("EADDRINUSE") || msg.includes("address already in use") || msg.includes("in use");
|
|
40860
|
+
if (!inUse || i === MAX_PORT_ATTEMPTS - 1)
|
|
40861
|
+
throw e2;
|
|
40862
|
+
log2(`Port ${actualPort} in use, trying ${actualPort + 1}...`);
|
|
40863
|
+
actualPort++;
|
|
40864
|
+
}
|
|
40865
|
+
}
|
|
40866
|
+
if (!platform3) {
|
|
40867
|
+
err(`Failed to bind a free port starting from ${port}`);
|
|
40868
|
+
process.exit(1);
|
|
40869
|
+
}
|
|
40870
|
+
ok(`Server on http://localhost:${actualPort}`);
|
|
40801
40871
|
if (platform3.contextHandler) {
|
|
40802
40872
|
ok("Commands, queries, WebSocket \u2014 all on same port");
|
|
40803
40873
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
4
4
|
"description": "CLI tool for Arc framework",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
"build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform --external '@opentelemetry/*' && chmod +x dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@arcote.tech/arc": "^0.7.
|
|
16
|
-
"@arcote.tech/arc-ds": "^0.7.
|
|
17
|
-
"@arcote.tech/arc-react": "^0.7.
|
|
18
|
-
"@arcote.tech/arc-host": "^0.7.
|
|
19
|
-
"@arcote.tech/arc-adapter-db-sqlite": "^0.7.
|
|
20
|
-
"@arcote.tech/arc-adapter-db-postgres": "^0.7.
|
|
21
|
-
"@arcote.tech/arc-otel": "^0.7.
|
|
15
|
+
"@arcote.tech/arc": "^0.7.8",
|
|
16
|
+
"@arcote.tech/arc-ds": "^0.7.8",
|
|
17
|
+
"@arcote.tech/arc-react": "^0.7.8",
|
|
18
|
+
"@arcote.tech/arc-host": "^0.7.8",
|
|
19
|
+
"@arcote.tech/arc-adapter-db-sqlite": "^0.7.8",
|
|
20
|
+
"@arcote.tech/arc-adapter-db-postgres": "^0.7.8",
|
|
21
|
+
"@arcote.tech/arc-otel": "^0.7.8",
|
|
22
22
|
"@opentelemetry/api": "^1.9.0",
|
|
23
23
|
"@opentelemetry/api-logs": "^0.57.0",
|
|
24
24
|
"@opentelemetry/core": "^1.30.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
32
32
|
"@opentelemetry/sdk-trace-node": "^1.30.0",
|
|
33
33
|
"@opentelemetry/semantic-conventions": "^1.27.0",
|
|
34
|
-
"@arcote.tech/platform": "^0.7.
|
|
34
|
+
"@arcote.tech/platform": "^0.7.8",
|
|
35
35
|
"@clack/prompts": "^0.9.0",
|
|
36
36
|
"commander": "^11.1.0",
|
|
37
37
|
"chokidar": "^3.5.3",
|
package/src/platform/startup.ts
CHANGED
|
@@ -74,17 +74,39 @@ export async function startPlatform(
|
|
|
74
74
|
|
|
75
75
|
// 3. Start the platform server. Cache headers + SSE behaviour are
|
|
76
76
|
// controlled inside the server by `devMode`; we just pass the flag.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
// In dev mode, if the port is busy, try incrementing up to 20 times.
|
|
78
|
+
const MAX_PORT_ATTEMPTS = devMode ? 20 : 1;
|
|
79
|
+
let platform: PlatformServer | null = null;
|
|
80
|
+
let actualPort = port;
|
|
81
|
+
for (let i = 0; i < MAX_PORT_ATTEMPTS; i++) {
|
|
82
|
+
try {
|
|
83
|
+
platform = await startPlatformServer({
|
|
84
|
+
ws,
|
|
85
|
+
port: actualPort,
|
|
86
|
+
manifest,
|
|
87
|
+
context,
|
|
88
|
+
moduleAccess,
|
|
89
|
+
dbPath,
|
|
90
|
+
devMode,
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
} catch (e) {
|
|
94
|
+
const msg = (e as Error).message || "";
|
|
95
|
+
const inUse =
|
|
96
|
+
msg.includes("EADDRINUSE") ||
|
|
97
|
+
msg.includes("address already in use") ||
|
|
98
|
+
msg.includes("in use");
|
|
99
|
+
if (!inUse || i === MAX_PORT_ATTEMPTS - 1) throw e;
|
|
100
|
+
log(`Port ${actualPort} in use, trying ${actualPort + 1}...`);
|
|
101
|
+
actualPort++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!platform) {
|
|
105
|
+
err(`Failed to bind a free port starting from ${port}`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
86
108
|
|
|
87
|
-
ok(`Server on http://localhost:${
|
|
109
|
+
ok(`Server on http://localhost:${actualPort}`);
|
|
88
110
|
if (platform.contextHandler) {
|
|
89
111
|
ok("Commands, queries, WebSocket — all on same port");
|
|
90
112
|
}
|