@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.
- package/.agents/skills/harper-best-practices/AGENTS.md +778 -580
- package/.agents/skills/harper-best-practices/rules/adding-tables-with-schemas.md +7 -7
- package/.agents/skills/harper-best-practices/rules/automatic-apis.md +131 -55
- package/.agents/skills/harper-best-practices/rules/checking-authentication.md +265 -127
- package/.agents/skills/harper-best-practices/rules/custom-resources.md +28 -45
- 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 +125 -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 +273 -2
- package/.agents/skills/harper-mcp/AGENTS.md +77 -31
- package/.agents/skills/harper-mcp/rules/custom-mcp-tools.md +79 -33
- package/.agents/skills/harper-mcp/rules.manifest.yaml +1 -1
- package/agent/skills/harper-best-practices/AGENTS.md +778 -580
- package/agent/skills/harper-best-practices/rules/adding-tables-with-schemas.md +7 -7
- package/agent/skills/harper-best-practices/rules/automatic-apis.md +131 -55
- package/agent/skills/harper-best-practices/rules/checking-authentication.md +265 -127
- package/agent/skills/harper-best-practices/rules/custom-resources.md +28 -45
- 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 +125 -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 +273 -2
- package/agent/skills/harper-mcp/AGENTS.md +77 -31
- package/agent/skills/harper-mcp/rules/custom-mcp-tools.md +79 -33
- package/agent/skills/harper-mcp/rules.manifest.yaml +1 -1
- package/package.json +1 -1
- 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:
|
|
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.
|
|
@@ -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
|
|
17
|
-
sourceCommit:
|
|
18
|
-
inputHash:
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
36
|
+
// equivalent to: databases.data.Product
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
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. **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
75
|
+
// Scalar fields only
|
|
82
76
|
Product.search({ select: ['name', 'price'] });
|
|
83
77
|
|
|
84
|
-
//
|
|
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
|
-
|
|
84
|
+
6. **Paginate with `limit` and `offset`**:
|
|
94
85
|
|
|
95
86
|
```javascript
|
|
96
|
-
|
|
97
|
-
record.addTo('quantity', -1);
|
|
87
|
+
Product.search({ conditions: [...], limit: 20, offset: 40 });
|
|
98
88
|
```
|
|
99
89
|
|
|
100
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
199
|
+
### Background job with `transaction()`
|
|
141
200
|
|
|
142
201
|
```javascript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
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.
|