@codemarc/blt 1.7.0 → 1.8.1

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.
Files changed (87) hide show
  1. package/README.md +162 -4
  2. package/dist/blt +4 -0
  3. package/dist/blt.d.ts.map +1 -1
  4. package/dist/blt.js.map +1 -1
  5. package/dist/commands/app/data.d.ts +29 -0
  6. package/dist/commands/app/data.d.ts.map +1 -0
  7. package/dist/commands/app/data.js +70 -0
  8. package/dist/commands/app/data.js.map +1 -0
  9. package/dist/commands/app/env-check.d.ts +36 -0
  10. package/dist/commands/app/env-check.d.ts.map +1 -0
  11. package/dist/commands/app/env-check.js +110 -0
  12. package/dist/commands/app/env-check.js.map +1 -0
  13. package/dist/commands/app/paths.d.ts +6 -0
  14. package/dist/commands/app/paths.d.ts.map +1 -0
  15. package/dist/commands/app/paths.js +41 -0
  16. package/dist/commands/app/paths.js.map +1 -0
  17. package/dist/commands/app/pos.d.ts +8 -0
  18. package/dist/commands/app/pos.d.ts.map +1 -0
  19. package/dist/commands/app/pos.js +74 -0
  20. package/dist/commands/app/pos.js.map +1 -0
  21. package/dist/commands/app/shell.d.ts +9 -0
  22. package/dist/commands/app/shell.d.ts.map +1 -0
  23. package/dist/commands/app/shell.js +33 -0
  24. package/dist/commands/app/shell.js.map +1 -0
  25. package/dist/commands/app.d.ts +3 -0
  26. package/dist/commands/app.d.ts.map +1 -0
  27. package/dist/commands/app.js +139 -0
  28. package/dist/commands/app.js.map +1 -0
  29. package/dist/commands/data/apply.d.ts.map +1 -1
  30. package/dist/commands/data/apply.js +20 -3
  31. package/dist/commands/data/apply.js.map +1 -1
  32. package/dist/commands/data/helpers.d.ts +12 -2
  33. package/dist/commands/data/helpers.d.ts.map +1 -1
  34. package/dist/commands/data/helpers.js +44 -3
  35. package/dist/commands/data/helpers.js.map +1 -1
  36. package/dist/commands/data/pull.js +2 -2
  37. package/dist/commands/data/pull.js.map +1 -1
  38. package/dist/commands/data.d.ts.map +1 -1
  39. package/dist/commands/data.js +18 -3
  40. package/dist/commands/data.js.map +1 -1
  41. package/dist/commands/env/get.d.ts.map +1 -1
  42. package/dist/commands/env/get.js +4 -2
  43. package/dist/commands/env/get.js.map +1 -1
  44. package/dist/commands/spin/dns.d.ts +7 -0
  45. package/dist/commands/spin/dns.d.ts.map +1 -0
  46. package/dist/commands/spin/dns.js +42 -0
  47. package/dist/commands/spin/dns.js.map +1 -0
  48. package/dist/commands/spin/down.d.ts +3 -0
  49. package/dist/commands/spin/down.d.ts.map +1 -0
  50. package/dist/commands/spin/down.js +17 -0
  51. package/dist/commands/spin/down.js.map +1 -0
  52. package/dist/commands/spin/helpers.d.ts +7 -0
  53. package/dist/commands/spin/helpers.d.ts.map +1 -0
  54. package/dist/commands/spin/helpers.js +38 -0
  55. package/dist/commands/spin/helpers.js.map +1 -0
  56. package/dist/commands/spin/list.d.ts +3 -0
  57. package/dist/commands/spin/list.d.ts.map +1 -0
  58. package/dist/commands/spin/list.js +20 -0
  59. package/dist/commands/spin/list.js.map +1 -0
  60. package/dist/commands/spin/setup.d.ts +3 -0
  61. package/dist/commands/spin/setup.d.ts.map +1 -0
  62. package/dist/commands/spin/setup.js +33 -0
  63. package/dist/commands/spin/setup.js.map +1 -0
  64. package/dist/commands/spin/ssh.d.ts +3 -0
  65. package/dist/commands/spin/ssh.d.ts.map +1 -0
  66. package/dist/commands/spin/ssh.js +14 -0
  67. package/dist/commands/spin/ssh.js.map +1 -0
  68. package/dist/commands/spin/status.d.ts +3 -0
  69. package/dist/commands/spin/status.d.ts.map +1 -0
  70. package/dist/commands/spin/status.js +21 -0
  71. package/dist/commands/spin/status.js.map +1 -0
  72. package/dist/commands/spin/up.d.ts +11 -0
  73. package/dist/commands/spin/up.d.ts.map +1 -0
  74. package/dist/commands/spin/up.js +34 -0
  75. package/dist/commands/spin/up.js.map +1 -0
  76. package/dist/commands/spin.d.ts +3 -0
  77. package/dist/commands/spin.d.ts.map +1 -0
  78. package/dist/commands/spin.js +166 -0
  79. package/dist/commands/spin.js.map +1 -0
  80. package/dist/lib/digitalocean.d.ts +60 -0
  81. package/dist/lib/digitalocean.d.ts.map +1 -0
  82. package/dist/lib/digitalocean.js +108 -0
  83. package/dist/lib/digitalocean.js.map +1 -0
  84. package/dist/lib/repositories.d.ts.map +1 -1
  85. package/dist/lib/repositories.js +6 -0
  86. package/dist/lib/repositories.js.map +1 -1
  87. package/package.json +1 -1
package/README.md CHANGED
@@ -19,6 +19,8 @@
19
19
  - **Workflow**: List, show, delete, reload, and deploy GitHub Actions workflows (requires `gh`)
20
20
  - **Show**: Schema info, row counts, env vars, DB version, repo list
21
21
  - **Env**: Decrypt trailz `.trailz/env/.env.<name>.bin` to `.env` via smash (`blt env get`; requires `SMASH_KEY`)
22
+ - **Spin**: Manage DigitalOcean droplets and DNS for BLT test environments
23
+ - **App**: Developer-triggered POS deployment and test data refresh workflows
22
24
  - **Cleanup**: Remove generated SQL/instance files, clean infrequently used YAML tags
23
25
 
24
26
  ## Installation
@@ -51,6 +53,9 @@ blt template render legal/nda/mutual --data ./doc/nda/mutual-nda-blt-otter.yaml
51
53
  # Update version.json in current directory
52
54
  blt version update
53
55
 
56
+ # List DigitalOcean droplets
57
+ blt spin list
58
+
54
59
  # List Supabase storage buckets
55
60
  blt bucket names
56
61
 
@@ -77,6 +82,8 @@ blt deploy schema
77
82
  | `blt workflow`| list, show, delete, reload, deploy |
78
83
  | `blt bucket` | names, list, upload, download, url, clear |
79
84
  | `blt show` | schema, counts, env, db, repo |
85
+ | `blt spin` | up, down, list, status, ssh, dns, setup |
86
+ | `blt app` | POS deploy and test data refresh workflows |
80
87
  | `blt env` | get (decrypt trailz env to `.env`) |
81
88
  | `blt cleanup` | generated, tags |
82
89
 
@@ -418,11 +425,39 @@ blt deploy sql ./migrations/001_init.sql
418
425
 
419
426
  ---
420
427
 
428
+ ## App Commands
429
+
430
+ The `app` namespace wraps developer on-demand workflows for BLT test environments.
431
+
432
+ ### `blt app pos --branch <branch> --env <env> <target>`
433
+
434
+ Build and deploy POS to a configured test target.
435
+
436
+ ```bash
437
+ blt app pos --branch laf-test --env test-laf test1
438
+ ```
439
+
440
+ The first implementation uses the existing `deploy/sites/test1.bltcore.com` layout and deploys through SSH/SCP to the target alias.
441
+
442
+ ### `blt app data refresh --from <source-env> --to <target-env>`
443
+
444
+ Pull a data snapshot from one environment and apply it to another.
445
+
446
+ ```bash
447
+ blt app data refresh --from laf --to test-laf
448
+ ```
449
+
450
+ This command uses `.trailz/env/.env.<name>.bin` bundles in the **current working directory** (for example a deploy site checkout) and the existing `blt data pull` / `blt data apply` implementation. Snapshots default to `<cwd>/snapshots/` in **toon** format (override with `--out` / `--format`).
451
+
452
+ ---
453
+
421
454
  ## Data snapshots (`blt data`)
422
455
 
423
456
  Export **instance-shaped** rows from Postgres into portable **JSON**, **YAML**, or **TOON** ([Token-Oriented Object Notation](https://toonformat.dev)) payloads that mirror `data/schema/instances/<instance>/yaml/`: `table:` / `procedure:` / `function:` targets plus `{ row }[]`. TOON yields smaller prompts for LLM workflows; `blt data apply` accepts `.toon` snapshots the same as JSON/YAML.
424
457
 
425
- **Pulled assets (full pull):** `role_props`, `modules`, `settings` (one file per `kind`, e.g. `10-020-settings-02-location`), `payment_providers` (`10-020-settings-04-payment-providers`), `nodes`, `create_user` + `add_profile_role_by_email` (optional), `insert_tax`, menu graph (`insert_menu`, `insert_category`, `insert_item` split into `10-121-items-menu` + `10-125-items-NN-<tag>`, `insert_modifier_group` split into `10-135-groups-NN-<tag>`).
458
+ **Curated pulled assets:** `role_props`, `modules`, `settings` (one file per `kind`, e.g. `10-020-settings-02-location`), `payment_providers` (`10-020-settings-04-payment-providers`), `nodes`, `create_user` + `add_profile_role_by_email` (optional via flags), `insert_tax`, menu graph `insert_menu`, `insert_category`, **`public.items`** as **table-mode** rows split into `10-121-items-menu` + `10-125-items-NN-<tag>` (stable `id` values for FKs such as `order_items`), and `insert_modifier_group` split into `10-135-groups-NN-<tag>`. Older snapshots may still contain a legacy `insert_item` function asset instead of `public.items`.
459
+
460
+ **Extra `public.*` tables (default):** By default, pull also discovers other `public` base tables not covered above and writes one asset per table (stems like `10-900-public-<table>`). Use **`--skip-extra-tables`** to pull only the curated manifest.
426
461
 
427
462
  Use snapshots to seed a disposable dev DB from production-shaped data **without writes on pull**.
428
463
 
@@ -435,18 +470,20 @@ Use snapshots to seed a disposable dev DB from production-shaped data **without
435
470
 
436
471
  If `BLT_DATA_PULL_URL` is unset, pull falls back to `SUPABASE_CONNECTION_STRING` (still runs inside `BEGIN READ ONLY`).
437
472
 
438
- **Never** aim `blt data apply` at production. Replay runs `TRUNCATE … RESTART IDENTITY CASCADE` only on **function-mode** tables **listed in that snapshot’s manifest** (e.g. `groups`, `items`, `categories`, `menus`, and `taxes` when those assets are present), which also clears dependents such as `order_items` referencing `items`. Table / procedure assets use `INSERT` / `CALL` upserts and are not truncated globally.
473
+ **Never** aim `blt data apply` at production. Apply first runs **`TRUNCATE … RESTART IDENTITY CASCADE`** only for **snapshot targets present in that manifest** that replace whole tables: function-mode replay targets (`insert_menu` `menus`, `insert_category` `categories`, `insert_modifier_group` → `groups`, `insert_tax` `taxes`) and **table-mode `public.items`** when included. That clears dependent rows (e.g. `order_items` referencing `items`). Other table/procedure assets (`settings`, `nodes`, `payment_providers`, extra `public.*` dumps, etc.) are applied with **`INSERT` `ON CONFLICT`**, `CALL`, or `SELECT` function calls — they are **not** covered by that truncate block unless their target is in the truncate set above.
439
474
 
440
- ### Pull / apply flags and caveats
475
+ ### Pull / apply / remove flags and caveats
441
476
 
442
477
  | Flag | Purpose |
443
478
  |------|---------|
444
479
  | `--skip-users` | Omit `10-070-users` and `10-071-profile-roles-extra`. Use when pulling from prod to avoid staff PII in files. |
445
480
  | `--include-secrets` | Include `api_key`, `secret_key_encrypted`, `webhook_secret_encrypted` on `payment_providers` (default: secrets nulled). |
481
+ | `--skip-extra-tables` | Only pull the curated manifest; omit discovered extra `public.*` base tables. |
446
482
 
447
483
  - **`create_user` / PINs:** Pulled rows set `p_pin: null`; PINs are not recoverable from `auth.users`. To restore deterministic dev PINs after apply, re-run `blt build data <instance>` against the same DB from canonical instance YAML, or edit snapshot rows.
448
484
  - **`payment_providers`:** If the table is missing (older schema), pull writes an empty asset file.
449
485
  - **Item / group file names:** Splits follow content rules (`props.category` vs first `props.tags[]`), not byte-identical parity with hand-authored multi-file YAML (e.g. pizza menu + toppings in one file).
486
+ - **`blt data remove`:** Deletes **`data-snapshots/<instance>/`** (all stamped runs) under the snapshot base. Use `-o, --out <dir>` to match your pull base (default `data-snapshots`). `--dry-run` prints the path that would be removed.
450
487
 
451
488
  ### Commands
452
489
 
@@ -458,13 +495,19 @@ blt data pull laf --format yaml
458
495
  blt data pull laf --format toon
459
496
  # Prod-safe: omit users / profile_roles junction
460
497
  blt data pull prod --skip-users
498
+ # Curated manifest only (no extra public.* dumps)
499
+ blt data pull laf --skip-extra-tables
461
500
 
462
- # Replay onto dev (writes via insert_* SELECTs — dev only)
501
+ # Replay onto dev (truncates menu-graph targets from manifest, then UPSERT / CALL / SELECT — dev only)
463
502
  export BLT_DATA_LOAD_URL="postgresql://...@localhost:5432/postgres"
464
503
  blt data apply ./data-snapshots/laf/2026-05-12T12-30-45Z
465
504
 
466
505
  # Inspect generated SQL without executing
467
506
  blt data apply ./data-snapshots/laf/2026-05-12T12-30-45Z --dry-run
507
+
508
+ # Remove every stamped snapshot under ./data-snapshots/<instance>/
509
+ blt data remove laf
510
+ blt data remove laf --dry-run
468
511
  ```
469
512
 
470
513
  Snapshots default under `data-snapshots/` from the **current working directory**. The **`data`** git repo ignores `data-snapshots/` so accidental commits of customer data are avoided.
@@ -797,6 +840,121 @@ blt show repo --ssh
797
840
 
798
841
  ---
799
842
 
843
+ ## Spin Commands (DigitalOcean)
844
+
845
+ Manage DigitalOcean droplets and DNS records for BLT test environments. Uses the `blt spin` namespace.
846
+
847
+ **Authentication:** The token is resolved in this order:
848
+
849
+ 1. `DIGITALOCEAN_ACCESS_TOKEN` environment variable
850
+ 2. `DIGITALOCEAN_TOKEN` environment variable
851
+ 3. doctl config file (written by `doctl auth init`)
852
+
853
+ If you've already run `doctl auth init`, no extra env var setup is needed.
854
+
855
+ ### `blt spin up`
856
+
857
+ Create a new droplet. Waits for the droplet to become active and reports its IP.
858
+
859
+ ```bash
860
+ blt spin up --name test-laf --ssh-keys 12345678
861
+ blt spin up --name test-laf --size s-1vcpu-1gb --region nyc3 --image ubuntu-24-04-x64 --tag blt
862
+ ```
863
+
864
+ **Options:**
865
+
866
+ - `--name <name>` — Droplet name (required)
867
+ - `--size <size>` — Droplet size slug (default: `s-1vcpu-512mb-10gb`)
868
+ - `--region <region>` — Region slug (default: `nyc3`)
869
+ - `--image <image>` — OS image slug (default: `ubuntu-24-04-x64`)
870
+ - `--ssh-keys <keys>` — Comma-separated SSH key IDs or fingerprints
871
+ - `--tag <tag>` — Tag for the droplet (default: `blt`)
872
+
873
+ ### `blt spin down <name>`
874
+
875
+ Destroy a droplet. Prompts for confirmation unless `--force` is passed.
876
+
877
+ ```bash
878
+ blt spin down test-laf
879
+ blt spin down test-laf --force
880
+ ```
881
+
882
+ **Options:**
883
+
884
+ - `--force` — Skip confirmation prompt
885
+
886
+ ### `blt spin list`
887
+
888
+ List droplets filtered by tag.
889
+
890
+ ```bash
891
+ blt spin list
892
+ blt spin list --tag staging
893
+ ```
894
+
895
+ **Options:**
896
+
897
+ - `--tag <tag>` — Filter by tag (default: `blt`)
898
+
899
+ ### `blt spin status <name>`
900
+
901
+ Show detailed information about a droplet.
902
+
903
+ ```bash
904
+ blt spin status test-laf
905
+ blt spin status 12345678
906
+ ```
907
+
908
+ ### `blt spin ssh <name>`
909
+
910
+ SSH into a droplet. Resolves the droplet's public IP and opens an interactive SSH session.
911
+
912
+ ```bash
913
+ blt spin ssh test-laf
914
+ blt spin ssh test-laf --user root
915
+ ```
916
+
917
+ **Options:**
918
+
919
+ - `--user <user>` — SSH user (default: `blt`)
920
+
921
+ ### `blt spin dns <action>`
922
+
923
+ Manage DNS records for a domain.
924
+
925
+ ```bash
926
+ # Create an A record
927
+ blt spin dns create --domain bltcore.com --name test-laf --ip 1.2.3.4
928
+
929
+ # List all DNS records
930
+ blt spin dns list --domain bltcore.com
931
+
932
+ # Delete a record by ID
933
+ blt spin dns delete --domain bltcore.com --record-id 12345678
934
+ ```
935
+
936
+ **Options:**
937
+
938
+ - `--domain <domain>` — Domain name (default: `bltcore.com`)
939
+ - `--name <name>` — Record name / subdomain (for create)
940
+ - `--ip <ip>` — IP address for A record (for create)
941
+ - `--record-id <id>` — Record ID (for delete)
942
+
943
+ ### `blt spin setup <name>`
944
+
945
+ Bootstrap a fresh droplet by uploading and running `deploy/scripts/bootstrap-droplet.sh`. If the script doesn't exist, prints what would run.
946
+
947
+ ```bash
948
+ blt spin setup test-laf
949
+ blt spin setup test-laf --user root
950
+ ```
951
+
952
+ **Options:**
953
+
954
+ - `--user <user>` — SSH user for bootstrap (default: `root`)
955
+
956
+ ---
957
+
800
958
  ## Env Commands
801
959
 
802
960
  ### `blt env get <name>`
package/dist/blt CHANGED
@@ -14,6 +14,8 @@ import waiCommand from "./commands/wai";
14
14
  import templateCommand from "./commands/template";
15
15
  import envCommand from "./commands/env";
16
16
  import dataCommand from "./commands/data";
17
+ import spinCommand from "./commands/spin";
18
+ import appCommand from "./commands/app";
17
19
  import { join, dirname } from "node:path";
18
20
  import { readFileSync } from "node:fs";
19
21
  import { fileURLToPath } from "node:url";
@@ -41,6 +43,8 @@ waiCommand(program);
41
43
  templateCommand(program);
42
44
  envCommand(program);
43
45
  dataCommand(program);
46
+ spinCommand(program);
47
+ appCommand(program);
44
48
  // If no command is provided, show help
45
49
  const args = process.argv.slice(2);
46
50
  if (args.length === 0) {
package/dist/blt.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"blt.d.ts","sourceRoot":"","sources":["../src/blt.ts"],"names":[],"mappings":";AAyBA,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C"}
1
+ {"version":3,"file":"blt.d.ts","sourceRoot":"","sources":["../src/blt.ts"],"names":[],"mappings":";AA2BA,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C"}
package/dist/blt.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"blt.js","sourceRoot":"","sources":["../src/blt.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,aAAa,MAAM,mBAAmB,CAAC;AAC9C,OAAO,YAAY,MAAM,kBAAkB,CAAC;AAC5C,OAAO,aAAa,MAAM,mBAAmB,CAAC;AAC9C,OAAO,YAAY,MAAM,kBAAkB,CAAC;AAC5C,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAChD,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAChD,OAAO,eAAe,MAAM,qBAAqB,CAAC;AAClD,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,eAAe,MAAM,qBAAqB,CAAC;AAClD,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC;AAE/D,MAAM,UAAU,iBAAiB;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;KACjB,OAAO,CAAC,iBAAiB,EAAE,CAAC;KAC5B,WAAW,CAAC,cAAc,CAAC,CAAC;AAE5B,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,WAAW,CAAC,OAAO,CAAC,CAAC;AAEvB,uCAAuC;AACvC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACtB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AACD,OAAO,CAAC,GAAG,EAAE,CAAC"}
1
+ {"version":3,"file":"blt.js","sourceRoot":"","sources":["../src/blt.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,aAAa,MAAM,mBAAmB,CAAC;AAC9C,OAAO,YAAY,MAAM,kBAAkB,CAAC;AAC5C,OAAO,aAAa,MAAM,mBAAmB,CAAC;AAC9C,OAAO,YAAY,MAAM,kBAAkB,CAAC;AAC5C,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAChD,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAChD,OAAO,eAAe,MAAM,qBAAqB,CAAC;AAClD,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,eAAe,MAAM,qBAAqB,CAAC;AAClD,OAAO,UAAU,MAAM,gBAAgB,CAAC;AACxC,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,UAAU,MAAM,gBAAgB,CAAC;AAExC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAG,IAAI,EAAE,cAAc,CAAC,CAAC;AAE/D,MAAM,UAAU,iBAAiB;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;KACjB,OAAO,CAAC,iBAAiB,EAAE,CAAC;KAC5B,WAAW,CAAC,cAAc,CAAC,CAAC;AAE5B,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,UAAU,CAAC,OAAO,CAAC,CAAC;AAEtB,uCAAuC;AACvC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACtB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AACD,OAAO,CAAC,GAAG,EAAE,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { Logger } from "@caporal/core";
2
+ type DataFormat = "json" | "yaml" | "toon";
3
+ /** Default `--out` segment; resolved under the invocation cwd (not the data repo). */
4
+ export declare const APP_DATA_DEFAULT_OUT = "data-snapshots";
5
+ export declare const APP_DATA_DEFAULT_FORMAT: DataFormat;
6
+ /** Absolute snapshot base dir anchored to where the user ran the command. */
7
+ export declare function resolveAppSnapshotOutDir(out: string, invocationCwd?: string): string;
8
+ export type AppDataPullOptions = {
9
+ env: string;
10
+ out: string;
11
+ format: DataFormat;
12
+ };
13
+ export type AppDataApplyOptions = {
14
+ env: string;
15
+ snapshotDir: string;
16
+ out: string;
17
+ dryRun: boolean;
18
+ };
19
+ export type AppDataRefreshOptions = {
20
+ from: string;
21
+ to: string;
22
+ out: string;
23
+ format: DataFormat;
24
+ };
25
+ export declare function appDataPull(opts: AppDataPullOptions, logger: Logger): Promise<void>;
26
+ export declare function appDataApply(opts: AppDataApplyOptions, logger: Logger): Promise<void>;
27
+ export declare function appDataRefresh(opts: AppDataRefreshOptions, logger: Logger): Promise<void>;
28
+ export {};
29
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/commands/app/data.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAO5C,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C,sFAAsF;AACtF,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD,eAAO,MAAM,uBAAuB,EAAE,UAAmB,CAAC;AAE1D,6EAA6E;AAC7E,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,SAAgB,GAAG,MAAM,CAE3F;AAED,MAAM,MAAM,kBAAkB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC;CACnB,CAAC;AAsBF,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3F;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/F"}
@@ -0,0 +1,70 @@
1
+ import { isAbsolute, resolve } from "node:path";
2
+ import { dataPull } from "../data/pull";
3
+ import { dataApply } from "../data/apply";
4
+ import { resolveLatestSnapshot } from "../data/helpers";
5
+ import { ensureAppEnv, parseDotEnvContent, trailzEnvBasePath } from "./env-check";
6
+ import { readCommand } from "./shell";
7
+ /** Default `--out` segment; resolved under the invocation cwd (not the data repo). */
8
+ export const APP_DATA_DEFAULT_OUT = "data-snapshots";
9
+ export const APP_DATA_DEFAULT_FORMAT = "toon";
10
+ /** Absolute snapshot base dir anchored to where the user ran the command. */
11
+ export function resolveAppSnapshotOutDir(out, invocationCwd = process.cwd()) {
12
+ return isAbsolute(out) ? out : resolve(invocationCwd, out);
13
+ }
14
+ function loadDataEnv(envName, logger) {
15
+ const cwd = process.cwd();
16
+ const availability = ensureAppEnv({ envName, cwd, logger, trailzOnly: true });
17
+ const envPath = trailzEnvBasePath(cwd, envName);
18
+ const content = readCommand("smash", ["-n", envPath], logger, { cwd });
19
+ if (!content.trim()) {
20
+ throw new Error(`Env bundle produced no output: ${availability.bundlePath}`);
21
+ }
22
+ return parseDotEnvContent(content);
23
+ }
24
+ function withTemporaryEnv(env, run) {
25
+ const previous = process.env;
26
+ process.env = env;
27
+ return run().finally(() => {
28
+ process.env = previous;
29
+ });
30
+ }
31
+ export async function appDataPull(opts, logger) {
32
+ const invocationCwd = process.cwd();
33
+ const outDir = resolveAppSnapshotOutDir(opts.out, invocationCwd);
34
+ const env = loadDataEnv(opts.env, logger);
35
+ logger.info(`Snapshots base: ${outDir}`);
36
+ await withTemporaryEnv(env, async () => {
37
+ await dataPull(opts.env, {
38
+ outBaseDir: outDir,
39
+ format: opts.format,
40
+ includeUsers: true,
41
+ includeSecrets: false,
42
+ includeExtraTables: true,
43
+ }, logger);
44
+ });
45
+ }
46
+ export async function appDataApply(opts, logger) {
47
+ const invocationCwd = process.cwd();
48
+ const outDir = resolveAppSnapshotOutDir(opts.out, invocationCwd);
49
+ const env = loadDataEnv(opts.env, logger);
50
+ await withTemporaryEnv(env, async () => {
51
+ let snapshotDir = opts.snapshotDir;
52
+ if (snapshotDir === "latest") {
53
+ const resolved = resolveLatestSnapshot(outDir);
54
+ if (!resolved) {
55
+ throw new Error(`No snapshots found under ${outDir}/`);
56
+ }
57
+ snapshotDir = resolved;
58
+ logger.info(`Resolved latest -> ${snapshotDir}`);
59
+ }
60
+ else if (!isAbsolute(snapshotDir)) {
61
+ snapshotDir = resolve(invocationCwd, snapshotDir);
62
+ }
63
+ await dataApply(snapshotDir, { dryRun: opts.dryRun }, logger);
64
+ });
65
+ }
66
+ export async function appDataRefresh(opts, logger) {
67
+ await appDataPull({ env: opts.from, out: opts.out, format: opts.format }, logger);
68
+ await appDataApply({ env: opts.to, snapshotDir: "latest", out: opts.out, dryRun: false }, logger);
69
+ }
70
+ //# sourceMappingURL=data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.js","sourceRoot":"","sources":["../../../src/commands/app/data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { Logger } from "@caporal/core";
2
+ /** Canonical tag written by `blt env get` and recognized on preflight. */
3
+ export declare const BLT_ENV_NAME_KEY = "BLT_ENV_NAME";
4
+ /** Legacy / human-readable header (still parsed for older `.env` files). */
5
+ export declare const ENV_FROM_COMMENT_RE: RegExp;
6
+ export type AppEnvSource = "local-dotenv" | "trailz-bundle";
7
+ export type AppEnvAvailability = {
8
+ envName: string;
9
+ source: AppEnvSource;
10
+ cwd: string;
11
+ dotEnvPath: string;
12
+ bundlePath: string;
13
+ };
14
+ export declare function trailzEnvBasePath(cwd: string, envName: string): string;
15
+ export declare function trailzEnvBundlePath(cwd: string, envName: string): string;
16
+ /** Device-manager bundle name paired with a site env (e.g. test1 → test1-dm). */
17
+ export declare function siteDmEnvName(siteEnvName: string): string;
18
+ export declare function appDmEnvPath(cwd: string): string;
19
+ export declare function dotEnvPathFor(cwd: string): string;
20
+ /** Header prepended when decrypting trailz env bundles to `.env`. */
21
+ export declare function formatEnvFileHeader(envName: string): string;
22
+ /** Parse `BLT_ENV_NAME` or `# from .trailz/env/.env.<name>.bin` from a `.env` body. */
23
+ export declare function parseEnvNameFromDotEnv(content: string): string | null;
24
+ export declare function parseDotEnvContent(content: string, baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
25
+ /**
26
+ * Fail fast when the requested env is not available in `cwd`.
27
+ * Accepts a loaded, tagged `.env` or a trailz ciphertext bundle (+ SMASH_KEY).
28
+ */
29
+ export declare function ensureAppEnv(opts: {
30
+ envName: string;
31
+ cwd: string;
32
+ logger?: Logger;
33
+ /** When true, skip cwd `.env` and require `.trailz/env/.env.<name>.bin` (app data workflows). */
34
+ trailzOnly?: boolean;
35
+ }): AppEnvAvailability;
36
+ //# sourceMappingURL=env-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-check.d.ts","sourceRoot":"","sources":["../../../src/commands/app/env-check.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,QAA6E,CAAC;AAE9G,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,eAAe,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO3D;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYrE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,UAAU,CAe/G;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,kBAAkB,CAsDrB"}
@@ -0,0 +1,110 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /** Canonical tag written by `blt env get` and recognized on preflight. */
4
+ export const BLT_ENV_NAME_KEY = "BLT_ENV_NAME";
5
+ /** Legacy / human-readable header (still parsed for older `.env` files). */
6
+ export const ENV_FROM_COMMENT_RE = /^#\s*from\s+\.trailz\/env\/\.env\.([a-zA-Z0-9][a-zA-Z0-9._-]*)\.bin\s*$/m;
7
+ export function trailzEnvBasePath(cwd, envName) {
8
+ return join(cwd, ".trailz", "env", `.env.${envName}`);
9
+ }
10
+ export function trailzEnvBundlePath(cwd, envName) {
11
+ return `${trailzEnvBasePath(cwd, envName)}.bin`;
12
+ }
13
+ /** Device-manager bundle name paired with a site env (e.g. test1 → test1-dm). */
14
+ export function siteDmEnvName(siteEnvName) {
15
+ return `${siteEnvName}-dm`;
16
+ }
17
+ export function appDmEnvPath(cwd) {
18
+ return join(cwd, "app", ".dm.env");
19
+ }
20
+ export function dotEnvPathFor(cwd) {
21
+ return join(cwd, ".env");
22
+ }
23
+ /** Header prepended when decrypting trailz env bundles to `.env`. */
24
+ export function formatEnvFileHeader(envName) {
25
+ return [
26
+ `# BLT environment: ${envName}`,
27
+ `# from .trailz/env/.env.${envName}.bin`,
28
+ `${BLT_ENV_NAME_KEY}=${envName}`,
29
+ "",
30
+ ].join("\n");
31
+ }
32
+ /** Parse `BLT_ENV_NAME` or `# from .trailz/env/.env.<name>.bin` from a `.env` body. */
33
+ export function parseEnvNameFromDotEnv(content) {
34
+ for (const rawLine of content.split("\n")) {
35
+ const line = rawLine.trim();
36
+ if (!line || line.startsWith("#"))
37
+ continue;
38
+ if (line.startsWith(`${BLT_ENV_NAME_KEY}=`)) {
39
+ const value = line.slice(BLT_ENV_NAME_KEY.length + 1).trim();
40
+ return value.length > 0 ? value : null;
41
+ }
42
+ }
43
+ const commentMatch = ENV_FROM_COMMENT_RE.exec(content);
44
+ return commentMatch?.[1] ?? null;
45
+ }
46
+ export function parseDotEnvContent(content, baseEnv = process.env) {
47
+ const nextEnv = { ...baseEnv };
48
+ for (const rawLine of content.split("\n")) {
49
+ const line = rawLine.trim();
50
+ if (!line || line.startsWith("#"))
51
+ continue;
52
+ if (line.startsWith(`${BLT_ENV_NAME_KEY}=`))
53
+ continue;
54
+ const eq = line.indexOf("=");
55
+ if (eq <= 0)
56
+ continue;
57
+ const key = line.slice(0, eq).trim();
58
+ const value = line.slice(eq + 1).trim();
59
+ nextEnv[key] = value;
60
+ }
61
+ return nextEnv;
62
+ }
63
+ /**
64
+ * Fail fast when the requested env is not available in `cwd`.
65
+ * Accepts a loaded, tagged `.env` or a trailz ciphertext bundle (+ SMASH_KEY).
66
+ */
67
+ export function ensureAppEnv(opts) {
68
+ const bundlePath = trailzEnvBundlePath(opts.cwd, opts.envName);
69
+ const dotEnvPath = dotEnvPathFor(opts.cwd);
70
+ if (!opts.trailzOnly && existsSync(dotEnvPath)) {
71
+ const content = readFileSync(dotEnvPath, "utf8");
72
+ if (content.trim()) {
73
+ const loadedName = parseEnvNameFromDotEnv(content);
74
+ if (loadedName === opts.envName) {
75
+ opts.logger?.info(`Environment "${opts.envName}" loaded in ${dotEnvPath}`);
76
+ return {
77
+ envName: opts.envName,
78
+ source: "local-dotenv",
79
+ cwd: opts.cwd,
80
+ dotEnvPath,
81
+ bundlePath,
82
+ };
83
+ }
84
+ if (loadedName) {
85
+ throw new Error(`Local .env is tagged for "${loadedName}", but this command requires "${opts.envName}".\n` +
86
+ ` cd ${opts.cwd} && blt env get ${opts.envName}`);
87
+ }
88
+ opts.logger?.info(`Local .env exists in ${opts.cwd} but is not tagged with ${BLT_ENV_NAME_KEY}; checking trailz bundle`);
89
+ }
90
+ }
91
+ if (!existsSync(bundlePath)) {
92
+ throw new Error(`Environment "${opts.envName}" is not available in ${opts.cwd}.\n` +
93
+ ` Expected tagged .env (${BLT_ENV_NAME_KEY}=${opts.envName}) or ciphertext at:\n` +
94
+ ` ${bundlePath}\n` +
95
+ ` Load with: cd ${opts.cwd} && blt env get ${opts.envName}`);
96
+ }
97
+ if (!process.env.SMASH_KEY) {
98
+ throw new Error(`Trailz env bundle found for "${opts.envName}" but SMASH_KEY is not set.\n` +
99
+ ` export SMASH_KEY=... or load a tagged .env with: blt env get ${opts.envName}`);
100
+ }
101
+ opts.logger?.info(`Environment "${opts.envName}" available via ${bundlePath}`);
102
+ return {
103
+ envName: opts.envName,
104
+ source: "trailz-bundle",
105
+ cwd: opts.cwd,
106
+ dotEnvPath,
107
+ bundlePath,
108
+ };
109
+ }
110
+ //# sourceMappingURL=env-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-check.js","sourceRoot":"","sources":["../../../src/commands/app/env-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function findWorkspaceRoot(startDir?: string): string;
2
+ export declare function resolveDeploySiteDir(target: string, workspaceRoot?: string): string;
3
+ export declare function resolveDataRepoDir(workspaceRoot?: string): string;
4
+ export declare function resolveDataEnvBundle(envName: string, dataDir?: string): string;
5
+ export { trailzEnvBasePath, trailzEnvBundlePath } from "./env-check";
6
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/commands/app/paths.ts"],"names":[],"mappings":"AAIA,wBAAgB,iBAAiB,CAAC,QAAQ,SAAgB,GAAG,MAAM,CAmBlE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,SAAsB,GAAG,MAAM,CAMhG;AAED,wBAAgB,kBAAkB,CAAC,aAAa,SAAsB,GAAG,MAAM,CAM9E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAuB,GAAG,MAAM,CAM5F;AAED,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { trailzEnvBundlePath } from "./env-check";
4
+ export function findWorkspaceRoot(startDir = process.cwd()) {
5
+ let current = resolve(startDir);
6
+ for (;;) {
7
+ if (existsSync(join(current, "tools", "cli")) &&
8
+ existsSync(join(current, "deploy")) &&
9
+ existsSync(join(current, "data"))) {
10
+ return current;
11
+ }
12
+ const parent = dirname(current);
13
+ if (parent === current) {
14
+ throw new Error("Could not find BLT workspace root. Run from inside the BLT workspace or a subrepo checkout.");
15
+ }
16
+ current = parent;
17
+ }
18
+ }
19
+ export function resolveDeploySiteDir(target, workspaceRoot = findWorkspaceRoot()) {
20
+ const siteDir = join(workspaceRoot, "deploy", "sites", `${target}.bltcore.com`);
21
+ if (!existsSync(siteDir)) {
22
+ throw new Error(`Deploy site directory not found: ${siteDir}`);
23
+ }
24
+ return siteDir;
25
+ }
26
+ export function resolveDataRepoDir(workspaceRoot = findWorkspaceRoot()) {
27
+ const dataDir = join(workspaceRoot, "data");
28
+ if (!existsSync(dataDir)) {
29
+ throw new Error(`Data repo directory not found: ${dataDir}`);
30
+ }
31
+ return dataDir;
32
+ }
33
+ export function resolveDataEnvBundle(envName, dataDir = resolveDataRepoDir()) {
34
+ const bundlePath = trailzEnvBundlePath(dataDir, envName);
35
+ if (!existsSync(bundlePath)) {
36
+ throw new Error(`Data env bundle not found: ${bundlePath}`);
37
+ }
38
+ return bundlePath;
39
+ }
40
+ export { trailzEnvBasePath, trailzEnvBundlePath } from "./env-check";
41
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/commands/app/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,UAAU,iBAAiB,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;IACzD,IAAI,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChC,SAAS,CAAC;QACT,IACC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YACzC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACnC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAChC,CAAC;YACF,OAAO,OAAO,CAAC;QAChB,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACd,6FAA6F,CAC7F,CAAC;QACH,CAAC;QACD,OAAO,GAAG,MAAM,CAAC;IAClB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,aAAa,GAAG,iBAAiB,EAAE;IACvF,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,cAAc,CAAC,CAAC;IAChF,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,oCAAoC,OAAO,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,aAAa,GAAG,iBAAiB,EAAE;IACrE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,OAAO,GAAG,kBAAkB,EAAE;IACnF,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,8BAA8B,UAAU,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,UAAU,CAAC;AACnB,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Logger } from "@caporal/core";
2
+ export type DeployPosAppOptions = {
3
+ target: string;
4
+ branch: string;
5
+ env: string;
6
+ };
7
+ export declare function deployPosApp(opts: DeployPosAppOptions, logger: Logger): Promise<void>;
8
+ //# sourceMappingURL=pos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pos.d.ts","sourceRoot":"","sources":["../../../src/commands/app/pos.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,MAAM,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAQF,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF3F"}