@harperfast/template-react-ts-studio 1.12.2 → 1.12.4

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 (30) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +778 -580
  2. package/.agents/skills/harper-best-practices/rules/adding-tables-with-schemas.md +7 -7
  3. package/.agents/skills/harper-best-practices/rules/automatic-apis.md +131 -55
  4. package/.agents/skills/harper-best-practices/rules/checking-authentication.md +265 -127
  5. package/.agents/skills/harper-best-practices/rules/custom-resources.md +28 -45
  6. package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +63 -85
  7. package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +125 -52
  8. package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
  9. package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
  10. package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +82 -96
  11. package/.agents/skills/harper-best-practices/rules.manifest.yaml +273 -2
  12. package/.agents/skills/harper-mcp/AGENTS.md +77 -31
  13. package/.agents/skills/harper-mcp/rules/custom-mcp-tools.md +79 -33
  14. package/.agents/skills/harper-mcp/rules.manifest.yaml +1 -1
  15. package/agent/skills/harper-best-practices/AGENTS.md +778 -580
  16. package/agent/skills/harper-best-practices/rules/adding-tables-with-schemas.md +7 -7
  17. package/agent/skills/harper-best-practices/rules/automatic-apis.md +131 -55
  18. package/agent/skills/harper-best-practices/rules/checking-authentication.md +265 -127
  19. package/agent/skills/harper-best-practices/rules/custom-resources.md +28 -45
  20. package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +63 -85
  21. package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +125 -52
  22. package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
  23. package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
  24. package/agent/skills/harper-best-practices/rules/v5-upgrade.md +82 -96
  25. package/agent/skills/harper-best-practices/rules.manifest.yaml +273 -2
  26. package/agent/skills/harper-mcp/AGENTS.md +77 -31
  27. package/agent/skills/harper-mcp/rules/custom-mcp-tools.md +79 -33
  28. package/agent/skills/harper-mcp/rules.manifest.yaml +1 -1
  29. package/package.json +1 -1
  30. package/skills-lock.json +2 -2
@@ -8,17 +8,17 @@ metadata:
8
8
  - >-
9
9
  fabric/cluster-creation-management.md#Connecting the Harper CLI to a
10
10
  Cluster
11
- sourceCommit: 6bf676d4bde158fcd08a81565556a0ee91768274
12
- inputHash: 8c8b2fabac08a561
11
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
12
+ inputHash: e5586ad2bcc7c12a
13
13
  ---
14
14
 
15
15
  # Deploying to Harper Fabric
16
16
 
17
- Instructions for the agent to follow when deploying a Harper application to a remote Harper Fabric cluster.
17
+ Instructions for the agent to follow when deploying a Harper application to a remote Harper Fabric cloud cluster.
18
18
 
19
19
  ## When to Use
20
20
 
21
- Apply this rule when deploying a Harper application to a remote Harper Fabric cluster or any remote Harper instance. This includes first-time deploys, redeployments, rollbacks, and CI/CD pipeline deployments. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) to set up a cluster before deploying.
21
+ Apply this rule when deploying a Harper application to a remote Harper Fabric cluster or any remote Harper instance. This includes first-time deploys, redeployments, rollbacks, CI/CD pipeline deploys, and provisioning credentials for private repositories. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) for setting up the cluster before deploying.
22
22
 
23
23
  ## How It Works
24
24
 
@@ -29,7 +29,7 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
29
29
  # Provide cluster username and password when prompted
30
30
  ```
31
31
 
32
- 2. **Deploy the application**: Run `harper deploy` with the required parameters. After logging in, no credentials need to be repeated.
32
+ 2. **Deploy the application**: After login, run `harper deploy` without repeating credentials. Set `restart=true` and `replicated=true` for a production deploy.
33
33
 
34
34
  ```bash
35
35
  harper deploy \
@@ -40,30 +40,42 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
40
40
  replicated=true
41
41
  ```
42
42
 
43
- 3. **Choose a package source**: Set the `package` parameter to any valid npm dependency value, or omit it to package and upload the current local directory.
43
+ 3. **Use environment variables for CI/CD**: Instead of `harper login`, export credentials as environment variables before calling `harper deploy`.
44
44
 
45
- | Value | Effect |
46
- | ---------------------------------------------------- | ------------------------------------------------ |
47
- | _(omitted)_ | Packages and deploys the current local directory |
48
- | `"@harperdb/status-check"` | npm package |
49
- | `"HarperDB/status-check"` | GitHub shorthand |
50
- | `"https://github.com/HarperDB/status-check"` | GitHub URL |
51
- | `"git+ssh://git@github.com:HarperDB/secret-app.git"` | Private repo via SSH |
52
- | `"https://example.com/application.tar.gz"` | Tarball URL |
45
+ ```bash
46
+ export HARPER_CLI_USERNAME=<username>
47
+ export HARPER_CLI_PASSWORD=<password>
48
+ harper deploy \
49
+ project=<name> \
50
+ package=<package> \
51
+ target=<remote> \
52
+ restart=true \
53
+ replicated=true
54
+ ```
55
+
56
+ 4. **Choose a package source**: The `package` field accepts any valid npm dependency value. Select the form that matches your source:
57
+
58
+ | Source | `package` value |
59
+ | ----------------------- | ---------------------------------------------------- |
60
+ | Current local directory | Omit `package` |
61
+ | npm package | `"@harperdb/status-check"` |
62
+ | GitHub (public) | `"HarperFast/status-check"` or full URL |
63
+ | Private repo (SSH) | `"git+ssh://git@github.com:HarperDB/secret-app.git"` |
64
+ | Tarball | `"https://example.com/application.tar.gz"` |
53
65
 
54
66
  For git tags, use the `semver` directive:
55
67
 
56
68
  ```
57
- HarperDB/application-template#semver:v1.0.0
69
+ HarperFast/application-template#semver:v1.0.0
58
70
  ```
59
71
 
60
- 4. **Deploy by reference (optional)**: Instead of uploading a snapshot, send a pinned git reference so the cluster fetches and builds that exact commit. Use `by_ref=true` to resolve the current commit from the local repository automatically.
72
+ 5. **Deploy by reference for reproducible deploys**: Pass `by_ref=true` to send a pinned git SHA instead of uploading a snapshot. The cluster fetches and builds from that exact commit.
61
73
 
62
74
  ```bash
63
75
  harper deploy by_ref=true restart=true replicated=true
64
76
  ```
65
77
 
66
- Use `ref` to deploy a specific commit, tag, or branch (resolved to a full SHA before sending):
78
+ Use `ref` to target a specific commit, tag, or branch (resolved to a full SHA before sending):
67
79
 
68
80
  ```bash
69
81
  # Deploy a specific tag
@@ -73,53 +85,32 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
73
85
  harper deploy ref=9f8c2a1 restart=true replicated=true
74
86
  ```
75
87
 
76
- **Key `by_ref` parameters:**
77
-
78
- | Parameter | Required | Description |
79
- | ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------- |
80
- | `by_ref` | — | Build the package reference from the local repository |
81
- | `ref` | optional | Deploy a specific commit, tag, or branch instead of `HEAD`. Implies `by_ref`. |
82
- | `credential` | optional | Set to `true` to authenticate the clone with the stored credential for the repository's host. Omit for public repositories. |
83
-
84
- > Commit and push before deploying by reference. The cluster clones from the remote and only sees pushed commits. Run `git fetch` if the unpushed-commit check fires for a commit you know you pushed.
88
+ **Key constraints for `ref` values:**
89
+ - Must name something a clone can fetch: `refs/heads/*` and `refs/tags/*`, or a bare branch or tag name.
90
+ - Anything else (e.g., `refs/pull/123/head`) is rejected up front.
91
+ - If a ref can't be resolved, the deploy stops — run `git fetch` and retry, or pass a full commit SHA.
92
+ - Commit and push before deploying: the cluster clones from the remote and only sees pushed commits.
85
93
 
86
- 5. **Authenticate for CI/CD**: Use environment variables instead of `harper login` for automated pipelines.
94
+ 6. **Deploy private repositories by reference**: Pass `credential=true` alongside `by_ref=true`. The CLI attaches a credentials reference; the cluster resolves the secret in memory at clone time — no token travels in the operation body or lands on disk.
87
95
 
88
96
  ```bash
89
- export HARPER_CLI_USERNAME=<username>
90
- export HARPER_CLI_PASSWORD=<password>
91
- harper deploy \
92
- project=<name> \
93
- package=<package> \
94
- target=<remote> \
95
- restart=true \
96
- replicated=true
97
+ harper deploy by_ref=true credential=true restart=true replicated=true
97
98
  ```
98
99
 
99
- 6. **Provision credentials for private repositories**: Run `harper deploy setup=true` once per component and source to provision a deploy credential. Run this with an administrative credential, not the CI identity.
100
+ 7. **Provision a deploy credential for private sources**: Run `harper deploy setup=true` once per component and source. This is interactive and requires **super_user** — run it with an administrative credential, not the CI identity.
100
101
 
101
102
  ```bash
102
103
  harper deploy setup=true
103
104
  ```
104
105
 
105
- Then pass `credential=true` on subsequent deploys:
106
-
107
- ```bash
108
- harper deploy by_ref=true credential=true restart=true replicated=true
109
- ```
110
-
111
- 7. **Use inline auth for one-off commands (not recommended for production)**: Pass `auth_username` and `auth_password` directly. These take precedence over environment variables and saved login tokens.
106
+ This command:
107
+ 1. Fetches the cluster's public key with `get_secrets_public_key`.
108
+ 2. Encrypts the token locally into an `enc:v1:` envelope.
109
+ 3. Stores only the ciphertext with `set_secret`, in the component-scoped tier.
110
+ 4. Grants the component permission to resolve it with `grant_secret`.
111
+ 5. Prints the `credentials` reference for the deploy to use.
112
112
 
113
- ```bash
114
- harper deploy \
115
- project=<name> \
116
- package=<package> \
117
- auth_username=<username> \
118
- auth_password=<password> \
119
- target=<remote> \
120
- restart=true \
121
- replicated=true
122
- ```
113
+ Use a **fine-grained** personal access token (PAT) scoped to **Contents: Read-only** on the specific repository. Avoid session tokens from `gh` CLI — they typically carry `repo`, `read:org`, `gist`, and `workflow` scopes across your whole account.
123
114
 
124
115
  ## Examples
125
116
 
@@ -129,60 +120,47 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
129
120
  harper login https://my-cluster.harperdbcloud.com
130
121
  harper deploy \
131
122
  project=my-app \
132
- package="@myorg/my-app" \
123
+ package="HarperFast/my-app" \
133
124
  target=https://my-cluster.harperdbcloud.com \
134
125
  restart=true \
135
126
  replicated=true
136
127
  ```
137
128
 
138
- **Deploy current directory as a snapshot:**
129
+ **CI/CD deploy using environment variables:**
139
130
 
140
131
  ```bash
132
+ export HARPER_CLI_USERNAME=admin
133
+ export HARPER_CLI_PASSWORD=secret
141
134
  harper deploy \
142
135
  project=my-app \
136
+ package="HarperFast/my-app" \
143
137
  target=https://my-cluster.harperdbcloud.com \
144
138
  restart=true \
145
139
  replicated=true
146
140
  ```
147
141
 
148
- **Deploy a pinned tag by reference:**
149
-
150
- ```bash
151
- harper deploy ref=v1.2.0 restart=true replicated=true
152
- ```
153
-
154
- **Deploy a private repository by reference with a stored credential:**
155
-
156
- ```bash
157
- harper deploy by_ref=true credential=true restart=true replicated=true
158
- ```
159
-
160
- **GitHub Actions — deploy the pull request head commit explicitly:**
142
+ **Deploy by reference in GitHub Actions (pull request):**
161
143
 
162
144
  ```bash
163
145
  harper deploy ref=${{ github.event.pull_request.head.sha }} restart=true replicated=true
164
146
  ```
165
147
 
166
- **CI/CD deploy using environment variables:**
148
+ **Deploy a private repo by reference with a provisioned credential:**
167
149
 
168
150
  ```bash
169
- export HARPER_CLI_USERNAME=<username>
170
- export HARPER_CLI_PASSWORD=<password>
171
- harper deploy \
172
- project=my-app \
173
- package="HarperDB/my-app#semver:v1.0.0" \
174
- target=https://my-cluster.harperdbcloud.com \
175
- restart=true \
176
- replicated=true
151
+ # Provision once (run as super_user)
152
+ harper deploy setup=true
153
+
154
+ # Deploy subsequently
155
+ harper deploy by_ref=true credential=true restart=true replicated=true
177
156
  ```
178
157
 
179
158
  ## Notes
180
159
 
181
- - `harper login` stores an authentication token so subsequent `harper deploy` commands do not require credentials to be repeated.
182
- - Inline `auth_username`/`auth_password` parameters take precedence over environment variables, which take precedence over saved login tokens.
183
- - For SSH-based private repos, register keys with the `add_ssh_key` operation before deploying.
184
- - `by_ref` deploys build from source on each cluster node. If your application requires a build step that cannot run on the node, deploy a built payload (omit `by_ref`) instead.
185
- - `harper deploy setup=true` requires **super_user** privileges. Provision credentials with an administrative account, not the CI identity.
186
- - Tags and branches passed to `ref` are resolved to a full commit SHA locally before the deploy is sent. If resolution fails, run `git fetch` and retry, or pass a full commit SHA directly.
187
- - The `refs/pull/<n>/head` style refs are rejected; use `ref=${{ github.event.pull_request.head.sha }}` in GitHub Actions pull request workflows instead.
188
- - Deploy credentials provisioned via `harper deploy setup=true` are stored scoped to the component and reused on every subsequent deploy, including rollbacks.
160
+ - `auth_username` and `auth_password` can be passed directly as deploy parameters for one-off commands, but this is not recommended for production. Dedicated authentication parameters take precedence over environment variables and saved login tokens.
161
+ - The `enc:v1:` envelope means the plaintext token never leaves your machine — only ciphertext is stored and replicated.
162
+ - Deploy credentials are stored scoped to the component, never in the global `processEnv` tier. If a global secret exists at the derived name, it is converted to the component-scoped tier automatically.
163
+ - Because stored credentials are durable, later deploys and rollbacks reuse them without re-entering anything.
164
+ - The unpushed-commit check is skipped under GitHub Actions; the dirty-tree warning still applies.
165
+ - Deploying by reference means the cluster installs and builds from source. If your application requires a build step that cannot run on the node, deploy the built output as a payload deploy instead.
166
+ - For SSH-based private repos, use the `add_ssh_key` operation to register keys before deploying.
@@ -13,30 +13,32 @@ metadata:
13
13
  - >-
14
14
  reference/v5/resources/resource-api.md#`addTo(property: string, value:
15
15
  number)`
16
- - reference/v5/components/javascript-environment.md#Module Loading
17
- sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
18
- inputHash: ace5c3f4a0ad4519
16
+ - reference/v5/components/javascript-environment.md#Module Formats
17
+ sourceCommit: 9e6ecf87dd25bb488ad44dc2876ca6439ccd682e
18
+ inputHash: f5629f73cde74764
19
19
  ---
20
20
 
21
21
  # Programmatic Table Requests
22
22
 
23
- Instructions for the agent to interact with Harper tables programmatically using the `tables` object and its query API.
23
+ Instructions for the agent to interact with Harper tables programmatically using the `tables` object, the Query API, and transactions.
24
24
 
25
25
  ## When to Use
26
26
 
27
- Apply this rule when writing server-side code that reads from or writes to Harper tables directly — for example, in request handlers, background jobs, or SSR rendering — without going through the REST API. Use it whenever you need to construct queries with `conditions`, `select`, `sort`, or `search(`.
27
+ Apply this rule when writing server-side Harper component code that reads from or writes to tables directly — for example, in HTTP handlers, background jobs, timers, or SSR render functions. Use it whenever you need to construct queries with `conditions`, `sort`, `select`, `limit`, or `offset`, or when you need explicit transaction control via `transaction()`.
28
28
 
29
29
  ## How It Works
30
30
 
31
- 1. **Import `tables`**: Pull `tables` from the `harper` package. Each property on `tables` corresponds to a table defined in `schema.graphql`.
31
+ 1. **Import `tables` (and other APIs) from `harper`**: Access every table defined in `schema.graphql` as a named property of `tables`. Each property is the table class implementing the Resource API.
32
32
 
33
33
  ```javascript
34
- import { tables } from 'harper';
34
+ import { tables, transaction } from 'harper';
35
35
  const { Product } = tables;
36
- // same as: databases.data.Product
36
+ // equivalent to: databases.data.Product
37
37
  ```
38
38
 
39
- 2. **Define your schema**: Declare tables with `@table` in `schema.graphql`. Each type becomes a property on `tables`.
39
+ For standalone components, run `npm link harper` so imports resolve to the live runtime.
40
+
41
+ 2. **Define your schema with `@table`**: Tables must be declared in `schema.graphql`. Mark attributes you intend to sort or filter on with `@indexed`.
40
42
 
41
43
  ```graphql
42
44
  type Product @table {
@@ -46,15 +48,7 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
46
48
  }
47
49
  ```
48
50
 
49
- 3. **Create and modify records**: Use `create`, `patch`, and `get` for basic CRUD.
50
-
51
- ```javascript
52
- const created = await Product.create({ name: 'Shirt', price: 9.5 });
53
- await Product.patch(created.id, { price: Math.round(created.price * 0.8 * 100) / 100 });
54
- const record = await Product.get(created.id);
55
- ```
56
-
57
- 4. **Query with `search(`**: Pass a query object to `Product.search(query)`. It returns an async iterable.
51
+ 3. **Query records with `search(`**: Pass a Query object to `search(`. Iterate results with `for await`.
58
52
 
59
53
  ```javascript
60
54
  const query = {
@@ -65,7 +59,7 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
65
59
  }
66
60
  ```
67
61
 
68
- 5. **Build `conditions`**: Each condition object filters records. Nest conditions with `operator` for boolean logic.
62
+ 4. **Build `conditions`**: Each condition object supports these properties:
69
63
 
70
64
  | Property | Description |
71
65
  | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -75,29 +69,31 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
75
69
  | `conditions` | Nested conditions array |
76
70
  | `operator` | `and` (default) or `or` for the nested `conditions` |
77
71
 
78
- 6. **Use `select`** to control which properties are returned. Accepts an array of property names, a string for a single property, or nested objects for relationships.
72
+ 5. **Control result shape with `select`**: Pass an array of property names, a single string, or nested objects for relationships.
79
73
 
80
74
  ```javascript
81
- // Array of names
75
+ // Scalar fields only
82
76
  Product.search({ select: ['name', 'price'] });
83
77
 
84
- // Nested relationship select
78
+ // Partial related record
85
79
  Book.get({ id: 42, select: ['id', 'title', { name: 'author', select: ['name'] }] });
86
80
  ```
87
81
 
88
- Special `select` values:
89
- - `$id` — returns the primary key regardless of its name
90
- - `$updatedtime` — returns the last-updated timestamp
91
- - `$distance` — returns the computed distance when querying a vector index
82
+ Special `select` values: `$id`, `$updatedtime`, `$distance`.
92
83
 
93
- 7. **Use `addTo`** for concurrent-safe numeric increments (CRDT incrementation, safe across threads and nodes).
84
+ 6. **Paginate with `limit` and `offset`**:
94
85
 
95
86
  ```javascript
96
- const record = await Product.update(target.id);
97
- record.addTo('quantity', -1);
87
+ Product.search({ conditions: [...], limit: 20, offset: 40 });
98
88
  ```
99
89
 
100
- 8. **Apply `sort`, `limit`, and `offset`** for ordering and pagination. A `sort` attribute must be `@indexed`, or at least one `conditions` entry must be present. Pass `allowFullScan: true` to permit an unconditional ordered scan.
90
+ 7. **Sort with `sort`**: The `sort` object accepts `attribute`, `descending` (default `false`), and `next` for tie-breaking. Harper uses an index for sort order — the sort `attribute` must be `@indexed`, **or** at least one entry in `conditions` must be present.
91
+
92
+ Sorting by a non-indexed attribute with zero conditions raises:
93
+
94
+ > `HdbError: <attribute> is not indexed and not combined with any other conditions`
95
+
96
+ Note: `@primaryKey` alone is treated as not indexed for sort purposes. To scan the whole table in primary-key order, add an open-ended condition:
101
97
 
102
98
  ```javascript
103
99
  Product.search({
@@ -106,15 +102,78 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
106
102
  });
107
103
  ```
108
104
 
109
- | Sort property | Description |
110
- | ------------- | ---------------------------------------------------------- |
111
- | `attribute` | Property name (or array for chained relationship property) |
112
- | `descending` | Sort descending if `true` (default: `false`) |
113
- | `next` | Secondary sort to resolve ties (same structure) |
105
+ Alternatively, pass `allowFullScan: true` to permit an unconditional ordered scan, or omit `sort` entirely to iterate without an index requirement.
106
+
107
+ 8. **Debug query planning with `explain` and `enforceExecutionOrder`**:
108
+ - `explain: true` — returns conditions reordered as Harper will execute them.
109
+ - `enforceExecutionOrder: true` — forces conditions to execute in the order supplied, disabling automatic re-ordering.
110
+
111
+ 9. **Use `addTo` for concurrent-safe numeric updates**: `addTo` uses CRDT incrementation, safe across threads and nodes.
112
+
113
+ ```javascript
114
+ const record = await Product.update(32);
115
+ record.addTo('quantity', -1);
116
+ ```
117
+
118
+ 10. **Wrap background work in `transaction()`**: HTTP handlers get a transaction automatically. Use `transaction()` explicitly for timers, background jobs, or any code outside a request context. Always `await` the call and `catch` errors.
119
+
120
+ ```javascript
121
+ await transaction(async (txn) => {
122
+ for (let item of data) {
123
+ await MyTable.put(item, txn);
124
+ }
125
+ });
126
+ ```
127
+
128
+ The `txn` object exposes:
129
+
130
+ | Member | Description |
131
+ | --------------------- | ------------------------------------------------------ |
132
+ | `commit()` | Commits the current transaction |
133
+ | `abort()` | Aborts and resets the transaction |
134
+ | `resetReadSnapshot()` | Resets the read snapshot to the latest committed state |
135
+ | `timestamp` | Timestamp associated with the current transaction |
136
+
137
+ **Atomicity**: all tables in the same database share one transactional context — writes across multiple tables commit atomically. Tables in different databases each get their own transaction with no cross-database atomicity guarantee.
138
+
139
+ If `transaction()` is called with a context that already has an active transaction, it reuses that transaction — safe to call defensively.
140
+
141
+ 11. **Keep `harper` external when bundling for SSR**: In `vite.config`, mark `harper` as external so it resolves to the runtime rather than being bundled.
142
+
143
+ ```javascript
144
+ // vite.config
145
+ ssr: {
146
+ external: ['harper'];
147
+ }
148
+ ```
114
149
 
115
150
  ## Examples
116
151
 
117
- **Nested conditions with `or`:**
152
+ ### Full CRUD sequence
153
+
154
+ ```javascript
155
+ import { tables } from 'harper';
156
+ const { Product } = tables;
157
+
158
+ // Create
159
+ const created = await Product.create({ name: 'Shirt', price: 9.5 });
160
+
161
+ // Patch
162
+ await Product.patch(created.id, { price: Math.round(created.price * 0.8 * 100) / 100 });
163
+
164
+ // Retrieve by primary key
165
+ const record = await Product.get(created.id);
166
+
167
+ // Query with conditions
168
+ const query = {
169
+ conditions: [{ attribute: 'price', comparator: 'less_than', value: 8.0 }],
170
+ };
171
+ for await (const record of Product.search(query)) {
172
+ // process record
173
+ }
174
+ ```
175
+
176
+ ### Nested conditions with `or`
118
177
 
119
178
  ```javascript
120
179
  Product.search({
@@ -131,25 +190,41 @@ Product.search({
131
190
  });
132
191
  ```
133
192
 
134
- **Chained attribute reference (relationship join):**
193
+ ### Chained attribute reference (join/relationship)
135
194
 
136
195
  ```javascript
137
196
  Product.search({ conditions: [{ attribute: ['brand', 'name'], value: 'Harper' }] });
138
197
  ```
139
198
 
140
- **Deep nested `select` across multiple relationships:**
199
+ ### Background job with `transaction()`
141
200
 
142
201
  ```javascript
143
- Product.search({
144
- select: [
145
- 'id',
146
- 'name',
147
- { name: 'segments', select: ['id', 'name', { name: 'client', select: ['id', 'name'] }] },
148
- ],
149
- });
202
+ import { isMainThread } from 'node:worker_threads';
203
+ import { tables, transaction } from 'harper';
204
+ const { MyTable } = tables;
205
+
206
+ if (isMainThread) {
207
+ let running = false;
208
+ setInterval(async () => {
209
+ if (running) return;
210
+ running = true;
211
+ try {
212
+ let data = await (await fetch('https://example.com/data')).json();
213
+ await transaction(async (txn) => {
214
+ for (let item of data) {
215
+ await MyTable.put(item, txn);
216
+ }
217
+ });
218
+ } catch (error) {
219
+ logger.error('hourly import failed', error);
220
+ } finally {
221
+ running = false;
222
+ }
223
+ }, 3600000);
224
+ }
150
225
  ```
151
226
 
152
- **SSR usage — read directly from `tables` in a render function:**
227
+ ### SSR render with `tables`
153
228
 
154
229
  ```typescript
155
230
  import { tables } from 'harper';
@@ -162,9 +237,7 @@ export async function render(url: string): Promise<string> {
162
237
 
163
238
  ## Notes
164
239
 
165
- - Scope destructive operations (`update`, `patch`, `delete`) with specific `conditions` and validate the affected set before writing. These operate on live data and are not easily reversible.
166
- - Sorting by a bare `@primaryKey` with no conditions raises `HdbError: <attribute> is not indexed and not combined with any other conditions`. Add an open-ended condition or pass `allowFullScan: true`.
167
- - Selecting a relationship field without filtering on it behaves as a **LEFT JOIN**. Adding a condition on a related attribute (e.g. `attribute: ['author', 'name']`) behaves as an **INNER JOIN**.
168
- - A to-many relationship resolves to an array; `await` the property before iterating when needed.
169
- - `tables` and `databases.data` reference the same live objects — a record written through one component is immediately visible to all others.
170
- - Keep `harper` external when bundling for SSR (e.g. `ssr: { external: ['harper'] }` in `vite.config`) so it resolves to the runtime.
240
+ - `tables` and `databases` do **not** automatically apply role permissions — calls run in a trusted server-side context. Apply your own authorization controls before exposing results.
241
+ - Destructive operations (`update`, `patch`, `delete`) act on live data and are not easily reversible. Scope them with specific `conditions` and validate the affected set before writing.
242
+ - `tables` is the same live, process-wide object whether accessed as a global or via `import { tables } from 'harper'`. A record written through one component is immediately visible to every other.
243
+ - Run `npm link harper` for components in their own directory to ensure typings match the running installation.