@gapi/gcli 1.8.229 → 1.8.231

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,246 @@ options:
205
205
  external: []
206
206
  ```
207
207
 
208
+ ### Conditional Configuration (Environment-Aware Specs)
209
+
210
+ 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.
211
+
212
+ 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).
213
+
214
+ #### Supported tags
215
+
216
+ | Tag | Form | Resolves to |
217
+ | --- | --- | --- |
218
+ | `!env` | `!env VAR` | `process.env.VAR` (or `null` when unset) |
219
+ | `!env` | `!env [VAR, default]` | `process.env.VAR ?? default` |
220
+ | `!switch` | `!switch { var, default, cases }` | `cases[process.env[var] ?? default]` |
221
+ | `!if` | `!if { when, then, else }` | `then` when `when` is truthy, else `else` |
222
+ | `!if` | `!if { var, equals, default, then, else }` | `then` when `process.env[var] ?? default === equals`, else `else` |
223
+
224
+ Notes:
225
+
226
+ - `!switch` requires a `var`. When the selected value has no matching case it falls back to `cases[default]`, otherwise `null`.
227
+ - `!if` truthiness treats `null`, `false`, `''`, `"false"` and `"0"` as **falsy**.
228
+ - Tags work **anywhere** in the file (any field — `external`, `scaleOptions`, `image`, `network`, …), and may be nested (e.g. an `!env` inside an `!if`).
229
+ - Specs that use **no** tags keep parsing exactly as before.
230
+
231
+ #### Recipes
232
+
233
+ ##### 1. One file, two deploys (the flagship use case)
234
+
235
+ 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).
236
+
237
+ ```yaml
238
+ # Framework packages shipped preinstalled by the lambforge.com env image.
239
+ # Declared once as an anchor and reused below.
240
+ .frameworkExternals: &frameworkExternals
241
+ - '@gapi/core'
242
+ - '@rxdi/core'
243
+ - 'graphql'
244
+ - 'rxjs'
245
+ - 'reflect-metadata'
246
+ # ...the rest of the preinstalled packages
247
+
248
+ options:
249
+ bundler:
250
+ bundle: true
251
+ target: node24
252
+ outfile: 'index.js'
253
+ external: !switch
254
+ var: LAMBFORGE_TARGET
255
+ default: lambforge # unset => new platform => keep framework external
256
+ cases:
257
+ lambforge: *frameworkExternals
258
+ docker: [] # legacy servers => nothing preinstalled => bundle everything
259
+ ```
260
+
261
+ ```bash
262
+ # New lambforge.com platform (default) — framework stays external, slim bundle
263
+ gcli lambda:create
264
+
265
+ # Legacy Docker servers — bundle every dependency
266
+ LAMBFORGE_TARGET=docker gcli lambda:create
267
+ ```
268
+
269
+ ##### 2. Per-stage autoscaling (anchors + merge keys for DRY)
270
+
271
+ Define a base block once, then override only what changes per stage. Branches that are YAML literals keep their type (numbers stay numbers).
272
+
273
+ ```yaml
274
+ # Shared baseline, declared once.
275
+ .baseScale: &baseScale
276
+ minCpu: 30
277
+ maxCpu: 500
278
+ minMemory: 128
279
+ maxMemory: 1024
280
+ targetCpu: 80
281
+ executorType: newdeploy
282
+ concurrency: 1000
283
+ functionTimeout: 120
284
+
285
+ function:
286
+ name: graphql-server-lambdas
287
+ scaleOptions:
288
+ <<: *baseScale # merge in the shared baseline…
289
+ # …and override per environment
290
+ minScale: !switch { var: DEPLOY_ENV, default: dev, cases: { prod: 2, staging: 1, dev: 0 } }
291
+ maxScale: !switch { var: DEPLOY_ENV, default: dev, cases: { prod: 10, staging: 4, dev: 2 } }
292
+ ```
293
+
294
+ ##### 3. Region / image / builder selection (strings with sane defaults)
295
+
296
+ ```yaml
297
+ environment:
298
+ name: nodejs-graphql-lambdas
299
+ # `!env [VAR, default]` — falls back to the default when the var is unset.
300
+ image: !env [ENV_IMAGE, 'rxdi/fission-nodejs-graphql-lambdas:0.0.4']
301
+ builder: !env [ENV_BUILDER, 'rxdi/fission-node-builder:1.0.8']
302
+ region: !switch
303
+ var: REGION
304
+ default: eu
305
+ cases:
306
+ eu: EU_CENTRAL
307
+ us: US_EAST
308
+ ```
309
+
310
+ ##### 4. Network visibility & secrets per stage
311
+
312
+ ```yaml
313
+ function:
314
+ # Public only in dev; private everywhere else.
315
+ network: !switch
316
+ var: DEPLOY_ENV
317
+ default: prod
318
+ cases:
319
+ dev: ['public']
320
+ staging: ['private']
321
+ prod: ['private']
322
+ # Swap the mounted secret set per stage.
323
+ secrets: !switch
324
+ var: DEPLOY_ENV
325
+ default: prod
326
+ cases:
327
+ dev: ['environment-dev']
328
+ prod: ['environment']
329
+ ```
330
+
331
+ ##### 5. Build toggles (minify only in production)
332
+
333
+ ```yaml
334
+ options:
335
+ bundler:
336
+ target: !env [BUILD_TARGET, node24]
337
+ # `!if { var, equals }` returns real booleans.
338
+ minify: !if { var: NODE_ENV, equals: production, then: true, else: false }
339
+ bundle: !if { var: NO_BUNDLE, equals: 'true', then: false, else: true }
340
+ ```
341
+
342
+ ##### 6. Feature flags with `!if` truthiness
343
+
344
+ `!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.
345
+
346
+ ```yaml
347
+ options:
348
+ bundler:
349
+ # Ship sourcemaps-style verbose build only when DEBUG is set to a truthy value.
350
+ minify: !if { when: !env DEBUG, then: false, else: true }
351
+ ```
352
+
353
+ ##### 7. Nested conditionals
354
+
355
+ Tags compose — a `!switch` branch can itself be an `!if`, an `!env`, or another `!switch`.
356
+
357
+ ```yaml
358
+ environment:
359
+ region: !switch
360
+ var: DEPLOY_ENV
361
+ default: dev
362
+ cases:
363
+ # In prod, pick the region from REGION; everywhere else stay in EU.
364
+ prod: !if { var: REGION, equals: us, then: US_EAST, else: EU_CENTRAL }
365
+ dev: EU_CENTRAL
366
+ ```
367
+
368
+ ##### 8. Putting it together — a multi-stage spec
369
+
370
+ ```yaml
371
+ .frameworkExternals: &frameworkExternals
372
+ - '@gapi/core'
373
+ - 'rxjs'
374
+ - 'graphql'
375
+ - 'reflect-metadata'
376
+
377
+ .baseScale: &baseScale
378
+ minCpu: 30
379
+ maxCpu: 500
380
+ minMemory: 128
381
+ maxMemory: 1024
382
+
383
+ function:
384
+ name: graphql-server-lambdas
385
+ route: graphql-server-lambdas
386
+ file: ./src/main.ts
387
+ network: !switch { var: DEPLOY_ENV, default: prod, cases: { dev: ['public'], prod: ['private'] } }
388
+ scaleOptions:
389
+ <<: *baseScale
390
+ minScale: !switch { var: DEPLOY_ENV, default: dev, cases: { prod: 2, dev: 0 } }
391
+ maxScale: !switch { var: DEPLOY_ENV, default: dev, cases: { prod: 10, dev: 2 } }
392
+
393
+ options:
394
+ bundler:
395
+ target: node24
396
+ minify: !if { var: DEPLOY_ENV, equals: prod, then: true, else: false }
397
+ external: !switch
398
+ var: LAMBFORGE_TARGET
399
+ default: lambforge
400
+ cases:
401
+ lambforge: *frameworkExternals
402
+ docker: []
403
+ ```
404
+
405
+ ```bash
406
+ # Production deploy to the new platform
407
+ DEPLOY_ENV=prod gcli lambda:update
408
+
409
+ # Dev deploy, still bundling everything for a local Docker run
410
+ DEPLOY_ENV=dev LAMBFORGE_TARGET=docker gcli lambda:create
411
+ ```
412
+
413
+ #### Setting the variables
414
+
415
+ 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:
416
+
417
+ ```bash
418
+ # Inline for a single command
419
+ LAMBFORGE_TARGET=docker gcli build
420
+
421
+ # Exported for a whole shell session / script
422
+ export DEPLOY_ENV=prod
423
+ gcli lambda:update
424
+ ```
425
+
426
+ ```yaml
427
+ # GitHub Actions — per step or job
428
+ - run: npx gcli lambda:update
429
+ env:
430
+ DEPLOY_ENV: prod
431
+ # LAMBFORGE_TARGET left unset => defaults to the new platform
432
+ ```
433
+
434
+ ```dockerfile
435
+ # Dockerfile — bake the legacy target into the image build
436
+ ENV LAMBFORGE_TARGET=docker
437
+ RUN npm run build:es # gcli build runs here and reads the value
438
+ ```
439
+
440
+ #### Types & gotchas
441
+
442
+ - **`!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.
443
+ - **`!if { equals }` compares as strings**, so `equals: 'true'` matches the string `"true"`.
444
+ - **`!switch` requires `var`** and falls back to `cases[default]` (then `null`) when the resolved value has no case.
445
+ - **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.
446
+ - **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.
447
+
208
448
  ### Configuration Auto-Discovery (Zero-Argument Commands)
209
449
 
210
450
  `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.229",
3
+ "version": "1.8.231",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/Stradivario/gapi.git"
@@ -38,9 +38,8 @@
38
38
  "url": "https://github.com/Stradivario/gapi/issues"
39
39
  },
40
40
  "devDependencies": {
41
- "@rxdi/compressor": "^0.7.248",
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",