@explorer02/cfm-survey-sdk 0.2.1 → 0.2.2
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/dist/cli/index.js +18 -18
- package/dist/cli/index.mjs +14 -14
- package/dist/index.d.mts +465 -580
- package/dist/index.d.ts +465 -580
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +6 -4
- package/postinstall.js +96 -69
- package/templates/AGENT.md +14 -8
- package/templates/docs/00-integration/answer-logic-and-navigation.md +50 -0
- package/templates/docs/00-integration/aws-deploy.md +281 -0
- package/templates/docs/00-integration/client-integration-guide.md +351 -0
- package/templates/docs/00-integration/component-checklist.md +45 -22
- package/templates/docs/00-integration/constraints.md +49 -4
- package/templates/docs/00-integration/custom-field-logic-and-navigation.md +174 -0
- package/templates/docs/00-integration/display-logic-and-navigation.md +49 -0
- package/templates/docs/00-integration/end-page-logic.md +64 -0
- package/templates/docs/00-integration/partial-save-and-recovery.md +74 -0
- package/templates/docs/00-integration/placeholders-and-tokens.md +116 -0
- package/templates/docs/00-integration/progress.md +61 -0
- package/templates/docs/00-integration/question-display-variants.md +65 -0
- package/templates/docs/00-integration/question-numbering.md +51 -0
- package/templates/docs/00-integration/question-type-sdk-matrix.md +242 -0
- package/templates/docs/00-integration/setup.md +84 -0
- package/templates/docs/00-integration/skip-logic-and-navigation.md +80 -0
- package/templates/docs/00-integration/survey-lifecycle-analytics.md +94 -0
- package/templates/docs/00-integration/useSurveySDK.md +151 -23
- package/templates/docs/01-components/01-survey-page.md +76 -54
- package/templates/docs/01-components/02-question.md +61 -58
- package/templates/docs/01-components/03-rating-scale.md +11 -1
- package/templates/docs/01-components/04-star-rating-scale.md +45 -0
- package/templates/docs/01-components/05-csat-matrix-scale.md +20 -11
- package/templates/docs/01-components/06-likert-matrix-scale.md +12 -3
- package/templates/docs/01-components/07-slider-matrix-scale.md +11 -2
- package/templates/docs/01-components/08-file-upload-scale.md +10 -1
- package/templates/docs/01-components/09-custom-slider-track.md +2 -2
- package/templates/docs/01-components/11-progress-bar.md +10 -4
- package/templates/docs/01-components/13-matrix-dropdown.md +3 -1
- package/templates/docs/01-components/14-intro-page.md +34 -0
- package/templates/docs/01-components/15-end-page.md +31 -0
- package/templates/docs/01-components/16-paused-page.md +27 -0
- package/templates/docs/01-components/17-heatmap-scale.md +75 -0
- package/templates/docs/01-components/18-rank-order-scale.md +100 -0
- package/templates/docs/01-components/README.md +42 -36
- package/templates/docs/02-reference/config-field-index.md +141 -139
- package/templates/docs/02-reference/exports.md +149 -0
- package/templates/docs/02-reference/question-types/01-rating.md +31 -32
- package/templates/docs/02-reference/question-types/04-csat.md +51 -42
- package/templates/docs/02-reference/question-types/05-rating-scale.md +16 -11
- package/templates/docs/02-reference/question-types/06-slider.md +2 -2
- package/templates/docs/02-reference/question-types/07-matrix-cfm.md +44 -30
- package/templates/docs/02-reference/question-types/08-matrix-csat.md +4 -26
- package/templates/docs/02-reference/question-types/09-matrix-rating.md +29 -13
- package/templates/docs/02-reference/question-types/10-slider-matrix.md +15 -4
- package/templates/docs/02-reference/question-types/11-file-upload.md +13 -2
- package/templates/docs/02-reference/question-types/12-heatmap.md +59 -0
- package/templates/docs/02-reference/question-types/13-rank-order.md +101 -0
- package/templates/docs/02-reference/question-types/README.md +86 -47
- package/templates/docs/02-reference/routing-table.md +33 -18
- package/templates/docs/02-reference/value-derivation.md +63 -0
- package/templates/docs/03-ui-specs/00-question-shell.md +19 -88
- package/templates/docs/03-ui-specs/01-rating.md +12 -54
- package/templates/docs/03-ui-specs/02-radio.md +21 -33
- package/templates/docs/03-ui-specs/03-text.md +2 -2
- package/templates/docs/03-ui-specs/04-csat.md +38 -33
- package/templates/docs/03-ui-specs/05-rating-scale.md +21 -18
- package/templates/docs/03-ui-specs/06-slider.md +3 -1
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +24 -21
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +17 -45
- package/templates/docs/03-ui-specs/09-slider-matrix.md +1 -1
- package/templates/docs/03-ui-specs/10-file-upload.md +1 -1
- package/templates/docs/03-ui-specs/11-text-and-media.md +29 -29
- package/templates/docs/03-ui-specs/12-survey-chrome.md +20 -14
- package/templates/docs/03-ui-specs/13-heatmap.md +73 -0
- package/templates/docs/03-ui-specs/14-rank-order.md +59 -0
- package/templates/docs/03-ui-specs/README.md +13 -13
- package/templates/docs/03-ui-specs/shared/custom-slider-track.md +2 -2
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +5 -0
- package/templates/docs/MANIFEST.json +451 -69
- package/templates/docs/index.md +80 -29
- package/templates/docs/templates/Question.tsx +221 -154
- package/templates/docs/templates/deploy-to-aws.sh +114 -0
- package/templates/docs/templates/heatmapCoords.ts +58 -0
- package/templates/docs/templates/implementation_plan.md +108 -69
- package/templates/docs/templates/survey-inventory.schema.json +72 -0
- package/templates/docs/templates/verify-agent-build.sh +53 -0
- package/templates/docs/01-components/04-csat-scale.md +0 -40
- package/templates/docs/02-question-types/01-rating.md +0 -44
package/templates/AGENT.md
CHANGED
|
@@ -3,9 +3,9 @@ You are an AI coding agent tasked with building a survey UI using the CFM Survey
|
|
|
3
3
|
## Start Here
|
|
4
4
|
|
|
5
5
|
**Monorepo:** read `packages/sdk/templates/docs/index.md`
|
|
6
|
-
**Installed package:** read `node_modules/@explorer02/cfm-survey-sdk/docs/index.md`
|
|
6
|
+
**Installed package:** read `node_modules/@explorer02/cfm-survey-sdk/templates/docs/index.md`
|
|
7
7
|
|
|
8
|
-
Also read `MANIFEST.json` in the same `docs/` folder for machine-readable phase and question-type routing.
|
|
8
|
+
Also read `MANIFEST.json` in the same `templates/docs/` folder for machine-readable phase and question-type routing.
|
|
9
9
|
|
|
10
10
|
## Package Name
|
|
11
11
|
|
|
@@ -20,14 +20,20 @@ Import from whatever appears in `package.json`:
|
|
|
20
20
|
| 0 | Detect framework; extract branding from client prompt |
|
|
21
21
|
| 1 | Scaffold React app — `00-integration/setup.md` |
|
|
22
22
|
| 2 | Ask client for instanceId JWT |
|
|
23
|
-
| 3 | Read `
|
|
24
|
-
| 4 |
|
|
25
|
-
| 5 | Build UI —
|
|
26
|
-
| 6 |
|
|
27
|
-
| 7 |
|
|
23
|
+
| 3 | Read `client-integration-guide.md` + `question-type-sdk-matrix.md` + hook/logic/lifecycle docs — full SDK contract |
|
|
24
|
+
| 4 | Fetch survey; inventory types; read MANIFEST-driven docs per type + matrix row; copy `templates/Question.tsx` + `implementation_plan.md` |
|
|
25
|
+
| 5 | Build UI — read matrix before any scale component; create files from `component-checklist.md`; dispatch on **11** flat `QUESTION_TYPE` literals; apply AWS static export if prompt requires |
|
|
26
|
+
| 6 | Run `templates/verify-agent-build.sh` |
|
|
27
|
+
| 7 | On confirmation: **AWS prompt** → `00-integration/aws-deploy.md` + EC2 3-channel deploy; **Vercel prompt** → `npx cfm-sdk deploy` |
|
|
28
28
|
|
|
29
29
|
**Do NOT** run `cfm-sdk init`, read `survey-config.json`, or use any setup wizard.
|
|
30
30
|
|
|
31
|
-
**Do NOT** render `"not yet implemented"` or default fallback stubs in `Question.tsx`. Copy `docs/templates/Question.tsx` and create all required scale components.
|
|
31
|
+
**Do NOT** render `"not yet implemented"` or default fallback stubs in `Question.tsx`. Copy `templates/docs/templates/Question.tsx` and create all required scale components.
|
|
32
|
+
|
|
33
|
+
**Do NOT** route matrix questions by `subType` — use flat types: `CFM_MATRIX`, `CSAT_MATRIX`, `RATING_MATRIX`, `SLIDER_MATRIX`.
|
|
34
|
+
|
|
35
|
+
**Read `question-type-sdk-matrix.md` before building any scale component** — it defines answer shapes, visibility helpers, and performance expectations for all 11 types.
|
|
36
|
+
|
|
37
|
+
**Monorepo:** run `npm run build` in `packages/sdk` before starting the client dev server.
|
|
32
38
|
|
|
33
39
|
Follow the master index strictly. Do not guess SDK internals. Do not import from `/src/` paths.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Answer Logic and Navigation
|
|
2
|
+
|
|
3
|
+
> Integrator-facing behavior contract. Answer logic evaluation is handled inside the SDK — use exported visibility helpers; do not import internal `surveyLogic` paths directly.
|
|
4
|
+
|
|
5
|
+
## Distinction from display logic
|
|
6
|
+
|
|
7
|
+
Skip logic, display logic, and answer logic share the **same condition tree** and **same evaluator** (`evaluateCondition`). They differ by **what** the condition gates:
|
|
8
|
+
|
|
9
|
+
| Kind | API attachment | SDK field | When true |
|
|
10
|
+
|------|----------------|-----------|-----------|
|
|
11
|
+
| `SKIP` | Question `controlConfig.skipLogics` | `question.skipLogics` | Navigate to skip target or end of survey |
|
|
12
|
+
| `DISPLAY` | Question `controlConfig.displayLogic` | `question.displayLogic` | Show the whole question (dynamic or page-level) |
|
|
13
|
+
| `ANSWER` | Sub-entity `controlConfig.displayLogic` | `answerLogic` on options / rows / columns | Show that option, statement row, or scale column |
|
|
14
|
+
|
|
15
|
+
The API uses `displayLogic` on sub-entities (MCQ `answerDetails`, matrix `childSurveyQuestions`, scale `answerDetails` / `matrixColumnQuestions`). The SDK maps these to `answerLogic` to avoid confusion with question-level display logic.
|
|
16
|
+
|
|
17
|
+
## Supported question types
|
|
18
|
+
|
|
19
|
+
| Type | Answer logic on | Not supported |
|
|
20
|
+
|------|-----------------|---------------|
|
|
21
|
+
| MCQ | Each option | — |
|
|
22
|
+
| SLIDER_MATRIX | Each statement row | Scale columns |
|
|
23
|
+
| CSAT_MATRIX | Each statement row + each scale column | — |
|
|
24
|
+
| RATING_MATRIX | Each statement row only | Scale columns |
|
|
25
|
+
| CFM_MATRIX | Each statement row + each scale column | — |
|
|
26
|
+
| RANK_ORDER | Each option | — |
|
|
27
|
+
| NPS_SCALE, TEXTFIELD, FILE_UPLOAD, TEXT_AND_MEDIA | — | No answer logic |
|
|
28
|
+
|
|
29
|
+
## Page breaks
|
|
30
|
+
|
|
31
|
+
Adding answer logic in the survey builder creates a **page break before that question** (backend page structure). The SDK does not split pages at runtime; it filters visible items within the current page.
|
|
32
|
+
|
|
33
|
+
If **every** answer item is hidden (all options, all rows, or all scale columns), the question is treated as not answerable and may disappear from `state.currentQuestions` (same pattern as display logic empty pages).
|
|
34
|
+
|
|
35
|
+
## Client responsibilities
|
|
36
|
+
|
|
37
|
+
1. Use `state.currentQuestions` from `useSurveySDK` — question-level display logic is already applied.
|
|
38
|
+
2. **MCQ:** Filter options with `getVisibleMcqOptionsForAnswers(question, answers, allQuestions, customFieldValues?)`.
|
|
39
|
+
3. **Rank order:** Filter options with `getVisibleRankOrderOptionsForAnswers(question, answers, allQuestions, customFieldValues?)`.
|
|
40
|
+
4. **Matrix (CSAT, CFM, RATING, SLIDER):** Filter rows and (for CSAT/CFM) columns with `getVisibleMatrixItemsForAnswers(question, answers, allQuestions, customFieldValues?)`.
|
|
41
|
+
5. Pass the same `customFieldValues` map used in `useSurveySDK` options when logic rules reference `CUSTOM_FIELD` entities — see [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md).
|
|
42
|
+
6. Wire `onAction({ type: 'CHANGE' })` so answer logic re-evaluates when prior answers change.
|
|
43
|
+
|
|
44
|
+
## Progress and validation
|
|
45
|
+
|
|
46
|
+
- Progress and validation count only **visible** options, statement rows, and scale columns.
|
|
47
|
+
- Hidden selections are stripped automatically on answer change via SDK sanitization.
|
|
48
|
+
- Required questions with zero visible options are not blocked (treated as answered).
|
|
49
|
+
|
|
50
|
+
See also: [`display-logic-and-navigation.md`](display-logic-and-navigation.md), [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md), [`progress.md`](progress.md).
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# AWS Deploy (EC2 Middle API → S3 → CloudFront)
|
|
2
|
+
|
|
3
|
+
> Deploy a **static** survey UI to Sprinklr AWS using the hosted EC2 deploy API.
|
|
4
|
+
> **Do not** embed AWS credentials in client code — the API uses IAM roles on EC2 to presign S3 URLs.
|
|
5
|
+
|
|
6
|
+
Use this doc when the client prompt mentions **AWS**, **CloudFront**, **S3**, **EC2**, or **Sprinklr hosting**.
|
|
7
|
+
For Vercel deploy, use `npx cfm-sdk deploy` instead (see `index.md` Phase 7).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Architecture — Three-Channel Flow
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
sequenceDiagram
|
|
15
|
+
participant Dev as Agent_DevMachine
|
|
16
|
+
participant API as EC2_Deploy_API
|
|
17
|
+
participant S3 as Hosting_Bucket
|
|
18
|
+
participant CF as CloudFront
|
|
19
|
+
|
|
20
|
+
Dev->>Dev: yarn build or npm run build
|
|
21
|
+
Dev->>API: POST /deploy/start + JWT + file manifest
|
|
22
|
+
API-->>Dev: deployId + presigned PUT URLs
|
|
23
|
+
loop each file in out/
|
|
24
|
+
Dev->>S3: PUT file direct
|
|
25
|
+
end
|
|
26
|
+
Dev->>API: POST /deploy/complete
|
|
27
|
+
API->>S3: HeadObject index.html
|
|
28
|
+
API->>CF: CreateInvalidation
|
|
29
|
+
API-->>Dev: public HTTPS URL
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Channel | Direction | Action |
|
|
33
|
+
|---------|-----------|--------|
|
|
34
|
+
| 1 Control | Dev → EC2 API | `POST /deploy/start` with JWT + file manifest |
|
|
35
|
+
| 2 Data | Dev → S3 direct | `PUT` each static file to presigned URLs |
|
|
36
|
+
| 3 Finalize | Dev → EC2 API | `POST /deploy/complete` → CloudFront URL |
|
|
37
|
+
|
|
38
|
+
S3 path shape: `sites/{surveyId}/{deployId}/` — extracted from JWT at deploy time.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Prerequisites (verify before deploy)
|
|
43
|
+
|
|
44
|
+
1. **Phase 6 passed** — `npm run build` / `yarn build` exits 0
|
|
45
|
+
2. **JWT alignment** — deploy JWT **must equal** `SURVEY_INSTANCE_ID` in `SurveyPage.tsx`
|
|
46
|
+
3. **Static output exists** — Next.js: `out/index.html`; Vite: `dist/index.html`
|
|
47
|
+
4. **File count ≤ 500** (API limit)
|
|
48
|
+
|
|
49
|
+
### Environment variables (dev machine)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export CFM_INSTANCE_ID="<same JWT as SurveyPage SURVEY_INSTANCE_ID>"
|
|
53
|
+
export CFM_DEPLOY_API_BASE="http://43.204.26.213:3000"
|
|
54
|
+
export OUT_DIR="<absolute path to static output folder>"
|
|
55
|
+
# Next.js example:
|
|
56
|
+
# export OUT_DIR="/path/to/project/out"
|
|
57
|
+
# Vite example:
|
|
58
|
+
# export OUT_DIR="/path/to/project/dist"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Health check
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
curl -s "$CFM_DEPLOY_API_BASE/health"
|
|
65
|
+
# Expected: {"ok":true,"service":"sprinklr-deploy-api"}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Build Config (apply during Phase 5 BUILD)
|
|
71
|
+
|
|
72
|
+
### Next.js — static export (required for AWS)
|
|
73
|
+
|
|
74
|
+
See also [`setup.md#nextjs-aws-static-export`](setup.md#nextjs-aws-static-export).
|
|
75
|
+
|
|
76
|
+
**`next.config.js`:**
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
/** @type {import('next').NextConfig} */
|
|
80
|
+
const nextConfig = {
|
|
81
|
+
reactStrictMode: true,
|
|
82
|
+
transpilePackages: ['@explorer02/cfm-survey-sdk'], // or '@repo/sdk'
|
|
83
|
+
output: 'export',
|
|
84
|
+
assetPrefix: './', // required for nested CloudFront path
|
|
85
|
+
images: { unoptimized: true },
|
|
86
|
+
};
|
|
87
|
+
module.exports = nextConfig;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**`package.json` build script** — copy RSC payload for `index.html` URLs:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
"build": "next build && cp out/index.txt out/index.html.txt"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Setting | Why |
|
|
97
|
+
|---------|-----|
|
|
98
|
+
| `output: 'export'` | Produces uploadable `out/` folder (no Node server) |
|
|
99
|
+
| `assetPrefix: './'` | Assets resolve under `/sites/{surveyId}/{deployId}/`, not CloudFront root |
|
|
100
|
+
| `images.unoptimized` | Image optimization needs a server; static export does not |
|
|
101
|
+
| `index.html.txt` copy | Fixes 403 on `index.html.txt?_rsc` when page URL ends with `index.html` |
|
|
102
|
+
|
|
103
|
+
### Vite
|
|
104
|
+
|
|
105
|
+
Set `OUT_DIR` to `dist/`. Ensure asset paths in built HTML are **relative** (no leading `/`). See Vite `base: './'` if needed.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Static Asset Rules (apply during BUILD)
|
|
110
|
+
|
|
111
|
+
| Rule | Detail |
|
|
112
|
+
|------|--------|
|
|
113
|
+
| Public logos | Use relative paths (`logo.svg`), not `/logo.svg` |
|
|
114
|
+
| Rank-order images | Prefer `option.imageUrl` over `option.previewImageUrl` — preview `*_p.*` URLs often return 403 |
|
|
115
|
+
| Footer hash links | Use plain `<a href="#">`, not `next/link` — avoids RSC prefetch on static export |
|
|
116
|
+
| No AWS keys in code | JWT only in `Authorization: Bearer` header at deploy time |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Deploy Procedure (agent executes in order)
|
|
121
|
+
|
|
122
|
+
### Step 0 — Build
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
cd <project-root>
|
|
126
|
+
yarn build # or npm run build
|
|
127
|
+
test -f "$OUT_DIR/index.html" || { echo "Missing index.html"; exit 1; }
|
|
128
|
+
find "$OUT_DIR" -type f | wc -l # must be ≤ 500
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Step 1 — Channel 1: manifest + start
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python3 <<'PY' > /tmp/deploy-manifest.json
|
|
135
|
+
import json, os, mimetypes
|
|
136
|
+
from pathlib import Path
|
|
137
|
+
out = Path(os.environ["OUT_DIR"])
|
|
138
|
+
files = []
|
|
139
|
+
for path in sorted(out.rglob("*")):
|
|
140
|
+
if not path.is_file():
|
|
141
|
+
continue
|
|
142
|
+
rel = path.relative_to(out).as_posix()
|
|
143
|
+
mime, _ = mimetypes.guess_type(rel)
|
|
144
|
+
files.append({
|
|
145
|
+
"path": rel,
|
|
146
|
+
"contentType": mime or "application/octet-stream",
|
|
147
|
+
"sizeBytes": path.stat().st_size,
|
|
148
|
+
})
|
|
149
|
+
print(json.dumps({"files": files}))
|
|
150
|
+
print(f"files: {len(files)}", file=__import__('sys').stderr)
|
|
151
|
+
PY
|
|
152
|
+
|
|
153
|
+
curl -s -X POST "$CFM_DEPLOY_API_BASE/deploy/start" \
|
|
154
|
+
-H "Authorization: Bearer $CFM_INSTANCE_ID" \
|
|
155
|
+
-H "Content-Type: application/json" \
|
|
156
|
+
-d @/tmp/deploy-manifest.json \
|
|
157
|
+
| tee /tmp/deploy-start.json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Success:** JSON with `deployId`, `s3Prefix`, `uploads[]`.
|
|
161
|
+
|
|
162
|
+
| Error | Fix |
|
|
163
|
+
|-------|-----|
|
|
164
|
+
| `401 TOKEN_EXPIRED` | Ask client for fresh instanceId JWT |
|
|
165
|
+
| `503 DEPLOY_UNAVAILABLE` | EC2 hosting env misconfigured — report to operator |
|
|
166
|
+
| `400 files must not exceed 500` | Reduce build output |
|
|
167
|
+
|
|
168
|
+
### Step 2 — Channel 2: upload every file to S3
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
python3 <<'PY'
|
|
172
|
+
import json, os, subprocess
|
|
173
|
+
from pathlib import Path
|
|
174
|
+
|
|
175
|
+
out = Path(os.environ["OUT_DIR"])
|
|
176
|
+
start = json.load(open("/tmp/deploy-start.json"))
|
|
177
|
+
manifest = {f["path"]: f["contentType"] for f in json.load(open("/tmp/deploy-manifest.json"))["files"]}
|
|
178
|
+
failed = []
|
|
179
|
+
|
|
180
|
+
for item in start["uploads"]:
|
|
181
|
+
path = item["path"]
|
|
182
|
+
url = item["uploadUrl"]
|
|
183
|
+
mime = manifest.get(path, "application/octet-stream")
|
|
184
|
+
proc = subprocess.run(
|
|
185
|
+
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
|
|
186
|
+
"-X", "PUT", url, "-H", f"Content-Type: {mime}",
|
|
187
|
+
"--data-binary", f"@{out / path}"],
|
|
188
|
+
capture_output=True, text=True,
|
|
189
|
+
)
|
|
190
|
+
code = proc.stdout.strip()
|
|
191
|
+
mark = "OK" if code == "200" else "FAIL"
|
|
192
|
+
print(f"{mark} {code} {path}")
|
|
193
|
+
if code != "200":
|
|
194
|
+
failed.append(path)
|
|
195
|
+
|
|
196
|
+
if failed:
|
|
197
|
+
print(f"\n{len(failed)} failed — rerun from Step 1 (presigned URLs expire)")
|
|
198
|
+
raise SystemExit(1)
|
|
199
|
+
print("\nAll uploads succeeded")
|
|
200
|
+
PY
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Success:** every line shows `OK 200`.
|
|
204
|
+
|
|
205
|
+
### Step 3 — Channel 3: complete + live URL
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
DEPLOY_ID=$(python3 -c "import json; print(json.load(open('/tmp/deploy-start.json'))['deployId'])")
|
|
209
|
+
|
|
210
|
+
curl -s -X POST "$CFM_DEPLOY_API_BASE/deploy/complete" \
|
|
211
|
+
-H "Authorization: Bearer $CFM_INSTANCE_ID" \
|
|
212
|
+
-H "Content-Type: application/json" \
|
|
213
|
+
-d "{\"deployId\":\"$DEPLOY_ID\"}" \
|
|
214
|
+
| tee /tmp/deploy-complete.json
|
|
215
|
+
|
|
216
|
+
python3 -c "import json; print('\nLive URL:\n' + json.load(open('/tmp/deploy-complete.json'))['url'])"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Success:**
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"url": "https://<cloudfront-domain>/sites/{surveyId}/{deployId}/index.html",
|
|
224
|
+
"deployId": "...",
|
|
225
|
+
"surveyId": "..."
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Step 4 — Verify
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
LIVE_URL=$(python3 -c "import json; print(json.load(open('/tmp/deploy-complete.json'))['url'])")
|
|
233
|
+
curl -s -o /dev/null -w "index.html: %{http_code}\n" "$LIVE_URL"
|
|
234
|
+
# Optional: grep one CSS href from HTML and curl it under the deploy prefix — expect 200
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Tell the client to open the printed URL in a browser.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## One-Command Wrapper (optional)
|
|
242
|
+
|
|
243
|
+
After build, from project root:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
export CFM_INSTANCE_ID="<JWT>"
|
|
247
|
+
export CFM_DEPLOY_API_BASE="http://43.204.26.213:3000"
|
|
248
|
+
export OUT_DIR="<absolute path to out/ or dist/>"
|
|
249
|
+
|
|
250
|
+
bash templates/deploy-to-aws.sh
|
|
251
|
+
# or: bash node_modules/@explorer02/cfm-survey-sdk/templates/docs/templates/deploy-to-aws.sh
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Repeat Deploys
|
|
257
|
+
|
|
258
|
+
Each deploy creates a **new** `deployId` folder under `sites/{surveyId}/`. Old deploys remain in S3.
|
|
259
|
+
|
|
260
|
+
After UI changes: rebuild → re-run Steps 1–3 (same `CFM_INSTANCE_ID` if same survey).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Troubleshooting
|
|
265
|
+
|
|
266
|
+
| Symptom | Cause | Agent fix |
|
|
267
|
+
|---------|-------|-----------|
|
|
268
|
+
| 403 on `/_next/static/...` | Absolute asset paths | Add `assetPrefix: "./"`, rebuild |
|
|
269
|
+
| 403 on `index.html.txt?_rsc` | Missing RSC payload file | Add `cp out/index.txt out/index.html.txt` to build script |
|
|
270
|
+
| 403 on `*_p.png` / `*_p.jpg` images | Preview URL used | Use `option.imageUrl \|\| option.previewImageUrl` in RankOrderScale |
|
|
271
|
+
| 401 `TOKEN_EXPIRED` | JWT expired | Ask client for new instanceId |
|
|
272
|
+
| 503 `DEPLOY_UNAVAILABLE` | EC2 CloudFront/S3 env wrong | Report to operator |
|
|
273
|
+
| Some PUTs 403 | Presigned URLs expired (default 5 min) | Restart from Step 1; complete within window |
|
|
274
|
+
| Blank page, HTML 200 | JS/CSS not uploaded or wrong paths | Re-check Channel 2 logs |
|
|
275
|
+
| Survey API errors in browser | Deploy JWT ≠ SurveyPage JWT | Align `SURVEY_INSTANCE_ID` |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Future: CLI automation (Phase 7b)
|
|
280
|
+
|
|
281
|
+
`npx cfm-sdk deploy --aws` is **not implemented yet**. Use the manual flow above or `templates/deploy-to-aws.sh` until the CLI ships.
|