@gapi/gcli 1.8.230 → 1.8.232

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 CHANGED
@@ -205,6 +205,316 @@ options:
205
205
  external: []
206
206
  ```
207
207
 
208
+ ### MCP Server Functions
209
+
210
+ A function can be provisioned as an **MCP Server** in front of a **federation graph** by setting `mcp: true`. The MCP lambda has **no source code** — it is fully described by config: the selected `mcpGraph` (a federation lambda in the same project) is resolved to its internal router URL and **introspected** (single source of truth, no schema file), and the curated `mcpOperations` become MCP tools. Use the MCP runtime image and `newdeploy` + `minScale: 1` (MCP holds long-lived streamable-HTTP sessions).
211
+
212
+ ```yaml
213
+ function:
214
+ name: my-mcp
215
+ route: mcp
216
+ method: ['GET', 'POST'] # streamable HTTP needs both
217
+ network: ['public']
218
+ env: nodejs-graphql-mcp # the MCP runtime image
219
+
220
+ mcp: true
221
+ mcpGraph: my-federation-gateway # an existing federation lambda in this project
222
+
223
+ # Curated operations exposed as MCP tools. The tool name is the OPERATION name (must be named);
224
+ # optional — the introspection/search/execute tools work without any.
225
+ mcpOperations:
226
+ - query: |
227
+ query GetUser($id: ID!) { user(id: $id) { id name email } }
228
+
229
+ # Static headers sent on EVERY upstream request (e.g. a fixed API key / service token).
230
+ mcpHeaders:
231
+ - name: X-Api-Key
232
+ value: replace-me
233
+
234
+ # Extra request headers forwarded from the caller (Authorization is ALWAYS forwarded).
235
+ mcpForwardHeaders:
236
+ - name: X-Tenant-Id
237
+
238
+ scaleOptions:
239
+ executorType: newdeploy
240
+ minScale: 1
241
+ maxScale: 4
242
+ targetCpu: 70
243
+
244
+ environment:
245
+ name: nodejs-graphql-mcp
246
+ image: rxdi/fission-nodejs-graphql-mcp:0.0.2
247
+ builder: rxdi/fission-node-builder:1.0.8
248
+ poolSize: 0
249
+ region: EU_CENTRAL
250
+ ```
251
+
252
+ ```bash
253
+ gcli lambda:create --project <projectId> --spec ./lambforge.yaml
254
+ ```
255
+
256
+ `--mcp` and `--mcpGraph <name>` are also available as CLI flags; the array fields
257
+ (`mcpOperations`, `mcpHeaders`, `mcpForwardHeaders`) are spec-only. A full annotated example lives in
258
+ [`example/mcp/lambforge.yaml`](./example/mcp/lambforge.yaml). Client connects at
259
+ `https://<host>/<projectId>/<route>/mcp`.
260
+
261
+ ### Conditional Configuration (Environment-Aware Specs)
262
+
263
+ A single spec can describe **many deploy variants** without duplicating the file. The bundler/loader understands a small set of conditional tags that are resolved at load time from external environment variables, plus native YAML references (`&anchor` / `*alias`) and merge keys (`<<`) for de-duplication.
264
+
265
+ This is how you ship the **same code to two different targets** — e.g. the legacy Docker servers (where everything must be bundled) and the new [lambforge.com](https://www.lambforge.com) platform (where the `@gapi/core` framework is preinstalled in the environment image, so it must stay _external_ to avoid double-bundling).
266
+
267
+ #### Supported tags
268
+
269
+ | Tag | Form | Resolves to |
270
+ | --------- | ------------------------------------------ | ----------------------------------------------------------------- |
271
+ | `!env` | `!env VAR` | `process.env.VAR` (or `null` when unset) |
272
+ | `!env` | `!env [VAR, default]` | `process.env.VAR ?? default` |
273
+ | `!switch` | `!switch { var, default, cases }` | `cases[process.env[var] ?? default]` |
274
+ | `!if` | `!if { when, then, else }` | `then` when `when` is truthy, else `else` |
275
+ | `!if` | `!if { var, equals, default, then, else }` | `then` when `process.env[var] ?? default === equals`, else `else` |
276
+
277
+ Notes:
278
+
279
+ - `!switch` requires a `var`. When the selected value has no matching case it falls back to `cases[default]`, otherwise `null`.
280
+ - `!if` truthiness treats `null`, `false`, `''`, `"false"` and `"0"` as **falsy**.
281
+ - Tags work **anywhere** in the file (any field — `external`, `scaleOptions`, `image`, `network`, …), and may be nested (e.g. an `!env` inside an `!if`).
282
+ - Specs that use **no** tags keep parsing exactly as before.
283
+
284
+ #### Recipes
285
+
286
+ ##### 1. One file, two deploys (the flagship use case)
287
+
288
+ Select the bundler externals at build/deploy time. The new lambforge.com platform ships the framework preinstalled (keep it external), while the legacy Docker image ships nothing (bundle everything).
289
+
290
+ ```yaml
291
+ # Framework packages shipped preinstalled by the lambforge.com env image.
292
+ # Declared once as an anchor and reused below.
293
+ .frameworkExternals: &frameworkExternals
294
+ - '@gapi/core'
295
+ - '@rxdi/core'
296
+ - 'graphql'
297
+ - 'rxjs'
298
+ - 'reflect-metadata'
299
+ # ...the rest of the preinstalled packages
300
+
301
+ options:
302
+ bundler:
303
+ bundle: true
304
+ target: node24
305
+ outfile: 'index.js'
306
+ external: !switch
307
+ var: LAMBFORGE_TARGET
308
+ default: lambforge # unset => new platform => keep framework external
309
+ cases:
310
+ lambforge: *frameworkExternals
311
+ docker: [] # legacy servers => nothing preinstalled => bundle everything
312
+ ```
313
+
314
+ ```bash
315
+ # New lambforge.com platform (default) — framework stays external, slim bundle
316
+ gcli lambda:create
317
+
318
+ # Legacy Docker servers — bundle every dependency
319
+ LAMBFORGE_TARGET=docker gcli lambda:create
320
+ ```
321
+
322
+ ##### 2. Per-stage autoscaling (anchors + merge keys for DRY)
323
+
324
+ Define a base block once, then override only what changes per stage. Branches that are YAML literals keep their type (numbers stay numbers).
325
+
326
+ ```yaml
327
+ # Shared baseline, declared once.
328
+ .baseScale: &baseScale
329
+ minCpu: 30
330
+ maxCpu: 500
331
+ minMemory: 128
332
+ maxMemory: 1024
333
+ targetCpu: 80
334
+ executorType: newdeploy
335
+ concurrency: 1000
336
+ functionTimeout: 120
337
+
338
+ function:
339
+ name: graphql-server-lambdas
340
+ scaleOptions:
341
+ <<: *baseScale # merge in the shared baseline…
342
+ # …and override per environment
343
+ minScale:
344
+ !switch {
345
+ var: DEPLOY_ENV,
346
+ default: dev,
347
+ cases: { prod: 2, staging: 1, dev: 0 },
348
+ }
349
+ maxScale:
350
+ !switch {
351
+ var: DEPLOY_ENV,
352
+ default: dev,
353
+ cases: { prod: 10, staging: 4, dev: 2 },
354
+ }
355
+ ```
356
+
357
+ ##### 3. Region / image / builder selection (strings with sane defaults)
358
+
359
+ ```yaml
360
+ environment:
361
+ name: nodejs-graphql-lambdas
362
+ # `!env [VAR, default]` — falls back to the default when the var is unset.
363
+ image: !env [ENV_IMAGE, 'rxdi/fission-nodejs-graphql-lambdas:0.0.4']
364
+ builder: !env [ENV_BUILDER, 'rxdi/fission-node-builder:1.0.8']
365
+ region: !switch
366
+ var: REGION
367
+ default: eu
368
+ cases:
369
+ eu: EU_CENTRAL
370
+ us: US_EAST
371
+ ```
372
+
373
+ ##### 4. Network visibility & secrets per stage
374
+
375
+ ```yaml
376
+ function:
377
+ # Public only in dev; private everywhere else.
378
+ network: !switch
379
+ var: DEPLOY_ENV
380
+ default: prod
381
+ cases:
382
+ dev: ['public']
383
+ staging: ['private']
384
+ prod: ['private']
385
+ # Swap the mounted secret set per stage.
386
+ secrets: !switch
387
+ var: DEPLOY_ENV
388
+ default: prod
389
+ cases:
390
+ dev: ['environment-dev']
391
+ prod: ['environment']
392
+ ```
393
+
394
+ ##### 5. Build toggles (minify only in production)
395
+
396
+ ```yaml
397
+ options:
398
+ bundler:
399
+ target: !env [BUILD_TARGET, node24]
400
+ # `!if { var, equals }` returns real booleans.
401
+ minify: !if { var: NODE_ENV, equals: production, then: true, else: false }
402
+ bundle: !if { var: NO_BUNDLE, equals: 'true', then: false, else: true }
403
+ ```
404
+
405
+ ##### 6. Feature flags with `!if` truthiness
406
+
407
+ `!if { when }` branches on the truthiness of a value. `!env DEBUG` resolves to `null` when unset and to the string when set; `null`, `''`, `"false"` and `"0"` are treated as falsy.
408
+
409
+ ```yaml
410
+ options:
411
+ bundler:
412
+ # Ship sourcemaps-style verbose build only when DEBUG is set to a truthy value.
413
+ minify: !if { when: !env DEBUG, then: false, else: true }
414
+ ```
415
+
416
+ ##### 7. Nested conditionals
417
+
418
+ Tags compose — a `!switch` branch can itself be an `!if`, an `!env`, or another `!switch`.
419
+
420
+ ```yaml
421
+ environment:
422
+ region: !switch
423
+ var: DEPLOY_ENV
424
+ default: dev
425
+ cases:
426
+ # In prod, pick the region from REGION; everywhere else stay in EU.
427
+ prod: !if { var: REGION, equals: us, then: US_EAST, else: EU_CENTRAL }
428
+ dev: EU_CENTRAL
429
+ ```
430
+
431
+ ##### 8. Putting it together — a multi-stage spec
432
+
433
+ ```yaml
434
+ .frameworkExternals: &frameworkExternals
435
+ - '@gapi/core'
436
+ - 'rxjs'
437
+ - 'graphql'
438
+ - 'reflect-metadata'
439
+
440
+ .baseScale: &baseScale
441
+ minCpu: 30
442
+ maxCpu: 500
443
+ minMemory: 128
444
+ maxMemory: 1024
445
+
446
+ function:
447
+ name: graphql-server-lambdas
448
+ route: graphql-server-lambdas
449
+ file: ./src/main.ts
450
+ network:
451
+ !switch {
452
+ var: DEPLOY_ENV,
453
+ default: prod,
454
+ cases: { dev: ['public'], prod: ['private'] },
455
+ }
456
+ scaleOptions:
457
+ <<: *baseScale
458
+ minScale:
459
+ !switch { var: DEPLOY_ENV, default: dev, cases: { prod: 2, dev: 0 } }
460
+ maxScale:
461
+ !switch { var: DEPLOY_ENV, default: dev, cases: { prod: 10, dev: 2 } }
462
+
463
+ options:
464
+ bundler:
465
+ target: node24
466
+ minify: !if { var: DEPLOY_ENV, equals: prod, then: true, else: false }
467
+ external: !switch
468
+ var: LAMBFORGE_TARGET
469
+ default: lambforge
470
+ cases:
471
+ lambforge: *frameworkExternals
472
+ docker: []
473
+ ```
474
+
475
+ ```bash
476
+ # Production deploy to the new platform
477
+ DEPLOY_ENV=prod gcli lambda:update
478
+
479
+ # Dev deploy, still bundling everything for a local Docker run
480
+ DEPLOY_ENV=dev LAMBFORGE_TARGET=docker gcli lambda:create
481
+ ```
482
+
483
+ #### Setting the variables
484
+
485
+ The tags read from `process.env` at the moment `gcli` loads the spec (i.e. at build/deploy time), so set them however that process is launched:
486
+
487
+ ```bash
488
+ # Inline for a single command
489
+ LAMBFORGE_TARGET=docker gcli build
490
+
491
+ # Exported for a whole shell session / script
492
+ export DEPLOY_ENV=prod
493
+ gcli lambda:update
494
+ ```
495
+
496
+ ```yaml
497
+ # GitHub Actions — per step or job
498
+ - run: npx gcli lambda:update
499
+ env:
500
+ DEPLOY_ENV: prod
501
+ # LAMBFORGE_TARGET left unset => defaults to the new platform
502
+ ```
503
+
504
+ ```dockerfile
505
+ # Dockerfile — bake the legacy target into the image build
506
+ ENV LAMBFORGE_TARGET=docker
507
+ RUN npm run build:es # gcli build runs here and reads the value
508
+ ```
509
+
510
+ #### Types & gotchas
511
+
512
+ - **`!env` is always a string.** `process.env` values are strings, so `!env [PORT, 8080]` yields the number `8080` when unset but the string `"3000"` when `PORT=3000`. For numeric/boolean fields prefer `!switch`/`!if` with YAML literal branches (e.g. `cases: { prod: 10, dev: 2 }`) — those preserve real numbers and booleans.
513
+ - **`!if { equals }` compares as strings**, so `equals: 'true'` matches the string `"true"`.
514
+ - **`!switch` requires `var`** and falls back to `cases[default]` (then `null`) when the resolved value has no case.
515
+ - **Define anchors before use.** `&anchor` must appear earlier in the file than the `*alias` that references it. A common convention is a leading `.`-prefixed key (e.g. `.frameworkExternals:`), which consumers ignore.
516
+ - **Custom tags require the `gcli` loader.** Other YAML tooling that reads the file with a vanilla parser will error on `!switch` / `!if` / `!env` (`unknown mapping tag`). The CLI itself always understands them.
517
+
208
518
  ### Configuration Auto-Discovery (Zero-Argument Commands)
209
519
 
210
520
  `gcli` is designed to be context-aware. If a configuration file (`lambforge.yaml`, `spec.yaml`, or `env.yaml`) is present in your current directory, you can run commands without arguments.
package/eslint.config.mjs CHANGED
@@ -8,7 +8,12 @@ import prettierConfig from 'eslint-config-prettier';
8
8
  export default defineConfig([
9
9
  globalIgnores(['dist/*', './release', './example', 'coverage']),
10
10
  {
11
- files: ['./src/**/*.ts', './src/**/*.tsx'],
11
+ files: [
12
+ './src/**/*.ts',
13
+ './src/**/*.tsx',
14
+ './test/**/*.ts',
15
+ './test/**/*.tsx',
16
+ ],
12
17
  languageOptions: {
13
18
  parser: tsParser,
14
19
  ecmaVersion: 2018,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gapi/gcli",
3
- "version": "1.8.230",
3
+ "version": "1.8.232",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/Stradivario/gapi.git"
@@ -40,7 +40,6 @@
40
40
  "devDependencies": {
41
41
  "@rxdi/compressor": "^0.7.249",
42
42
  "@types/jest": "^25.2.1",
43
- "@types/js-yaml": "^4.0.3",
44
43
  "@types/node": "^25.0.3",
45
44
  "@types/node-fetch": "^2.5.7",
46
45
  "@typescript-eslint/eslint-plugin": "^8.50.1",
@@ -54,7 +53,7 @@
54
53
  "firebase": "7.14.6",
55
54
  "form-data": "^4.0.5",
56
55
  "jest": "^30.2.0",
57
- "js-yaml": "^4.1.1",
56
+ "js-yaml": "^5.0.0",
58
57
  "node-fetch": "^3.3.2",
59
58
  "prettier": "^3.8.1",
60
59
  "rxjs": "^7.8.2",