@nurix/etna 0.19.62 → 0.19.64
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/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +1 -1
- package/data/harnesses/delivery/harness.json +1 -1
- package/data/harnesses/delivery/hooks/generate-ownership-index.mjs +9 -1
- package/data/harnesses/etna/harness.json +1 -1
- package/data/harnesses/etna/hooks/generate-ownership-index.mjs +9 -1
- package/package.json +2 -2
- package/data/harnesses/code/rules/cloudflare.md +0 -167
package/data/harness-index.json
CHANGED
|
@@ -79,9 +79,17 @@ function field(block, key) {
|
|
|
79
79
|
const raw = m[1].trim();
|
|
80
80
|
if (raw === "") {
|
|
81
81
|
// A bare key may continue as an indented inline array on the next line, or a `- item` block.
|
|
82
|
+
// The array may span several lines, so join through the closing bracket before parsing —
|
|
83
|
+
// reading only the first line yields a bare "[", which parses to [] and makes the whole doc
|
|
84
|
+
// look like it owns nothing, dropping it from the index entirely.
|
|
82
85
|
const next = (lines[i + 1] || "").trim();
|
|
83
86
|
if (next.startsWith("[")) {
|
|
84
|
-
|
|
87
|
+
let joined = "";
|
|
88
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
89
|
+
joined += (joined ? " " : "") + lines[j].trim();
|
|
90
|
+
if (lines[j].includes("]")) break;
|
|
91
|
+
}
|
|
92
|
+
return parseInlineArray(joined);
|
|
85
93
|
}
|
|
86
94
|
const items = [];
|
|
87
95
|
for (let j = i + 1; j < lines.length; j++) {
|
|
@@ -79,9 +79,17 @@ function field(block, key) {
|
|
|
79
79
|
const raw = m[1].trim();
|
|
80
80
|
if (raw === "") {
|
|
81
81
|
// A bare key may continue as an indented inline array on the next line, or a `- item` block.
|
|
82
|
+
// The array may span several lines, so join through the closing bracket before parsing —
|
|
83
|
+
// reading only the first line yields a bare "[", which parses to [] and makes the whole doc
|
|
84
|
+
// look like it owns nothing, dropping it from the index entirely.
|
|
82
85
|
const next = (lines[i + 1] || "").trim();
|
|
83
86
|
if (next.startsWith("[")) {
|
|
84
|
-
|
|
87
|
+
let joined = "";
|
|
88
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
89
|
+
joined += (joined ? " " : "") + lines[j].trim();
|
|
90
|
+
if (lines[j].includes("]")) break;
|
|
91
|
+
}
|
|
92
|
+
return parseInlineArray(joined);
|
|
85
93
|
}
|
|
86
94
|
const items = [];
|
|
87
95
|
for (let j = i + 1; j < lines.length; j++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.64",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"agent",
|
|
43
43
|
"plugin"
|
|
44
44
|
],
|
|
45
|
-
"etnaFingerprint": "
|
|
45
|
+
"etnaFingerprint": "6b7cd94d8ed9"
|
|
46
46
|
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# Cloudflare Worker
|
|
2
|
-
|
|
3
|
-
Conventions for Cloudflare Workers — `wrangler.jsonc` setup, vars and secrets, and code shape (dispatch, errors, modules, WebSockets). Schema design doctrine is the `schema-design` skill.
|
|
4
|
-
|
|
5
|
-
## 1. Project & wrangler.jsonc Setup
|
|
6
|
-
|
|
7
|
-
### 1.1 Wrangler version
|
|
8
|
-
|
|
9
|
-
- Use Wrangler **v4** — v3's bundled config schema is missing newer keys (e.g. `secrets_store_secrets`), so editors flag valid config as invalid.
|
|
10
|
-
- **No `package.json`** (dep-free worker): use `npx wrangler@4` in every command and doc — the `@4` is the pin. Don't create a `package.json` just to record it.
|
|
11
|
-
- **`package.json` exists**: pin wrangler to the exact version tested against (not `^4.0.0`), reinstall, and verify with `wrangler deploy --dry-run` before pushing.
|
|
12
|
-
|
|
13
|
-
### 1.2 Config format
|
|
14
|
-
|
|
15
|
-
- Use `wrangler.jsonc` (not `wrangler.toml`).
|
|
16
|
-
- Include `"$schema": "node_modules/wrangler/config-schema.json"` so the editor validates against the exact installed wrangler version.
|
|
17
|
-
|
|
18
|
-
### 1.3 Comments in jsonc
|
|
19
|
-
|
|
20
|
-
None by default. Keep a comment only when it captures a non-obvious **WHY** — a hidden constraint, workaround, or account-specific quirk. Never narrate what a key already says.
|
|
21
|
-
|
|
22
|
-
### 1.4 workers.dev URLs
|
|
23
|
-
|
|
24
|
-
- Set `"workers_dev": false` once a custom domain is wired up, so the custom domain is the only entry point.
|
|
25
|
-
- Pair with a `routes` entry using `custom_domain: true` so Cloudflare manages the DNS record and cert.
|
|
26
|
-
|
|
27
|
-
### 1.5 Observability
|
|
28
|
-
|
|
29
|
-
Default block for every Worker:
|
|
30
|
-
|
|
31
|
-
```jsonc
|
|
32
|
-
"observability": {
|
|
33
|
-
"logs": {
|
|
34
|
-
"enabled": false,
|
|
35
|
-
"invocation_logs": true
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
`enabled: false` avoids per-log billing; `invocation_logs: true` keeps the cheap per-request audit trail. **Bootstrap override:** a new Worker (especially cron-only) runs with `enabled: true` until a few real runs look clean — with logs off, `console.log` is visible only live via `npx wrangler tail` and nothing persists after a failed run. Flip back once stable.
|
|
41
|
-
|
|
42
|
-
### 1.6 Project structure & dependencies
|
|
43
|
-
|
|
44
|
-
- Main entry file: `index.ts`. Reusable helpers: `helper.ts`.
|
|
45
|
-
- TypeScript under `strict: true` — wrangler bundles it natively, no separate build step; a minimal `tsconfig.json` ships even in dep-free Workers.
|
|
46
|
-
- Don't run `npm run build` or `npm run deploy` in examples.
|
|
47
|
-
- **`package.json` / `package-lock.json` are opt-in, not default.** Create them only when code under `src/` imports an npm package at runtime — a Worker on built-in Web/Workers APIs alone needs no manifest; run wrangler, linters, and one-offs via `npx <tool>`. Add both files the moment the first real dep lands. `node_modules/` stays in `.gitignore` either way.
|
|
48
|
-
|
|
49
|
-
### 1.7 Environment & configuration
|
|
50
|
-
|
|
51
|
-
- Use the deployed Cloudflare URL in docs/tests, not `localhost`.
|
|
52
|
-
- Prefer SQLite-based Durable Objects over in-memory-only designs; add classes under `migrations.new_sqlite_classes`.
|
|
53
|
-
|
|
54
|
-
### 1.8 Cloudflare Secrets Store
|
|
55
|
-
|
|
56
|
-
Use when the user mentions it. Use the same `binding` name as the `secret_name`.
|
|
57
|
-
|
|
58
|
-
```jsonc
|
|
59
|
-
"secrets_store_secrets": [
|
|
60
|
-
{ "binding": "CLICKUP_KEY", "store_id": "792206780c0c46e8ba8ade3f21b44eac", "secret_name": "CLICKUP_KEY" }
|
|
61
|
-
]
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Access in code: `await env.CLICKUP_KEY.get()`.
|
|
65
|
-
|
|
66
|
-
### 1.9 Naming
|
|
67
|
-
|
|
68
|
-
- Repo/dir: `cf-<name>` (e.g. `cf-extract-design`).
|
|
69
|
-
- Worker name: `<name>` — repo name minus the `cf-` prefix.
|
|
70
|
-
- R2 bucket, D1 database, and KV namespace names: equal to the worker name. One name, one resource set.
|
|
71
|
-
|
|
72
|
-
### 1.10 Vars & secrets
|
|
73
|
-
|
|
74
|
-
**Non-secret vars live in the `vars` block in `wrangler.jsonc`** — checked in, one source of truth. Vars are configuration, not runtime state; do not move them to the dashboard. A single-tenant application Worker (one deployment — e.g. Apollo) declares them at the top level; a multi-tenant Worker declares per-tenant vars under each `env.<tenant>` block (§11).
|
|
75
|
-
|
|
76
|
-
**Secrets never touch git.** Local dev: a gitignored `.dev.vars` (`KEY=VALUE` dotenv, auto-loaded by `wrangler dev`) with a committed `.dev.vars.example` listing the keys and placeholder values. Production: `wrangler secret put <NAME>`, a gitignored `.secrets` file pushed with `wrangler secret bulk .secrets` (or shipped with a deploy via `wrangler deploy --secrets-file .secrets`), or the Secrets Store (§1.8). Never log or echo a secret value.
|
|
77
|
-
|
|
78
|
-
**No `keep_vars`.** Leave it unset (default `false`) — each deploy reconciles vars to exactly what the config declares. `keep_vars` only guards plaintext vars set out-of-band in the dashboard; it never touches secrets — `wrangler secret put` secrets survive every deploy regardless. Only the multi-tenant pattern sets `keep_vars: true` (§11).
|
|
79
|
-
|
|
80
|
-
**Never name a deployed-value file `.env` or `.env.<anything>`** — `wrangler dev` auto-loads a root `.env` as local dev vars, so a prod-valued `.env` silently becomes the local environment on a fresh clone or in CI. Committed `*.example` files carry keys with placeholder values only.
|
|
81
|
-
|
|
82
|
-
`.gitignore`: `.dev.vars`, `.secrets`, `.secrets.*`, `.env`, `.env.*`
|
|
83
|
-
|
|
84
|
-
## 2. Code & Helpers
|
|
85
|
-
|
|
86
|
-
- Use `generateUuidV7()` in `helper.ts` for IDs (time-sortable UUIDv7).
|
|
87
|
-
- Only `await` what's needed (e.g. `request.json()`, `do.fetch(...)`).
|
|
88
|
-
|
|
89
|
-
## 3. fetch() Structure
|
|
90
|
-
|
|
91
|
-
`validateRequest()` first, then `switch (path)` for dispatch. Single `try/catch` wrapping the entire handler. No if-chains, no regex in fetch.
|
|
92
|
-
|
|
93
|
-
```js
|
|
94
|
-
export default {
|
|
95
|
-
async fetch(request, env, ctx) {
|
|
96
|
-
try {
|
|
97
|
-
const route = validateRequest(request);
|
|
98
|
-
switch (route.path) {
|
|
99
|
-
case "monitors":
|
|
100
|
-
return await handleMonitors({ ...route, env, ctx });
|
|
101
|
-
default:
|
|
102
|
-
throw { code: 404, message: "Route not found" };
|
|
103
|
-
}
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error("Unhandled error in fetch()", error);
|
|
106
|
-
return createErrorResponse(
|
|
107
|
-
error.message || "Internal server error",
|
|
108
|
-
error.code || 500,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
No nested `try/catch` except around WebSocket `send`/`close` (see §9).
|
|
116
|
-
|
|
117
|
-
## 4. validateRequest()
|
|
118
|
-
|
|
119
|
-
Lives in `helper.ts`. Parses the URL in one place and returns `{ path, method, query, resourceId, subPath }`; throws `{ code: 400 }` when a path requires a resource ID and none is present.
|
|
120
|
-
|
|
121
|
-
## 5. Validation
|
|
122
|
-
|
|
123
|
-
Fail fast with thrown `{ code, message }` (`400` bad input, `401` missing/invalid auth). Validate required fields explicitly — no optional chaining for required fields.
|
|
124
|
-
|
|
125
|
-
## 6. Errors & Responses
|
|
126
|
-
|
|
127
|
-
- Throw structured errors: `{ code, message }`. Always `console.error()` with context (path, user, etc.). No empty catch blocks.
|
|
128
|
-
- Response envelope `{ status: 1|0, message, data }` — the shared cross-service contract.
|
|
129
|
-
- `createResponse(data, message, code)` in `helper.ts` bakes CORS in, returns a null body for 204/304, and uses proper HTTP codes; `createErrorResponse(message, code)` delegates to it.
|
|
130
|
-
- CORS is passive: a `CORS_HEADERS` const in `helper.ts`, spread into `createResponse()` — no explicit OPTIONS handler, no wrapper functions. Origin-restricted CORS builds the headers from an allowlist (plus `localhost` in dev), still inside `createResponse()`.
|
|
131
|
-
|
|
132
|
-
## 7. Handler Signatures
|
|
133
|
-
|
|
134
|
-
Single destructured object — never positional args. Compose by spreading route with env/ctx: `handleGetStats({ request, env, resourceId })`, not `handleGetStats(request, id, env)`.
|
|
135
|
-
|
|
136
|
-
## 8. Module Structure
|
|
137
|
-
|
|
138
|
-
Feature-based folders with `handle.ts` + `helper.ts` pairs. `index.ts` only does dispatch.
|
|
139
|
-
|
|
140
|
-
| Path | Role |
|
|
141
|
-
| --- | --- |
|
|
142
|
-
| `src/index.ts` | Entry point — dispatch only |
|
|
143
|
-
| `src/helper.ts` | Shared CORS, responses, validation, utilities |
|
|
144
|
-
| `src/database.ts` | Shared D1 queries |
|
|
145
|
-
| `src/<feature>/handle.ts` | Route handlers |
|
|
146
|
-
| `src/<feature>/helper.ts` | Feature-specific helpers & DB ops |
|
|
147
|
-
|
|
148
|
-
## 9. WebSockets
|
|
149
|
-
|
|
150
|
-
Standard `WebSocketPair` handshake (`server.accept()`, return 101 with `webSocket: client`). Check `readyState === WebSocket.OPEN` before sending. The only acceptable nested `try/catch`: wrap `send`; in its catch, `close(1011)` inside a bare inner try, then evict the session.
|
|
151
|
-
|
|
152
|
-
## 10. D1 Schema
|
|
153
|
-
|
|
154
|
-
Follows the shared database naming and column-type vocabulary; what earns a table and the rest of the design doctrine is the `schema-design` skill.
|
|
155
|
-
|
|
156
|
-
## 11. Multi-tenant Workers (native environments)
|
|
157
|
-
|
|
158
|
-
Rare — only when **one Worker codebase ships as several independent tenants** (separate Workers, domains, and data stores from a single `src/`, e.g. `~/dev/auth` → `auth-nuplay`, `auth-nustack`). Use **native wrangler environments**, never one Worker that switches tenant at runtime. For a single-tenant application Worker, §1.10 applies and this section does not.
|
|
159
|
-
|
|
160
|
-
- **One config, `env.<tenant>` blocks.** The top-level block is **local dev** (`wrangler dev`): a `*-dev` name and placeholder D1/KV ids, simulated locally. Each `env.<tenant>` block is **one production tenant**; deploy it with `wrangler deploy --env <tenant>`.
|
|
161
|
-
- **Named environments never inherit** `vars` / `d1_databases` / `kv_namespaces` / `routes` / `secrets_store_secrets` / `assets` from the top level — **each `env.<tenant>` declares them in full**. Dropping a binding from an env block disables that capability for that one tenant.
|
|
162
|
-
- **Per-tenant non-secret vars live in `env.<tenant>.vars`** — still checked in; one file holds every tenant's config side by side.
|
|
163
|
-
- **Local-dev vars** come from a gitignored `.vars.local`, injected as `--var` by the dev script (the top-level block carries no in-config `vars`).
|
|
164
|
-
- **Per-tenant secrets, never in git** — `wrangler secret put <NAME> --env <tenant>`, or a gitignored per-tenant `.secrets` (e.g. `worker/tenants/<tenant>/.secrets`) pushed with `wrangler secret bulk --env <tenant>`, plus a committed `.secrets.example`. Secrets Store bindings are declared inside each env block.
|
|
165
|
-
- **`keep_vars: true`** (top level) keeps a per-tenant deploy **additive**: tenants inject their non-secret vars via `--var` (or the dashboard), and without it a deploy would wipe them. On secrets it changes nothing (§1.10).
|
|
166
|
-
|
|
167
|
-
`.gitignore`: `.vars.local`, `.secrets`, `.secrets.*`, `.env`, `.env.*`
|