toga-ai 1.0.134 → 1.0.136
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.
|
@@ -3,3 +3,4 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Cart Notification Emails — duplicate prevention](features/cart-notification-emails.md) | On the cart "Notifications" section a user can add CC email addresses to an order. | src/pages/Cart/CartPage.tsx, src/pages/Cart/view/cartForm/CartForm.tsx, src/stores/useEmailOptionsStore.ts, src/stores/useCartSalesQuoteZu.ts, src/pages/Cart/viewModel/FIELDS/*/*/*/CARTPAGE.ts |
|
|
6
|
+
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-commerce` (React + Vite, "commerce2-react") builds and deploys on **AWS Amplify**. | toga2-commerce/amplify.yml, toga2-commerce/.gitattributes, toga2-commerce/package.json, toga2-commerce/.github/workflows/sync-stage-environments.yml |
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AWS Amplify Build & Deploy (non-prod environments)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga2-commerce
|
|
5
|
+
project: TOGa Commerce
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-18
|
|
10
|
+
owners: ["jcardinal"]
|
|
11
|
+
files:
|
|
12
|
+
- toga2-commerce/amplify.yml
|
|
13
|
+
- toga2-commerce/.gitattributes
|
|
14
|
+
- toga2-commerce/package.json
|
|
15
|
+
- toga2-commerce/.github/workflows/sync-stage-environments.yml
|
|
16
|
+
related:
|
|
17
|
+
- ../../toga2-supply/workflows/amplify-build-and-deploy.md
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Summary
|
|
21
|
+
|
|
22
|
+
How `toga2-commerce` (React + Vite, "commerce2-react") builds and deploys on **AWS Amplify**.
|
|
23
|
+
Same branch-per-environment model as `toga2-supply` (see related doc), with one important
|
|
24
|
+
difference: commerce's build scripts **install a per-environment `@agilant/toga-blox` channel**
|
|
25
|
+
before building, so the Amplify build must run the npm scripts (not a raw `vite build`), and the
|
|
26
|
+
matching blox dist-tag must exist on npm first.
|
|
27
|
+
|
|
28
|
+
## Deployment model — one branch per environment (mirrors toga2-supply)
|
|
29
|
+
|
|
30
|
+
- Branches are **underscore-prefixed** (`_production`, `_beta`, `_gamma`, `_qc-security`); the
|
|
31
|
+
Vite mode is the same name **without** the underscore.
|
|
32
|
+
- Repo-root `amplify.yml` drives every branch's build. It sets
|
|
33
|
+
`NODE_OPTIONS=--max-old-space-size=8192`, strips the leading `_` from `$AWS_BRANCH` to get the
|
|
34
|
+
mode, **fails loudly if `.env.<mode>` is missing**, then maps the mode → the matching npm build
|
|
35
|
+
script via a `case` (production→`npm run build`, alpha→`buildAlpha`, beta→`buildBeta`,
|
|
36
|
+
gamma→`buildGamma`, qc-security→`buildQcSecurity`; unmapped → `exit 1`). Single-line command, LF
|
|
37
|
+
only (`.gitattributes` pins `*.yml` to `eol=lf`).
|
|
38
|
+
- `sync-stage-environments.yml` fans `_stage` → `_qc-security` on push.
|
|
39
|
+
|
|
40
|
+
## blox per-environment channel + the publish chain
|
|
41
|
+
|
|
42
|
+
Unlike supply (which pins blox via a normal dependency), each commerce build script installs a
|
|
43
|
+
**blox dist-tag** matching the environment:
|
|
44
|
+
`"buildQcSecurity": "npm install @agilant/toga-blox@qc-security --legacy-peer-deps && tsc && vite build --mode qc-security"`.
|
|
45
|
+
|
|
46
|
+
Those dist-tags are produced by the **`agilantsolutions/toga-blox-npm`** repo's
|
|
47
|
+
`.github/workflows/publish.yml`, which auto-publishes on push to specific branches and maps
|
|
48
|
+
**branch → dist-tag** (one-to-one, underscore stripped): `_production`→`latest`, `_beta`→`beta`,
|
|
49
|
+
`_gamma`→`gamma`, and (added 2026-06-18) `_qc-security`→`qc-security`. So enabling a new channel
|
|
50
|
+
is a two-part change: (1) add the branch + its `elif` case to `publish.yml`, push it onto that
|
|
51
|
+
branch, and let it publish; (2) reference `@agilant/toga-blox@<channel>` in the app's build script.
|
|
52
|
+
|
|
53
|
+
## Steps (to stand up a new environment, e.g. QC Security)
|
|
54
|
+
|
|
55
|
+
1. In `toga-blox-npm`: add the branch to `on.push.branches` and an `elif` case (version suffix +
|
|
56
|
+
`DIST_TAG`) in `publish.yml`; create the publish branch **from** a branch that has the updated
|
|
57
|
+
workflow (e.g. `_qc-security` from `_production`); push → it publishes `@agilant/toga-blox@qc-security`.
|
|
58
|
+
Verify: `npm dist-tag ls @agilant/toga-blox`.
|
|
59
|
+
2. In `toga2-commerce`: add a `build<Env>` script (blox `@<channel>` install + `--legacy-peer-deps`
|
|
60
|
+
+ `tsc` + `vite build --mode <mode>`); add the `case` arm in `amplify.yml`; commit `.env.<mode>`.
|
|
61
|
+
3. Connect the Amplify branch; push; watch for the `Branch '...' -> Vite mode '...'` echo.
|
|
62
|
+
|
|
63
|
+
## Systems involved
|
|
64
|
+
|
|
65
|
+
- AWS Amplify (build + hosting); GitHub Actions (`sync-stage-environments.yml` + the blox
|
|
66
|
+
`publish.yml`); npm registry (`@agilant/toga-blox` dist-tags); Vite 5 + `tsc`.
|
|
67
|
+
|
|
68
|
+
## Edge cases & escalation
|
|
69
|
+
|
|
70
|
+
- **All the toga2-supply gotchas apply** (console build spec overriding repo `amplify.yml`;
|
|
71
|
+
CRLF breaking block-scalar parsing; Vite silently falling back to `.env`/beta when a mode's env
|
|
72
|
+
file is missing). See the related doc.
|
|
73
|
+
- **blox dist-tag must exist before the app builds.** `npm install @agilant/toga-blox@qc-security`
|
|
74
|
+
fails `ETARGET No matching version` until `publish.yml` has published that tag. `publish.yml`
|
|
75
|
+
only triggers on branches listed in `on.push.branches` and only tags branches with an explicit
|
|
76
|
+
`elif` case — creating the branch alone does nothing; the workflow file must be present on that
|
|
77
|
+
branch (GitHub Actions runs the workflow version on the pushed branch).
|
|
78
|
+
- **`--legacy-peer-deps` is required** because blox declares `react-router-dom@^6` as a peer while
|
|
79
|
+
commerce uses `react-router-dom@7` (npm 7+ `ERESOLVE` otherwise). All non-prod build scripts use it.
|
|
80
|
+
- **`--legacy-peer-deps` disables npm's peer auto-install**, so any blox peer that is NOT in
|
|
81
|
+
commerce's own `dependencies` and NOT in blox's `dependencies` must be added explicitly, or Vite/
|
|
82
|
+
Rollup fails with `failed to resolve import "<pkg>"`. Concretely, commerce had to add the table
|
|
83
|
+
peers: `@tanstack/react-table@^8`, `react-table@^7.8.0`, `react-table-sticky@^1.1.3`. Check
|
|
84
|
+
`npm view @agilant/toga-blox@<tag> peerDependencies` against commerce deps when a resolve error
|
|
85
|
+
appears.
|
|
86
|
+
- **Secret hygiene:** commerce's committed `.npmrc` has historically held live FontAwesome + npm
|
|
87
|
+
publish tokens. Rotate and untrack — keep tokens in env vars (`${NODE_AUTH_TOKEN}` /
|
|
88
|
+
`${FONTAWESOME_NPM_TOKEN}`) as the blox `publish.yml` does.
|
|
89
|
+
|
|
90
|
+
## Change history
|
|
91
|
+
|
|
92
|
+
- 2026-06-18 — Initial Amplify setup: added `amplify.yml` (8192 heap, underscore-strip, fail-loud,
|
|
93
|
+
case→npm script), `.gitattributes` (LF), `sync-stage-environments.yml`; added `buildQcSecurity`
|
|
94
|
+
with `@qc-security` blox channel + `--legacy-peer-deps` (and same flag on alpha/beta/gamma);
|
|
95
|
+
added blox table peer deps; added `_qc-security` branch+case to toga-blox-npm `publish.yml`. (jcardinal)
|
|
96
|
+
|
|
97
|
+
## Related docs
|
|
98
|
+
|
|
99
|
+
- [toga2-supply Amplify Build & Deploy](../../toga2-supply/workflows/amplify-build-and-deploy.md) — the base pattern and shared gotchas.
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-18
|
|
10
10
|
owners: ["dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Netsuite.php
|
|
@@ -90,7 +90,15 @@ second, independently-gated concern in the same handler.
|
|
|
90
90
|
|
|
91
91
|
1. `findClickupTaskByOpportunityNumber($tranId)` — GET the list filtered by the `Opportunity #`
|
|
92
92
|
custom field (`include_closed=true&include_archived=true&custom_fields=[{field_id,operator:'=',value}]`).
|
|
93
|
-
2. **Match → `updateTask()
|
|
93
|
+
2. **Match → `updateTask()` — but ONLY when `UPDATE_CLICKUP === true`.** `const UPDATE_CLICKUP = false`
|
|
94
|
+
([Opportunity.php](worker2/Worker/Netsuite/Opportunity.php)) is a **deliberate one-way guard, NOT a
|
|
95
|
+
temporary backstop to flip**: once a ClickUp opportunity task exists, ClickUp is the source of truth
|
|
96
|
+
for it, and a NetSuite edit must **not** clobber edits made on the ClickUp side. With the flag false
|
|
97
|
+
(its production value), a match returns `skipped update (update disabled)` and ClickUp is left
|
|
98
|
+
untouched — so the *designed, correct* behavior of a NetSuite edit to an opportunity that already has
|
|
99
|
+
a task is: **Forecast upserts, ClickUp unchanged.** Do not "enable updates" by flipping it. The
|
|
100
|
+
change-detection logic below only runs in the (currently-off) update path. When enabled, it:
|
|
101
|
+
**diffs first, writes only what changed** (change-detection backstop,
|
|
94
102
|
since 2026-06-17). PUT name/description **only if** one differs; POST **only** the custom fields
|
|
95
103
|
that differ (each to `/task/{id}/field/{fieldId}` — ClickUp has no bulk custom-field set). When
|
|
96
104
|
nothing differs it writes nothing and returns `task unchanged`. This kills the no-op-write echo: a
|
|
@@ -204,6 +212,13 @@ None — platform-wide Forecast sync.
|
|
|
204
212
|
(`https://webhook.togahub.com/netsuite`) with `debugWrap` off. Also flip the enqueuer deployment
|
|
205
213
|
`Testing → Released` (Testing fires only for the deploying user). Full checklist in
|
|
206
214
|
`DEPLOY_RUNBOOK.md` §6.
|
|
215
|
+
- **ClickUp's `=` custom-field filter is substring/prefix, NOT strict equality.** `findClickupTaskByOpportunityNumber($tranId)`
|
|
216
|
+
filters the `Opportunity #` field with `operator:'='`, but for a short-text custom field ClickUp matches
|
|
217
|
+
loosely: a lookup for `74266` still returns a task whose value is `74266--` (confirmed live 2026-06-18).
|
|
218
|
+
So **appending** to the dedup value does NOT defeat the match — to force a dedup *miss* (e.g. to exercise
|
|
219
|
+
the create branch) you must change the **leading** content or clear the field entirely. Verified both ways
|
|
220
|
+
on opp 74266/internalId 7161054: `74266--` → matched (update branch, skipped); field removed → missed
|
|
221
|
+
(create branch, new task `868k2rfj8`).
|
|
207
222
|
- **ClickUp dedup keys on the `Opportunity #` field, not a DB column.** We deliberately did NOT add
|
|
208
223
|
a `clickupTaskId` column — the dedup lookup queries ClickUp itself by `Opportunity #` (= tranId).
|
|
209
224
|
Consequences to know:
|
|
@@ -262,6 +277,16 @@ same **skip-if-unchanged** compare on the extracted values, and **actor-identity
|
|
|
262
277
|
trigger a CU→NS write. The NS→CU change-detection above is the complementary backstop, not a substitute.
|
|
263
278
|
|
|
264
279
|
## Change history
|
|
280
|
+
- 2026-06-18 — **Verified live in production** (no code change): real NetSuite SuiteScript webhooks
|
|
281
|
+
(`user-agent: NetSuite/2026.1`) arrive at `webhook.togahub.com/netsuite` and process successfully
|
|
282
|
+
(e.g. opp 6926224 PUT). Clarified that **`UPDATE_CLICKUP = false` is an intentional one-way guard**
|
|
283
|
+
(NS edits must not overwrite ClickUp-side edits), not a flag to flip — so "NS edit → Forecast upserts,
|
|
284
|
+
existing ClickUp task untouched" is the *designed* behavior. Documented that ClickUp's `=` custom-field
|
|
285
|
+
filter is **substring/prefix**, not exact (a `74266` lookup matched `74266--`); confirmed both dedup
|
|
286
|
+
branches on opp 74266/internalId 7161054. Testing note: the webhook needs the **internalId**, not the
|
|
287
|
+
opportunity number — resolve it via `Forecast.Opportunities.netsuiteOpportunityInternalId` for a given
|
|
288
|
+
`opportunityNumber` (tranId). Fire a manual test with
|
|
289
|
+
`curl -X POST https://webhook.togahub.com/netsuite -d '{"recordType":"opportunity","eventType":"edit","internalId":<id>}'`. (dfranks)
|
|
265
290
|
- 2026-06-17 — ClickUp **change-detection backstop** added to `updateTask()`: diff before write, skip
|
|
266
291
|
no-op syncs (`task unchanged`), PUT name/description only when changed, POST only changed custom
|
|
267
292
|
fields. `findClickupTaskByOpportunityNumber()` now returns the full task object; added
|
package/knowledge/INDEX.md
CHANGED
|
@@ -25,7 +25,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
25
25
|
- **talos** (TOGa IQ) — 6 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
|
|
26
26
|
- **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
|
|
27
27
|
- **ai-bdr** (AI-BDR) — 4 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
|
|
28
|
-
- **toga2-commerce** (TOGa Commerce) —
|
|
28
|
+
- **toga2-commerce** (TOGa Commerce) — 2 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
|
|
29
29
|
|
|
30
30
|
## standalone framework
|
|
31
31
|
|
package/package.json
CHANGED