@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 CHANGED
@@ -14860,10 +14860,11 @@ class ArcFunction {
14860
14860
  params: schema instanceof ArcObject ? schema : new ArcObject(schema)
14861
14861
  });
14862
14862
  }
14863
- withResult(schema) {
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
- result: schema instanceof ArcObject ? schema : new ArcObject(schema)
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 result() {
14916
- return this.data.result;
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
- result: this.data.result?.toJsonSchema?.() ?? null
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 || typeof methods !== "object")
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: element2.name, method: methodName, args }, () => method(...args));
15317
+ wrapped[methodName] = (...args) => onCall({ element: elementName, method: methodName, args }, () => method(...args));
15304
15318
  }
15305
- result[element2.name] = wrapped;
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
- result: null,
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
- return new ArcCommand({ ...this.data, results: schemas }, this.#fn);
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
- result: null,
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
- result: null,
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(schema) {
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
- result: schema instanceof ArcObject2 ? schema : new ArcObject2(schema)
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 result() {
22249
- return this.data.result;
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
- result: this.data.result?.toJsonSchema?.() ?? null
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 || typeof methods !== "object")
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: element2.name, method: methodName, args }, () => method(...args));
22674
+ wrapped[methodName] = (...args) => onCall({ element: elementName, method: methodName, args }, () => method(...args));
22637
22675
  }
22638
- result[element2.name] = wrapped;
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
- result: null,
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
- return new ArcCommand2({ ...this.data, results: schemas }, this.#fn);
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
- result: null,
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
- result: null,
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 platform3 = await startPlatformServer({
40792
- ws,
40793
- port,
40794
- manifest,
40795
- context: context2,
40796
- moduleAccess,
40797
- dbPath,
40798
- devMode
40799
- });
40800
- ok(`Server on http://localhost:${port}`);
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.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.7",
16
- "@arcote.tech/arc-ds": "^0.7.7",
17
- "@arcote.tech/arc-react": "^0.7.7",
18
- "@arcote.tech/arc-host": "^0.7.7",
19
- "@arcote.tech/arc-adapter-db-sqlite": "^0.7.7",
20
- "@arcote.tech/arc-adapter-db-postgres": "^0.7.7",
21
- "@arcote.tech/arc-otel": "^0.7.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.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",
@@ -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
- const platform = await startPlatformServer({
78
- ws,
79
- port,
80
- manifest,
81
- context,
82
- moduleAccess,
83
- dbPath,
84
- devMode,
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:${port}`);
109
+ ok(`Server on http://localhost:${actualPort}`);
88
110
  if (platform.contextHandler) {
89
111
  ok("Commands, queries, WebSocket — all on same port");
90
112
  }