@harperfast/template-react-ts-studio 1.12.2 → 1.12.3
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/.agents/skills/harper-best-practices/AGENTS.md +632 -474
- package/.agents/skills/harper-best-practices/rules/automatic-apis.md +131 -55
- package/.agents/skills/harper-best-practices/rules/checking-authentication.md +155 -61
- package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +63 -85
- package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +111 -52
- package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
- package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
- package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +82 -96
- package/.agents/skills/harper-best-practices/rules.manifest.yaml +212 -0
- package/agent/skills/harper-best-practices/AGENTS.md +632 -474
- package/agent/skills/harper-best-practices/rules/automatic-apis.md +131 -55
- package/agent/skills/harper-best-practices/rules/checking-authentication.md +155 -61
- package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +63 -85
- package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +111 -52
- package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
- package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
- package/agent/skills/harper-best-practices/rules/v5-upgrade.md +82 -96
- package/agent/skills/harper-best-practices/rules.manifest.yaml +212 -0
- package/package.json +1 -1
- package/skills-lock.json +1 -1
|
@@ -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:
|
|
12
|
-
inputHash:
|
|
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,
|
|
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**:
|
|
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. **
|
|
43
|
+
3. **Use environment variables for CI/CD**: Instead of `harper login`, export credentials as environment variables before calling `harper deploy`.
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
69
|
+
HarperFast/application-template#semver:v1.0.0
|
|
58
70
|
```
|
|
59
71
|
|
|
60
|
-
|
|
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
|
|
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 `
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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="
|
|
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
|
-
**
|
|
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
|
|
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
|
-
**
|
|
148
|
+
**Deploy a private repo by reference with a provisioned credential:**
|
|
167
149
|
|
|
168
150
|
```bash
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
- `
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
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.
|
|
@@ -14,21 +14,21 @@ metadata:
|
|
|
14
14
|
reference/v5/resources/resource-api.md#`addTo(property: string, value:
|
|
15
15
|
number)`
|
|
16
16
|
- reference/v5/components/javascript-environment.md#Module Loading
|
|
17
|
-
sourceCommit:
|
|
18
|
-
inputHash:
|
|
17
|
+
sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
|
|
18
|
+
inputHash: ce2181ecade6522f
|
|
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
|
|
23
|
+
Instructions for the agent to interact with Harper tables programmatically using the `tables` object, including querying, transactions, and module integration.
|
|
24
24
|
|
|
25
25
|
## When to Use
|
|
26
26
|
|
|
27
|
-
Apply this rule when writing server-side code that reads from or writes to
|
|
27
|
+
Apply this rule when writing server-side Harper component code that reads from or writes to tables directly — bypassing REST endpoints — such as in request handlers, background jobs, timers, or SSR rendering. Use it whenever you need to construct queries with `conditions`, manage transactions explicitly, or perform CRDT-safe mutations.
|
|
28
28
|
|
|
29
29
|
## How It Works
|
|
30
30
|
|
|
31
|
-
1. **Import `tables`**:
|
|
31
|
+
1. **Import `tables` from `harper`**: Access all tables in the default `data` database via the `tables` object. Each table defined with `@table` in `schema.graphql` is a property.
|
|
32
32
|
|
|
33
33
|
```javascript
|
|
34
34
|
import { tables } from 'harper';
|
|
@@ -36,7 +36,7 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
|
|
|
36
36
|
// same as: databases.data.Product
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
2. **Define your schema
|
|
39
|
+
2. **Define your schema with `@table`**: Tables must be declared in `schema.graphql`. Use `@indexed` on attributes you intend to sort or filter efficiently.
|
|
40
40
|
|
|
41
41
|
```graphql
|
|
42
42
|
type Product @table {
|
|
@@ -46,15 +46,7 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
|
|
|
46
46
|
}
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
3. **
|
|
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.
|
|
49
|
+
3. **Use `search(` to query records**: Pass a Query object to `search(`. Iterate results with `for await`.
|
|
58
50
|
|
|
59
51
|
```javascript
|
|
60
52
|
const query = {
|
|
@@ -65,7 +57,7 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
|
|
|
65
57
|
}
|
|
66
58
|
```
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
4. **Build `conditions` arrays to filter**: Each condition object supports these properties:
|
|
69
61
|
|
|
70
62
|
| Property | Description |
|
|
71
63
|
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -75,46 +67,95 @@ Apply this rule when writing server-side code that reads from or writes to Harpe
|
|
|
75
67
|
| `conditions` | Nested conditions array |
|
|
76
68
|
| `operator` | `and` (default) or `or` for the nested `conditions` |
|
|
77
69
|
|
|
78
|
-
|
|
70
|
+
5. **Apply `select` to shape results**: Return only the fields you need. Supports arrays, nested relationship selects, and special properties.
|
|
79
71
|
|
|
80
72
|
```javascript
|
|
81
|
-
// Array of
|
|
73
|
+
// Array of fields
|
|
82
74
|
Product.search({ select: ['name', 'price'] });
|
|
83
75
|
|
|
84
76
|
// Nested relationship select
|
|
85
77
|
Book.get({ id: 42, select: ['id', 'title', { name: 'author', select: ['name'] }] });
|
|
86
78
|
```
|
|
87
79
|
|
|
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
|
|
92
|
-
|
|
93
|
-
7. **Use `addTo`** for concurrent-safe numeric increments (CRDT incrementation, safe across threads and nodes).
|
|
80
|
+
Special `select` values: `$id`, `$updatedtime`, `$distance`.
|
|
94
81
|
|
|
95
|
-
|
|
96
|
-
const record = await Product.update(target.id);
|
|
97
|
-
record.addTo('quantity', -1);
|
|
98
|
-
```
|
|
99
|
-
|
|
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.
|
|
82
|
+
6. **Apply `sort` with an `@indexed` attribute**: Harper uses an index to provide sort order. Sort by an `@indexed` attribute without requiring a condition, or provide at least one condition when sorting by a non-indexed attribute.
|
|
101
83
|
|
|
102
84
|
```javascript
|
|
85
|
+
// Sort by primary key with an open-ended condition to avoid scan error
|
|
103
86
|
Product.search({
|
|
104
87
|
conditions: [{ attribute: 'id', comparator: 'greater_than', value: '' }],
|
|
105
88
|
sort: { attribute: 'id' },
|
|
106
89
|
});
|
|
107
90
|
```
|
|
108
91
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
| `
|
|
92
|
+
Sort object properties:
|
|
93
|
+
|
|
94
|
+
| Property | Description |
|
|
95
|
+
| ------------ | -------------------------------------------------------- |
|
|
96
|
+
| `attribute` | Property name or array for chained relationship property |
|
|
97
|
+
| `descending` | Sort descending if `true` (default: `false`) |
|
|
98
|
+
| `next` | Secondary sort to resolve ties (same structure) |
|
|
99
|
+
|
|
100
|
+
7. **Use `limit` and `offset` for pagination**:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
Product.search({ conditions: [...], limit: 20, offset: 40 });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
8. **Use `explain` and `enforceExecutionOrder` for debugging**:
|
|
107
|
+
- `explain: true` — returns conditions reordered as Harper will execute them.
|
|
108
|
+
- `enforceExecutionOrder: true` — forces conditions to execute in the order supplied, disabling automatic re-ordering.
|
|
109
|
+
|
|
110
|
+
9. **Use `addTo` for concurrent-safe numeric updates**: `addTo` uses CRDT incrementation, safe across threads and nodes.
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
static async post(target, data) {
|
|
114
|
+
const record = await this.update(target.id);
|
|
115
|
+
record.addTo('quantity', -1); // decrement safely across nodes
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
10. **Wrap background work in `transaction()`**: HTTP handlers get a transaction automatically. Use `transaction()` explicitly for timers, background jobs, or any code outside a natural transaction context.
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
import { tables } from 'harper';
|
|
123
|
+
const { MyTable } = tables;
|
|
124
|
+
|
|
125
|
+
if (isMainThread) {
|
|
126
|
+
setInterval(async () => {
|
|
127
|
+
let data = await (await fetch('https://example.com/data')).json();
|
|
128
|
+
transaction(async (txn) => {
|
|
129
|
+
for (let item of data) {
|
|
130
|
+
await MyTable.put(item, txn);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}, 3600000); // every hour
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The `txn` object members:
|
|
138
|
+
|
|
139
|
+
| Member | Type | Description |
|
|
140
|
+
| --------------------- | --------------- | ------------------------------------------------------ |
|
|
141
|
+
| `commit()` | `() => Promise` | Commits the current transaction |
|
|
142
|
+
| `abort()` | `() => void` | Aborts the transaction and resets it |
|
|
143
|
+
| `resetReadSnapshot()` | `() => void` | Resets the read snapshot to the latest committed state |
|
|
144
|
+
| `timestamp` | `number` | Timestamp associated with the current transaction |
|
|
145
|
+
|
|
146
|
+
11. **Understand atomicity boundaries**: All tables within 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.
|
|
147
|
+
|
|
148
|
+
12. **Keep `harper` external in bundlers**: When using SSR bundlers, mark `harper` as external so it resolves to the live runtime. In `vite.config`:
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
ssr: {
|
|
152
|
+
external: ['harper'];
|
|
153
|
+
}
|
|
154
|
+
```
|
|
114
155
|
|
|
115
156
|
## Examples
|
|
116
157
|
|
|
117
|
-
|
|
158
|
+
### Nested conditions query
|
|
118
159
|
|
|
119
160
|
```javascript
|
|
120
161
|
Product.search({
|
|
@@ -131,25 +172,34 @@ Product.search({
|
|
|
131
172
|
});
|
|
132
173
|
```
|
|
133
174
|
|
|
134
|
-
|
|
175
|
+
### Chained attribute reference (join/relationship)
|
|
135
176
|
|
|
136
177
|
```javascript
|
|
137
178
|
Product.search({ conditions: [{ attribute: ['brand', 'name'], value: 'Harper' }] });
|
|
138
179
|
```
|
|
139
180
|
|
|
140
|
-
|
|
181
|
+
### Full CRUD sequence
|
|
141
182
|
|
|
142
183
|
```javascript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
// Create a new record (id auto-generated)
|
|
185
|
+
const created = await Product.create({ name: 'Shirt', price: 9.5 });
|
|
186
|
+
|
|
187
|
+
// Modify the record
|
|
188
|
+
await Product.patch(created.id, { price: Math.round(created.price * 0.8 * 100) / 100 });
|
|
189
|
+
|
|
190
|
+
// Retrieve by primary key
|
|
191
|
+
const record = await Product.get(created.id);
|
|
192
|
+
|
|
193
|
+
// Query with conditions
|
|
194
|
+
const query = {
|
|
195
|
+
conditions: [{ attribute: 'price', comparator: 'less_than', value: 8.0 }],
|
|
196
|
+
};
|
|
197
|
+
for await (const record of Product.search(query)) {
|
|
198
|
+
// process record
|
|
199
|
+
}
|
|
150
200
|
```
|
|
151
201
|
|
|
152
|
-
|
|
202
|
+
### SSR rendering with `tables`
|
|
153
203
|
|
|
154
204
|
```typescript
|
|
155
205
|
import { tables } from 'harper';
|
|
@@ -160,11 +210,20 @@ export async function render(url: string): Promise<string> {
|
|
|
160
210
|
}
|
|
161
211
|
```
|
|
162
212
|
|
|
213
|
+
### Mutable update with `addTo`
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
const product = await Product.update(32);
|
|
217
|
+
product.status = 'active';
|
|
218
|
+
product.subtractFrom('quantity', 1);
|
|
219
|
+
product.save();
|
|
220
|
+
```
|
|
221
|
+
|
|
163
222
|
## Notes
|
|
164
223
|
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
- `
|
|
170
|
-
-
|
|
224
|
+
- `tables` calls run in a trusted server-side context and do **not** automatically apply the target table's role permissions. Enforce authorization in your own application logic.
|
|
225
|
+
- Destructive operations (`update`, `patch`, `delete`) act on live data and are not easily reversible. Always scope with specific `conditions`, validate the affected set before writing, and gate behind authorization controls.
|
|
226
|
+
- Sorting by the bare `@primaryKey` alone with no conditions triggers `HdbError: <attribute> is not indexed and not combined with any other conditions`. Add an open-ended range condition or pass `allowFullScan: true` to permit an unconditional scan.
|
|
227
|
+
- Selecting a relationship field without filtering on it behaves as a **LEFT JOIN**; adding a condition on a related attribute behaves as an **INNER JOIN**.
|
|
228
|
+
- `transaction()` is safe to call defensively — if a transaction is already active on the context, it reuses it and executes the callback immediately.
|
|
229
|
+
- Link the `harper` package for correct typings in standalone component directories: `npm link harper`.
|