@constructive-io/cli 8.0.3 → 8.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.
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const graphql_env_1 = require("@constructive-io/graphql-env");
4
- const logger_1 = require("@pgpmjs/logger");
5
4
  const graphql_server_1 = require("@constructive-io/graphql-server");
5
+ const logger_1 = require("@pgpmjs/logger");
6
6
  const pg_cache_1 = require("pg-cache");
7
7
  const log = new logger_1.Logger('server');
8
8
  const serverUsageText = `
@@ -19,7 +19,7 @@ Options:
19
19
  --simpleInflection Use simple inflection (default: true)
20
20
  --oppositeBaseNames Use opposite base names (default: false)
21
21
  --postgis Enable PostGIS extension (default: true)
22
- --servicesApi Enable Services API (default: true)
22
+ --scopedRouting Enable scoped routing (default: true)
23
23
  --cwd <directory> Working directory (default: current directory)
24
24
 
25
25
  Examples:
@@ -54,8 +54,8 @@ const questions = [
54
54
  useDefault: true
55
55
  },
56
56
  {
57
- name: 'servicesApi',
58
- message: 'Enable Services API?',
57
+ name: 'scopedRouting',
58
+ message: 'Enable scoped routing?',
59
59
  type: 'confirm',
60
60
  required: false,
61
61
  default: true,
@@ -65,7 +65,7 @@ const questions = [
65
65
  name: 'origin',
66
66
  message: 'CORS origin (exact URL or *)',
67
67
  type: 'text',
68
- required: false,
68
+ required: false
69
69
  // no default to avoid accidentally opening up CORS; pass explicitly or via env
70
70
  },
71
71
  {
@@ -106,7 +106,7 @@ exports.default = async (argv, prompter, _options) => {
106
106
  selectedDb = database;
107
107
  log.info(`📌 Using database: "${selectedDb}"`);
108
108
  }
109
- const { oppositeBaseNames, port, postgis, simpleInflection, servicesApi, origin } = await prompter.prompt(argv, questions);
109
+ const { oppositeBaseNames, port, postgis, simpleInflection, scopedRouting, origin } = await prompter.prompt(argv, questions);
110
110
  // Warn when passing CORS override via CLI, especially in production
111
111
  if (origin && origin.trim().length) {
112
112
  const env = (process.env.NODE_ENV || 'development').toLowerCase();
@@ -122,7 +122,7 @@ exports.default = async (argv, prompter, _options) => {
122
122
  let selectedSchemas = [];
123
123
  let authRole;
124
124
  let roleName;
125
- if (!servicesApi) {
125
+ if (!scopedRouting) {
126
126
  const db = await (0, pg_cache_1.getPgPool)({ database: selectedDb });
127
127
  const result = await db.query(`
128
128
  SELECT nspname
@@ -172,8 +172,8 @@ exports.default = async (argv, prompter, _options) => {
172
172
  postgis
173
173
  },
174
174
  api: {
175
- enableServicesApi: servicesApi,
176
- ...(servicesApi === false && { exposedSchemas: selectedSchemas, authRole, roleName })
175
+ enableScopedRouting: scopedRouting,
176
+ ...(scopedRouting === false && { exposedSchemas: selectedSchemas, authRole, roleName })
177
177
  },
178
178
  server: {
179
179
  port,
@@ -186,7 +186,7 @@ exports.default = async (argv, prompter, _options) => {
186
186
  }
187
187
  // Debug: Log API routing configuration
188
188
  const apiOpts = options.api || {};
189
- log.debug(`📡 API Routing: isPublic=${apiOpts.isPublic}, enableServicesApi=${apiOpts.enableServicesApi}`);
189
+ log.debug(`📡 API Routing: isPublic=${apiOpts.isPublic}, enableScopedRouting=${apiOpts.enableScopedRouting}`);
190
190
  if (apiOpts.isPublic === false) {
191
191
  log.debug(` Header-based routing enabled (X-Api-Name, X-Database-Id, X-Meta-Schema)`);
192
192
  }
package/commands.js CHANGED
@@ -11,7 +11,6 @@ const codegen_1 = __importDefault(require("./commands/codegen"));
11
11
  const context_1 = __importDefault(require("./commands/context"));
12
12
  const execute_1 = __importDefault(require("./commands/execute"));
13
13
  const explorer_1 = __importDefault(require("./commands/explorer"));
14
- const jobs_1 = __importDefault(require("./commands/jobs"));
15
14
  const server_1 = __importDefault(require("./commands/server"));
16
15
  const utils_2 = require("./utils");
17
16
  const createCommandMap = () => {
@@ -19,7 +18,6 @@ const createCommandMap = () => {
19
18
  server: server_1.default,
20
19
  explorer: explorer_1.default,
21
20
  codegen: codegen_1.default,
22
- jobs: jobs_1.default,
23
21
  context: context_1.default,
24
22
  auth: auth_1.default,
25
23
  execute: execute_1.default,
@@ -1,6 +1,6 @@
1
1
  import { getEnvOptions } from '@constructive-io/graphql-env';
2
- import { Logger } from '@pgpmjs/logger';
3
2
  import { GraphQLServer as server } from '@constructive-io/graphql-server';
3
+ import { Logger } from '@pgpmjs/logger';
4
4
  import { getPgPool } from 'pg-cache';
5
5
  const log = new Logger('server');
6
6
  const serverUsageText = `
@@ -17,7 +17,7 @@ Options:
17
17
  --simpleInflection Use simple inflection (default: true)
18
18
  --oppositeBaseNames Use opposite base names (default: false)
19
19
  --postgis Enable PostGIS extension (default: true)
20
- --servicesApi Enable Services API (default: true)
20
+ --scopedRouting Enable scoped routing (default: true)
21
21
  --cwd <directory> Working directory (default: current directory)
22
22
 
23
23
  Examples:
@@ -52,8 +52,8 @@ const questions = [
52
52
  useDefault: true
53
53
  },
54
54
  {
55
- name: 'servicesApi',
56
- message: 'Enable Services API?',
55
+ name: 'scopedRouting',
56
+ message: 'Enable scoped routing?',
57
57
  type: 'confirm',
58
58
  required: false,
59
59
  default: true,
@@ -63,7 +63,7 @@ const questions = [
63
63
  name: 'origin',
64
64
  message: 'CORS origin (exact URL or *)',
65
65
  type: 'text',
66
- required: false,
66
+ required: false
67
67
  // no default to avoid accidentally opening up CORS; pass explicitly or via env
68
68
  },
69
69
  {
@@ -104,7 +104,7 @@ export default async (argv, prompter, _options) => {
104
104
  selectedDb = database;
105
105
  log.info(`📌 Using database: "${selectedDb}"`);
106
106
  }
107
- const { oppositeBaseNames, port, postgis, simpleInflection, servicesApi, origin } = await prompter.prompt(argv, questions);
107
+ const { oppositeBaseNames, port, postgis, simpleInflection, scopedRouting, origin } = await prompter.prompt(argv, questions);
108
108
  // Warn when passing CORS override via CLI, especially in production
109
109
  if (origin && origin.trim().length) {
110
110
  const env = (process.env.NODE_ENV || 'development').toLowerCase();
@@ -120,7 +120,7 @@ export default async (argv, prompter, _options) => {
120
120
  let selectedSchemas = [];
121
121
  let authRole;
122
122
  let roleName;
123
- if (!servicesApi) {
123
+ if (!scopedRouting) {
124
124
  const db = await getPgPool({ database: selectedDb });
125
125
  const result = await db.query(`
126
126
  SELECT nspname
@@ -170,8 +170,8 @@ export default async (argv, prompter, _options) => {
170
170
  postgis
171
171
  },
172
172
  api: {
173
- enableServicesApi: servicesApi,
174
- ...(servicesApi === false && { exposedSchemas: selectedSchemas, authRole, roleName })
173
+ enableScopedRouting: scopedRouting,
174
+ ...(scopedRouting === false && { exposedSchemas: selectedSchemas, authRole, roleName })
175
175
  },
176
176
  server: {
177
177
  port,
@@ -184,7 +184,7 @@ export default async (argv, prompter, _options) => {
184
184
  }
185
185
  // Debug: Log API routing configuration
186
186
  const apiOpts = options.api || {};
187
- log.debug(`📡 API Routing: isPublic=${apiOpts.isPublic}, enableServicesApi=${apiOpts.enableServicesApi}`);
187
+ log.debug(`📡 API Routing: isPublic=${apiOpts.isPublic}, enableScopedRouting=${apiOpts.enableScopedRouting}`);
188
188
  if (apiOpts.isPublic === false) {
189
189
  log.debug(` Header-based routing enabled (X-Api-Name, X-Database-Id, X-Meta-Schema)`);
190
190
  }
package/esm/commands.js CHANGED
@@ -5,7 +5,6 @@ import codegen from './commands/codegen';
5
5
  import context from './commands/context';
6
6
  import execute from './commands/execute';
7
7
  import explorer from './commands/explorer';
8
- import jobs from './commands/jobs';
9
8
  import server from './commands/server';
10
9
  import { usageText } from './utils';
11
10
  const createCommandMap = () => {
@@ -13,7 +12,6 @@ const createCommandMap = () => {
13
12
  server,
14
13
  explorer,
15
14
  codegen,
16
- jobs,
17
15
  context,
18
16
  auth,
19
17
  execute,
@@ -11,9 +11,6 @@ export const usageText = `
11
11
  Code Generation:
12
12
  codegen Generate TypeScript types and SDK from GraphQL schema
13
13
 
14
- Jobs:
15
- jobs up Start combined server (jobs runtime)
16
-
17
14
  Execution Engine:
18
15
  context Manage contexts (create, list, use, current, delete)
19
16
  auth Manage authentication (set-token, status, logout)
@@ -34,7 +31,6 @@ export const usageText = `
34
31
  cnc explorer Launch GraphiQL explorer
35
32
  cnc codegen --schema schema.graphql Generate types from schema
36
33
  cnc codegen --schema-enabled --output ./schemas Export schema SDL
37
- cnc jobs up Start combined server (jobs runtime)
38
34
 
39
35
  # Execution Engine
40
36
  cnc context create my-api --endpoint https://api.example.com/graphql
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/cli",
3
- "version": "8.0.3",
3
+ "version": "8.1.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Constructive CLI",
6
6
  "main": "index.js",
@@ -45,24 +45,23 @@
45
45
  "ts-node": "^10.9.2"
46
46
  },
47
47
  "dependencies": {
48
- "@constructive-io/graphql-codegen": "^5.0.3",
49
- "@constructive-io/graphql-env": "^3.16.3",
50
- "@constructive-io/graphql-explorer": "^5.0.3",
51
- "@constructive-io/graphql-server": "^5.0.3",
52
- "@constructive-io/graphql-types": "^3.15.0",
53
- "@constructive-io/knative-job-service": "^3.0.3",
48
+ "@constructive-io/graphql-codegen": "^5.1.0",
49
+ "@constructive-io/graphql-env": "^3.17.0",
50
+ "@constructive-io/graphql-explorer": "^5.0.5",
51
+ "@constructive-io/graphql-server": "^5.1.0",
52
+ "@constructive-io/graphql-types": "^3.16.0",
54
53
  "@inquirerer/utils": "^3.3.9",
55
- "@pgpmjs/core": "^7.2.1",
54
+ "@pgpmjs/core": "^7.3.0",
56
55
  "@pgpmjs/logger": "^2.14.0",
57
- "@pgpmjs/server-utils": "^3.15.3",
58
- "@pgpmjs/types": "^2.35.1",
56
+ "@pgpmjs/server-utils": "^3.15.4",
57
+ "@pgpmjs/types": "^2.36.0",
59
58
  "appstash": "^0.7.0",
60
59
  "find-and-require-package-json": "^0.9.1",
61
60
  "inquirerer": "^4.9.1",
62
61
  "js-yaml": "^4.1.0",
63
- "pg-cache": "^3.15.2",
62
+ "pg-cache": "^3.15.3",
64
63
  "pg-env": "^1.18.1",
65
- "pgpm": "^5.0.3",
64
+ "pgpm": "^5.1.0",
66
65
  "shelljs": "^0.10.0",
67
66
  "yanse": "^0.2.1"
68
67
  },
@@ -77,5 +76,5 @@
77
76
  "postgres",
78
77
  "graphile"
79
78
  ],
80
- "gitHead": "51c3e15e9d509154d0a8102bb18f6e39cfa4e0c6"
79
+ "gitHead": "39db6d576bce7bc1de8123849f237b1695779d88"
81
80
  }
@@ -1 +1 @@
1
- export declare const usageText = "\n Usage: cnc <command> [options]\n constructive <command> [options]\n\n Constructive CLI - API Server and Development Tools\n\n GraphQL Server:\n server Start a GraphQL server\n explorer Launch GraphiQL explorer interface\n\n Code Generation:\n codegen Generate TypeScript types and SDK from GraphQL schema\n\n Jobs:\n jobs up Start combined server (jobs runtime)\n\n Execution Engine:\n context Manage contexts (create, list, use, current, delete)\n auth Manage authentication (set-token, status, logout)\n execute Execute GraphQL queries against configured endpoints\n\n Global Options:\n -h, --help Display this help information\n -v, --version Display version information\n --cwd <directory> Working directory (default: current directory)\n\n Individual Command Help:\n cnc <command> --help Display detailed help for specific command\n cnc <command> -h Display detailed help for specific command\n\n Examples:\n cnc server Start GraphQL server\n cnc server --port 8080 Start server on custom port\n cnc explorer Launch GraphiQL explorer\n cnc codegen --schema schema.graphql Generate types from schema\n cnc codegen --schema-enabled --output ./schemas Export schema SDL\n cnc jobs up Start combined server (jobs runtime)\n\n # Execution Engine\n cnc context create my-api --endpoint https://api.example.com/graphql\n cnc auth set-token\n cnc execute --query 'query { __typename }'\n\n Database Operations:\n For database migrations, packages, and deployment, use pgpm:\n https://pgpm.io\n ";
1
+ export declare const usageText = "\n Usage: cnc <command> [options]\n constructive <command> [options]\n\n Constructive CLI - API Server and Development Tools\n\n GraphQL Server:\n server Start a GraphQL server\n explorer Launch GraphiQL explorer interface\n\n Code Generation:\n codegen Generate TypeScript types and SDK from GraphQL schema\n\n Execution Engine:\n context Manage contexts (create, list, use, current, delete)\n auth Manage authentication (set-token, status, logout)\n execute Execute GraphQL queries against configured endpoints\n\n Global Options:\n -h, --help Display this help information\n -v, --version Display version information\n --cwd <directory> Working directory (default: current directory)\n\n Individual Command Help:\n cnc <command> --help Display detailed help for specific command\n cnc <command> -h Display detailed help for specific command\n\n Examples:\n cnc server Start GraphQL server\n cnc server --port 8080 Start server on custom port\n cnc explorer Launch GraphiQL explorer\n cnc codegen --schema schema.graphql Generate types from schema\n cnc codegen --schema-enabled --output ./schemas Export schema SDL\n\n # Execution Engine\n cnc context create my-api --endpoint https://api.example.com/graphql\n cnc auth set-token\n cnc execute --query 'query { __typename }'\n\n Database Operations:\n For database migrations, packages, and deployment, use pgpm:\n https://pgpm.io\n ";
package/utils/display.js CHANGED
@@ -14,9 +14,6 @@ exports.usageText = `
14
14
  Code Generation:
15
15
  codegen Generate TypeScript types and SDK from GraphQL schema
16
16
 
17
- Jobs:
18
- jobs up Start combined server (jobs runtime)
19
-
20
17
  Execution Engine:
21
18
  context Manage contexts (create, list, use, current, delete)
22
19
  auth Manage authentication (set-token, status, logout)
@@ -37,7 +34,6 @@ exports.usageText = `
37
34
  cnc explorer Launch GraphiQL explorer
38
35
  cnc codegen --schema schema.graphql Generate types from schema
39
36
  cnc codegen --schema-enabled --output ./schemas Export schema SDL
40
- cnc jobs up Start combined server (jobs runtime)
41
37
 
42
38
  # Execution Engine
43
39
  cnc context create my-api --endpoint https://api.example.com/graphql
@@ -1,3 +0,0 @@
1
- import { CLIOptions, Inquirerer } from 'inquirerer';
2
- declare const _default: (argv: Partial<Record<string, any>>, prompter: Inquirerer, _options: CLIOptions) => Promise<void>;
3
- export default _default;
package/commands/jobs.js DELETED
@@ -1,167 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const fs_1 = require("fs");
4
- const path_1 = require("path");
5
- const knative_job_service_1 = require("@constructive-io/knative-job-service");
6
- const inquirerer_1 = require("inquirerer");
7
- const jobsUsageText = `
8
- Constructive Jobs:
9
-
10
- cnc jobs <subcommand> [OPTIONS]
11
-
12
- Start or manage Constructive jobs services.
13
-
14
- Subcommands:
15
- up Start jobs runtime (jobs + functions)
16
-
17
- Options:
18
- --help, -h Show this help message
19
- --cwd <directory> Working directory (default: current directory)
20
- --with-jobs-server Enable jobs server (default: disabled; flag-only)
21
- --functions <list> Comma-separated functions, optionally with ports (e.g. "fn=8080")
22
-
23
- Examples:
24
- cnc jobs up
25
- cnc jobs up --cwd /path/to/constructive
26
- cnc jobs up --with-jobs-server --functions send-email,send-verification-link=8082
27
- `;
28
- const questions = [
29
- {
30
- name: 'withJobsServer',
31
- alias: 'with-jobs-server',
32
- message: 'Enable jobs server?',
33
- type: 'confirm',
34
- required: false,
35
- default: false,
36
- useDefault: true
37
- }
38
- ];
39
- const ensureCwd = (cwd) => {
40
- const resolved = (0, path_1.resolve)(cwd);
41
- if (!(0, fs_1.existsSync)(resolved)) {
42
- throw new Error(`Working directory does not exist: ${resolved}`);
43
- }
44
- process.chdir(resolved);
45
- return resolved;
46
- };
47
- const parseFunctionsArg = (value) => {
48
- if (value === undefined)
49
- return undefined;
50
- const values = Array.isArray(value) ? value : [value];
51
- const tokens = [];
52
- for (const value of values) {
53
- if (value === true) {
54
- tokens.push('all');
55
- continue;
56
- }
57
- if (value === false || value === undefined || value === null)
58
- continue;
59
- const raw = String(value);
60
- raw
61
- .split(',')
62
- .map((part) => part.trim())
63
- .filter(Boolean)
64
- .forEach((part) => tokens.push(part));
65
- }
66
- if (!tokens.length) {
67
- return { mode: 'list', names: [], ports: {} };
68
- }
69
- const hasAll = tokens.some((token) => {
70
- const normalized = token.trim().toLowerCase();
71
- return normalized === 'all' || normalized === '*';
72
- });
73
- if (hasAll) {
74
- if (tokens.length > 1) {
75
- throw new Error('Use "all" without other function names.');
76
- }
77
- return { mode: 'all', names: [], ports: {} };
78
- }
79
- const names = [];
80
- const ports = {};
81
- for (const token of tokens) {
82
- const trimmed = token.trim();
83
- if (!trimmed)
84
- continue;
85
- const separatorIndex = trimmed.search(/[:=]/);
86
- if (separatorIndex === -1) {
87
- names.push(trimmed);
88
- continue;
89
- }
90
- const name = trimmed.slice(0, separatorIndex).trim();
91
- const portText = trimmed.slice(separatorIndex + 1).trim();
92
- if (!name) {
93
- throw new Error(`Missing function name in "${token}".`);
94
- }
95
- if (!portText) {
96
- throw new Error(`Missing port for function "${name}".`);
97
- }
98
- const port = Number(portText);
99
- if (!Number.isFinite(port) || port <= 0) {
100
- throw new Error(`Invalid port "${portText}" for function "${name}".`);
101
- }
102
- names.push(name);
103
- ports[name] = port;
104
- }
105
- const uniqueNames = [];
106
- const seen = new Set();
107
- for (const name of names) {
108
- if (seen.has(name))
109
- continue;
110
- seen.add(name);
111
- uniqueNames.push(name);
112
- }
113
- return { mode: 'list', names: uniqueNames, ports };
114
- };
115
- const buildKnativeJobsSvcOptions = (args) => {
116
- const parsedFunctions = parseFunctionsArg(args.functions);
117
- let functions;
118
- if (parsedFunctions) {
119
- if (parsedFunctions.mode === 'all') {
120
- functions = { enabled: true };
121
- }
122
- else if (parsedFunctions.names.length) {
123
- const services = parsedFunctions.names.map((name) => ({
124
- name: name,
125
- port: parsedFunctions.ports[name]
126
- }));
127
- functions = { enabled: true, services };
128
- }
129
- else {
130
- functions = undefined;
131
- }
132
- }
133
- return {
134
- jobs: { enabled: args.withJobsServer === true },
135
- functions
136
- };
137
- };
138
- exports.default = async (argv, prompter, _options) => {
139
- if (argv.help || argv.h) {
140
- console.log(jobsUsageText);
141
- process.exit(0);
142
- }
143
- const { first: subcommand, newArgv } = (0, inquirerer_1.extractFirst)(argv);
144
- const args = newArgv;
145
- if (!subcommand) {
146
- console.log(jobsUsageText);
147
- await (0, inquirerer_1.cliExitWithError)('No subcommand provided. Use "up".');
148
- return;
149
- }
150
- switch (subcommand) {
151
- case 'up': {
152
- try {
153
- ensureCwd(args.cwd || process.cwd());
154
- const promptAnswers = await prompter.prompt(args, questions);
155
- const server = new knative_job_service_1.KnativeJobsSvc(buildKnativeJobsSvcOptions(promptAnswers));
156
- await server.start();
157
- }
158
- catch (error) {
159
- await (0, inquirerer_1.cliExitWithError)(`Failed to start jobs runtime: ${error.message}`);
160
- }
161
- break;
162
- }
163
- default:
164
- console.log(jobsUsageText);
165
- await (0, inquirerer_1.cliExitWithError)(`Unknown subcommand: ${subcommand}. Use "up".`);
166
- }
167
- };
@@ -1,165 +0,0 @@
1
- import { existsSync } from 'fs';
2
- import { resolve } from 'path';
3
- import { KnativeJobsSvc } from '@constructive-io/knative-job-service';
4
- import { cliExitWithError, extractFirst } from 'inquirerer';
5
- const jobsUsageText = `
6
- Constructive Jobs:
7
-
8
- cnc jobs <subcommand> [OPTIONS]
9
-
10
- Start or manage Constructive jobs services.
11
-
12
- Subcommands:
13
- up Start jobs runtime (jobs + functions)
14
-
15
- Options:
16
- --help, -h Show this help message
17
- --cwd <directory> Working directory (default: current directory)
18
- --with-jobs-server Enable jobs server (default: disabled; flag-only)
19
- --functions <list> Comma-separated functions, optionally with ports (e.g. "fn=8080")
20
-
21
- Examples:
22
- cnc jobs up
23
- cnc jobs up --cwd /path/to/constructive
24
- cnc jobs up --with-jobs-server --functions send-email,send-verification-link=8082
25
- `;
26
- const questions = [
27
- {
28
- name: 'withJobsServer',
29
- alias: 'with-jobs-server',
30
- message: 'Enable jobs server?',
31
- type: 'confirm',
32
- required: false,
33
- default: false,
34
- useDefault: true
35
- }
36
- ];
37
- const ensureCwd = (cwd) => {
38
- const resolved = resolve(cwd);
39
- if (!existsSync(resolved)) {
40
- throw new Error(`Working directory does not exist: ${resolved}`);
41
- }
42
- process.chdir(resolved);
43
- return resolved;
44
- };
45
- const parseFunctionsArg = (value) => {
46
- if (value === undefined)
47
- return undefined;
48
- const values = Array.isArray(value) ? value : [value];
49
- const tokens = [];
50
- for (const value of values) {
51
- if (value === true) {
52
- tokens.push('all');
53
- continue;
54
- }
55
- if (value === false || value === undefined || value === null)
56
- continue;
57
- const raw = String(value);
58
- raw
59
- .split(',')
60
- .map((part) => part.trim())
61
- .filter(Boolean)
62
- .forEach((part) => tokens.push(part));
63
- }
64
- if (!tokens.length) {
65
- return { mode: 'list', names: [], ports: {} };
66
- }
67
- const hasAll = tokens.some((token) => {
68
- const normalized = token.trim().toLowerCase();
69
- return normalized === 'all' || normalized === '*';
70
- });
71
- if (hasAll) {
72
- if (tokens.length > 1) {
73
- throw new Error('Use "all" without other function names.');
74
- }
75
- return { mode: 'all', names: [], ports: {} };
76
- }
77
- const names = [];
78
- const ports = {};
79
- for (const token of tokens) {
80
- const trimmed = token.trim();
81
- if (!trimmed)
82
- continue;
83
- const separatorIndex = trimmed.search(/[:=]/);
84
- if (separatorIndex === -1) {
85
- names.push(trimmed);
86
- continue;
87
- }
88
- const name = trimmed.slice(0, separatorIndex).trim();
89
- const portText = trimmed.slice(separatorIndex + 1).trim();
90
- if (!name) {
91
- throw new Error(`Missing function name in "${token}".`);
92
- }
93
- if (!portText) {
94
- throw new Error(`Missing port for function "${name}".`);
95
- }
96
- const port = Number(portText);
97
- if (!Number.isFinite(port) || port <= 0) {
98
- throw new Error(`Invalid port "${portText}" for function "${name}".`);
99
- }
100
- names.push(name);
101
- ports[name] = port;
102
- }
103
- const uniqueNames = [];
104
- const seen = new Set();
105
- for (const name of names) {
106
- if (seen.has(name))
107
- continue;
108
- seen.add(name);
109
- uniqueNames.push(name);
110
- }
111
- return { mode: 'list', names: uniqueNames, ports };
112
- };
113
- const buildKnativeJobsSvcOptions = (args) => {
114
- const parsedFunctions = parseFunctionsArg(args.functions);
115
- let functions;
116
- if (parsedFunctions) {
117
- if (parsedFunctions.mode === 'all') {
118
- functions = { enabled: true };
119
- }
120
- else if (parsedFunctions.names.length) {
121
- const services = parsedFunctions.names.map((name) => ({
122
- name: name,
123
- port: parsedFunctions.ports[name]
124
- }));
125
- functions = { enabled: true, services };
126
- }
127
- else {
128
- functions = undefined;
129
- }
130
- }
131
- return {
132
- jobs: { enabled: args.withJobsServer === true },
133
- functions
134
- };
135
- };
136
- export default async (argv, prompter, _options) => {
137
- if (argv.help || argv.h) {
138
- console.log(jobsUsageText);
139
- process.exit(0);
140
- }
141
- const { first: subcommand, newArgv } = extractFirst(argv);
142
- const args = newArgv;
143
- if (!subcommand) {
144
- console.log(jobsUsageText);
145
- await cliExitWithError('No subcommand provided. Use "up".');
146
- return;
147
- }
148
- switch (subcommand) {
149
- case 'up': {
150
- try {
151
- ensureCwd(args.cwd || process.cwd());
152
- const promptAnswers = await prompter.prompt(args, questions);
153
- const server = new KnativeJobsSvc(buildKnativeJobsSvcOptions(promptAnswers));
154
- await server.start();
155
- }
156
- catch (error) {
157
- await cliExitWithError(`Failed to start jobs runtime: ${error.message}`);
158
- }
159
- break;
160
- }
161
- default:
162
- console.log(jobsUsageText);
163
- await cliExitWithError(`Unknown subcommand: ${subcommand}. Use "up".`);
164
- }
165
- };