@harperfast/template-vue-ts-studio 1.6.4 → 1.7.1
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 +268 -57
- package/.agents/skills/harper-best-practices/SKILL.md +1 -0
- package/.agents/skills/harper-best-practices/rules/creating-harper-apps.md +5 -2
- package/.agents/skills/harper-best-practices/rules/load-env.md +111 -0
- package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +132 -41
- package/.agents/skills/harper-best-practices/rules/typescript-type-stripping.md +47 -17
- package/.agents/skills/harper-best-practices/rules.manifest.yaml +46 -2
- package/package.json +1 -1
- package/skills-lock.json +1 -1
|
@@ -40,70 +40,153 @@ type ExamplePerson @table @export {
|
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
### 1.2 Schema Design
|
|
43
|
+
### 1.2 Schema Design and Tooling
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Instructions for the agent to follow when designing Harper schemas, applying core directives, and configuring GraphQL tooling.
|
|
46
46
|
|
|
47
|
-
####
|
|
47
|
+
#### When to Use
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Apply this rule when creating or modifying Harper schema files, configuring `graphqlSchema` in `config.yaml`, or deciding which directives to apply to tables and fields. Use it any time a component needs tables, indexes, primary keys, or exported endpoints defined.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
#### How It Works
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
- `@export`: Automatically generates REST and WebSocket APIs for the table.
|
|
55
|
-
- `@table(expiration: Int)`: Configures a time-to-expire for records in the table (useful for caching).
|
|
53
|
+
1. **Create a GraphQL schema file** with Harper-specific directives. Name it (e.g., `schema.graphql`) and place it in your component directory.
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
```graphql
|
|
56
|
+
type Dog @table {
|
|
57
|
+
id: Long @primaryKey
|
|
58
|
+
name: String
|
|
59
|
+
breed: String
|
|
60
|
+
age: Int
|
|
61
|
+
}
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
type Breed @table {
|
|
64
|
+
id: Long @primaryKey
|
|
65
|
+
name: String @indexed
|
|
66
|
+
}
|
|
67
|
+
```
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
2. **Register the schema in `config.yaml`** using the `graphqlSchema` plugin key:
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
```yaml
|
|
72
|
+
graphqlSchema:
|
|
73
|
+
files: 'schema.graphql'
|
|
74
|
+
```
|
|
66
75
|
|
|
67
|
-
|
|
76
|
+
Both plugins and applications can specify schemas this way.
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
3. **Mark every table type with `@table`**. The type name becomes the table name by default. Use optional arguments to override behavior:
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
| Argument | Type | Default | Description |
|
|
81
|
+
| -------------- | --------- | ----------------------------- | ------------------------------------------------------------- |
|
|
82
|
+
| `table` | `String` | type name | Override the table name |
|
|
83
|
+
| `database` | `String` | `"data"` | Database to place the table in |
|
|
84
|
+
| `expiration` | `Int` | — | Seconds until a record goes stale |
|
|
85
|
+
| `eviction` | `Int` | `0` | Additional seconds after `expiration` before physical removal |
|
|
86
|
+
| `scanInterval` | `Int` | `(expiration + eviction) / 4` | Seconds between eviction scans |
|
|
87
|
+
| `replicate` | `Boolean` | `true` | Enable replication of this table |
|
|
72
88
|
|
|
73
|
-
|
|
89
|
+
4. **Designate a primary key on every table** using `@primaryKey`. Primary keys must be unique; duplicate-key inserts are rejected. If no key is provided on insert, Harper auto-generates one:
|
|
90
|
+
- `String` or `ID` → UUID string
|
|
91
|
+
- `Int`, `Long`, or `Any` → auto-incrementing integer
|
|
74
92
|
|
|
75
|
-
|
|
93
|
+
Prefer `Long` or `Any` for auto-generated numeric keys; `Int` is 32-bit and may be insufficient for large tables.
|
|
76
94
|
|
|
77
|
-
|
|
95
|
+
5. **Index fields that need fast querying** with `@indexed`. This is required for filtering by that attribute in REST queries, SQL, or NoSQL operations. If the field value is an array, each element is individually indexed.
|
|
78
96
|
|
|
79
|
-
|
|
97
|
+
```graphql
|
|
98
|
+
type Product @table {
|
|
99
|
+
id: Long @primaryKey
|
|
100
|
+
category: String @indexed
|
|
101
|
+
price: Float @indexed
|
|
102
|
+
}
|
|
103
|
+
```
|
|
80
104
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
6. **Expose a table as an external resource endpoint** with `@export`. This makes the table accessible via REST, MQTT, and other interfaces. The optional `name` parameter sets the URL path segment; without it, the type name is used.
|
|
106
|
+
|
|
107
|
+
```graphql
|
|
108
|
+
type MyTable @table @export(name: "my-table") {
|
|
109
|
+
id: Long @primaryKey
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
7. **Restrict extra properties** with `@sealed` when records must not include attributes beyond those declared. By default, Harper allows additional properties.
|
|
114
|
+
|
|
115
|
+
```graphql
|
|
116
|
+
type StrictRecord @table @sealed {
|
|
117
|
+
id: Long @primaryKey
|
|
118
|
+
name: String
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
8. **Configure expiration, eviction, and scan behavior** together when building caching tables. These three arguments control the full record lifecycle:
|
|
123
|
+
- `expiration` — record becomes stale; next request triggers a source fetch
|
|
124
|
+
- `eviction` — additional time after `expiration` before physical removal
|
|
125
|
+
- `scanInterval` — how often Harper scans for records to evict; clock-aligned, not startup-aligned
|
|
126
|
+
|
|
127
|
+
#### Examples
|
|
128
|
+
|
|
129
|
+
**Caching table with tuned expiration:**
|
|
130
|
+
|
|
131
|
+
```graphql
|
|
132
|
+
# Expire after 5 minutes, evict after 1 hour, scan every 10 minutes
|
|
133
|
+
type WeatherCache @table(expiration: 300, eviction: 3300, scanInterval: 600) {
|
|
134
|
+
id: ID @primaryKey
|
|
135
|
+
temperature: Float
|
|
136
|
+
}
|
|
86
137
|
```
|
|
87
138
|
|
|
88
|
-
|
|
139
|
+
**Table in a named database with expiration and an indexed field:**
|
|
89
140
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
141
|
+
```graphql
|
|
142
|
+
type Event @table(database: "analytics", expiration: 86400) {
|
|
143
|
+
id: Long @primaryKey
|
|
144
|
+
name: String @indexed
|
|
145
|
+
}
|
|
146
|
+
```
|
|
93
147
|
|
|
94
|
-
|
|
148
|
+
**Session cache with auto-expiry:**
|
|
149
|
+
|
|
150
|
+
```graphql
|
|
151
|
+
type Session @table(expiration: 3600) {
|
|
152
|
+
id: Long @primaryKey
|
|
153
|
+
userId: String
|
|
154
|
+
}
|
|
155
|
+
```
|
|
95
156
|
|
|
96
|
-
|
|
157
|
+
**Table with audit timestamps:**
|
|
97
158
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
159
|
+
```graphql
|
|
160
|
+
type Order @table @export(name: "orders") {
|
|
161
|
+
id: Long @primaryKey
|
|
162
|
+
createdAt: Long @createdTime
|
|
163
|
+
updatedAt: Long @updatedTime
|
|
164
|
+
status: String @indexed
|
|
165
|
+
}
|
|
105
166
|
```
|
|
106
167
|
|
|
168
|
+
**Overriding the table name and disabling replication:**
|
|
169
|
+
|
|
170
|
+
```graphql
|
|
171
|
+
type Product @table(table: "products") {
|
|
172
|
+
id: Long @primaryKey
|
|
173
|
+
name: String
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
type LocalRecord @table(replicate: false) {
|
|
177
|
+
id: Long @primaryKey
|
|
178
|
+
value: String
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### Notes
|
|
183
|
+
|
|
184
|
+
- Use unique `database` names in plugins and applications to avoid table naming collisions, since all tables default to the `"data"` database.
|
|
185
|
+
- Eviction removes non-indexed record data but does **not** remove a record from its secondary indexes. Indexes remain functional for evicted records; Harper fetches the full record from the source on demand when a query matches an evicted record.
|
|
186
|
+
- `scanInterval` is clock-aligned to the server's local timezone. The server's startup time does not affect when eviction runs.
|
|
187
|
+
- If replication is disabled on a table and later re-enabled, it will not catch up on writes made while replication was disabled.
|
|
188
|
+
- Null values are indexed by `@indexed` fields, enabling queries such as `GET /Product/?category=null`.
|
|
189
|
+
|
|
107
190
|
### 1.3 Defining Relationships
|
|
108
191
|
|
|
109
192
|
Instructions for the agent to follow when defining relationships between Harper tables.
|
|
@@ -1138,34 +1221,60 @@ for await (const record of tables.Product.search({
|
|
|
1138
1221
|
|
|
1139
1222
|
Be very careful when performing updates and deletions! You may be dealing with live production data. The wrong request to delete, without approval from a human, could be devastating to a business. Always use the proper approval process.
|
|
1140
1223
|
|
|
1141
|
-
### 3.4 TypeScript Type Stripping
|
|
1224
|
+
### 3.4 TypeScript Type Stripping in Harper
|
|
1142
1225
|
|
|
1143
|
-
Instructions for the agent to
|
|
1226
|
+
Instructions for the agent to run `.ts` files directly in Harper without a build step using Node.js's built-in type stripping.
|
|
1144
1227
|
|
|
1145
1228
|
#### When to Use
|
|
1146
1229
|
|
|
1147
|
-
|
|
1230
|
+
Apply this rule when writing Harper resource files in TypeScript. Use it any time you need to reference `.ts` source files from `config.yaml` or import between local TypeScript modules in a Harper project.
|
|
1148
1231
|
|
|
1149
1232
|
#### How It Works
|
|
1150
1233
|
|
|
1151
|
-
1. **
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
import { Resource } from 'harper';
|
|
1156
|
-
export class MyResource extends Resource {
|
|
1157
|
-
async get(): Promise<{ message: string }> {
|
|
1158
|
-
return { message: 'Running TS directly!' };
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
```
|
|
1162
|
-
4. **Use Explicit Extensions in Imports**: When importing other local modules, include the `.ts` extension: `import { helper } from './helper.ts'`.
|
|
1163
|
-
5. **Configure `config.yaml`**: Ensure `jsResource` points to your `.ts` files:
|
|
1234
|
+
1. **Ensure Node.js version**: Require Node.js 22.6 or later. Type stripping is unavailable on earlier versions.
|
|
1235
|
+
|
|
1236
|
+
2. **Point `jsResource` at `.ts` files**: The `jsResource` plugin loads both `.js` and `.ts` files. Set its `files` glob in `config.yaml` to target your `.ts` source files:
|
|
1237
|
+
|
|
1164
1238
|
```yaml
|
|
1165
1239
|
jsResource:
|
|
1166
1240
|
files: 'resources/*.ts'
|
|
1167
1241
|
```
|
|
1168
1242
|
|
|
1243
|
+
3. **Use explicit `.ts` extensions in local imports**: Node's loader does not resolve `'./helper'` to `'./helper.ts'`, so always include the full extension:
|
|
1244
|
+
|
|
1245
|
+
```typescript
|
|
1246
|
+
import { helper } from './helper.ts';
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
4. **Stay within type-stripping limits**: Only type annotations and declarations are removed. Do not use enums with runtime values, namespaces with runtime semantics, or any other features that require code transformation beyond type stripping.
|
|
1250
|
+
|
|
1251
|
+
#### Examples
|
|
1252
|
+
|
|
1253
|
+
A complete Harper resource written in TypeScript, using imports from the `harper` package:
|
|
1254
|
+
|
|
1255
|
+
```typescript
|
|
1256
|
+
import { type RequestTargetOrId, Resource, tables } from 'harper';
|
|
1257
|
+
|
|
1258
|
+
export class MyResource extends Resource {
|
|
1259
|
+
async get(target?: RequestTargetOrId): Promise<{ message: string }> {
|
|
1260
|
+
return { message: 'Hello from TS' };
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
Paired `config.yaml` entry loading the file via `jsResource`:
|
|
1266
|
+
|
|
1267
|
+
```yaml
|
|
1268
|
+
jsResource:
|
|
1269
|
+
files: 'resources/*.ts'
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
#### Notes
|
|
1273
|
+
|
|
1274
|
+
- No build step or transpiler is required — Harper runs `.ts` files directly.
|
|
1275
|
+
- Type imports (e.g., `import { type RequestTargetOrId }`) from the `harper` package work as usual.
|
|
1276
|
+
- Unsupported TypeScript features include: enums with runtime values, namespaces with runtime semantics, and anything requiring code transformation beyond simple type stripping.
|
|
1277
|
+
|
|
1169
1278
|
### 3.5 Caching External Data Sources in Harper
|
|
1170
1279
|
|
|
1171
1280
|
Instructions for the agent to implement integrated data caching in Harper by wrapping external sources with a cache table and `sourcedFrom`.
|
|
@@ -1456,11 +1565,14 @@ CLI_TARGET='YOUR_CLUSTER_URL'
|
|
|
1456
1565
|
|
|
1457
1566
|
### 4.3 Creating Harper Applications
|
|
1458
1567
|
|
|
1459
|
-
The fastest way to start a new Harper project is using the `create-harper` CLI tool. This command
|
|
1568
|
+
The fastest way to start a new Harper project is using the `create-harper` CLI tool. This command
|
|
1569
|
+
initializes a project with a standard folder structure, essential configuration files, and basic
|
|
1570
|
+
schema definitions.
|
|
1460
1571
|
|
|
1461
1572
|
#### When to Use
|
|
1462
1573
|
|
|
1463
|
-
Use this command when starting a new Harper application or adding a new Harper microservice to an
|
|
1574
|
+
Use this command when starting a new Harper application or adding a new Harper microservice to an
|
|
1575
|
+
existing architecture.
|
|
1464
1576
|
|
|
1465
1577
|
#### Commands
|
|
1466
1578
|
|
|
@@ -1714,3 +1826,102 @@ Tagged entries appear in `hdb.log` with the tag in the header:
|
|
|
1714
1826
|
- `console.log` output is only forwarded to `hdb.log` when `logging.console: true` is explicitly set; it is not forwarded by default.
|
|
1715
1827
|
- When logging to standard streams, run Harper in the foreground (`harper`, not `harper start`).
|
|
1716
1828
|
- `TaggedLogger` is bound to the configured log level at creation time — always use `?.` on its methods.
|
|
1829
|
+
|
|
1830
|
+
### 4.6 Load Environment Variables with loadEnv
|
|
1831
|
+
|
|
1832
|
+
Instructions for the agent to follow when loading environment variables from `.env` files into a Harper application using the `loadEnv` plugin.
|
|
1833
|
+
|
|
1834
|
+
#### When to Use
|
|
1835
|
+
|
|
1836
|
+
Apply this rule when a Harper application needs to load secrets or configuration values from `.env` files into `process.env` at startup. Use it whenever hardcoding values must be avoided and environment-specific configuration must be supplied to Harper components.
|
|
1837
|
+
|
|
1838
|
+
#### How It Works
|
|
1839
|
+
|
|
1840
|
+
1. **Declare `loadEnv` in `config.yaml`**: Add `loadEnv` as a top-level key. It is built into Harper and requires no installation.
|
|
1841
|
+
|
|
1842
|
+
```yaml
|
|
1843
|
+
loadEnv:
|
|
1844
|
+
files: '.env'
|
|
1845
|
+
```
|
|
1846
|
+
|
|
1847
|
+
2. **Place `loadEnv` first**: Always list `loadEnv` before any other components in `config.yaml` so that environment variables are available on `process.env` before dependent components start.
|
|
1848
|
+
|
|
1849
|
+
```yaml
|
|
1850
|
+
# config.yaml — loadEnv must come first
|
|
1851
|
+
loadEnv:
|
|
1852
|
+
files: '.env'
|
|
1853
|
+
|
|
1854
|
+
rest: true
|
|
1855
|
+
|
|
1856
|
+
myApp:
|
|
1857
|
+
files: './src/*.js'
|
|
1858
|
+
```
|
|
1859
|
+
|
|
1860
|
+
3. **Configure the `files` option**: Provide one or more paths or glob patterns pointing to the env files to load. This option is required.
|
|
1861
|
+
|
|
1862
|
+
4. **Set `override` if needed**: By default, existing environment variables take precedence over values in `.env` files. Set `override: true` to reverse this and have loaded values win.
|
|
1863
|
+
|
|
1864
|
+
```yaml
|
|
1865
|
+
loadEnv:
|
|
1866
|
+
files: '.env'
|
|
1867
|
+
override: true
|
|
1868
|
+
```
|
|
1869
|
+
|
|
1870
|
+
5. **Load multiple files when required**: Supply a list of files or a glob pattern. Files are loaded in the order specified.
|
|
1871
|
+
```yaml
|
|
1872
|
+
loadEnv:
|
|
1873
|
+
files:
|
|
1874
|
+
- '.env'
|
|
1875
|
+
- '.env.local'
|
|
1876
|
+
```
|
|
1877
|
+
or
|
|
1878
|
+
```yaml
|
|
1879
|
+
loadEnv:
|
|
1880
|
+
files: 'env-vars/*'
|
|
1881
|
+
```
|
|
1882
|
+
|
|
1883
|
+
##### Configuration Options
|
|
1884
|
+
|
|
1885
|
+
| Option | Type | Required | Description |
|
|
1886
|
+
| ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------- |
|
|
1887
|
+
| `files` | `string \| string[]` | **Yes** | Path(s) or glob pattern(s) to the env file(s) to load. |
|
|
1888
|
+
| `override` | `boolean` | No | If `true`, loaded values override existing environment variables. Defaults to `false`. |
|
|
1889
|
+
|
|
1890
|
+
#### Examples
|
|
1891
|
+
|
|
1892
|
+
**Minimal setup — single `.env` file:**
|
|
1893
|
+
|
|
1894
|
+
```yaml
|
|
1895
|
+
loadEnv:
|
|
1896
|
+
files: '.env'
|
|
1897
|
+
```
|
|
1898
|
+
|
|
1899
|
+
**Full `config.yaml` with load order, multiple files, and override:**
|
|
1900
|
+
|
|
1901
|
+
```yaml
|
|
1902
|
+
# config.yaml — loadEnv must come first
|
|
1903
|
+
loadEnv:
|
|
1904
|
+
files:
|
|
1905
|
+
- '.env'
|
|
1906
|
+
- '.env.local'
|
|
1907
|
+
override: true
|
|
1908
|
+
|
|
1909
|
+
rest: true
|
|
1910
|
+
|
|
1911
|
+
myApp:
|
|
1912
|
+
files: './src/*.js'
|
|
1913
|
+
```
|
|
1914
|
+
|
|
1915
|
+
**Glob pattern:**
|
|
1916
|
+
|
|
1917
|
+
```yaml
|
|
1918
|
+
loadEnv:
|
|
1919
|
+
files: 'env-vars/*'
|
|
1920
|
+
```
|
|
1921
|
+
|
|
1922
|
+
#### Notes
|
|
1923
|
+
|
|
1924
|
+
- `loadEnv` is built into Harper — do not install it separately; only declare it in `config.yaml`.
|
|
1925
|
+
- Because Harper is a single-process application, variables loaded onto `process.env` are shared across all components.
|
|
1926
|
+
- Without `override: true`, variables already set in the shell or container environment will not be overwritten by values in `.env` files.
|
|
1927
|
+
- `files` is the only required option; omitting it will produce an invalid configuration.
|
|
@@ -82,6 +82,7 @@ See the concrete examples embedded in each rule subsection below (GraphQL schema
|
|
|
82
82
|
- `creating-harper-apps` — How to initialize a new Harper application using the CLI.
|
|
83
83
|
- `serving-web-content` — How to serve static files and integrated Vite/React applications in Harper.
|
|
84
84
|
- `logging` — Best practices for logging in Harper, including console capture, the granular logger interface, and programmatic log retrieval.
|
|
85
|
+
- `load-env` — How to load environment variables from .env files into a Harper application using the loadEnv plugin.
|
|
85
86
|
|
|
86
87
|
<!-- END GENERATED INDEX -->
|
|
87
88
|
|
|
@@ -7,11 +7,14 @@ metadata:
|
|
|
7
7
|
|
|
8
8
|
# Creating Harper Applications
|
|
9
9
|
|
|
10
|
-
The fastest way to start a new Harper project is using the `create-harper` CLI tool. This command
|
|
10
|
+
The fastest way to start a new Harper project is using the `create-harper` CLI tool. This command
|
|
11
|
+
initializes a project with a standard folder structure, essential configuration files, and basic
|
|
12
|
+
schema definitions.
|
|
11
13
|
|
|
12
14
|
## When to Use
|
|
13
15
|
|
|
14
|
-
Use this command when starting a new Harper application or adding a new Harper microservice to an
|
|
16
|
+
Use this command when starting a new Harper application or adding a new Harper microservice to an
|
|
17
|
+
existing architecture.
|
|
15
18
|
|
|
16
19
|
## Commands
|
|
17
20
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: load-env
|
|
3
|
+
description: >-
|
|
4
|
+
How to load environment variables from .env files into a Harper application
|
|
5
|
+
using the loadEnv plugin.
|
|
6
|
+
metadata:
|
|
7
|
+
mode: generate
|
|
8
|
+
sources:
|
|
9
|
+
- reference/v5/environment-variables/overview.md
|
|
10
|
+
sourceCommit: 42231db17c025a4a455e3d91875fad4084a01cb5
|
|
11
|
+
inputHash: fe610fc245226357
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Load Environment Variables with loadEnv
|
|
15
|
+
|
|
16
|
+
Instructions for the agent to follow when loading environment variables from `.env` files into a Harper application using the `loadEnv` plugin.
|
|
17
|
+
|
|
18
|
+
## When to Use
|
|
19
|
+
|
|
20
|
+
Apply this rule when a Harper application needs to load secrets or configuration values from `.env` files into `process.env` at startup. Use it whenever hardcoding values must be avoided and environment-specific configuration must be supplied to Harper components.
|
|
21
|
+
|
|
22
|
+
## How It Works
|
|
23
|
+
|
|
24
|
+
1. **Declare `loadEnv` in `config.yaml`**: Add `loadEnv` as a top-level key. It is built into Harper and requires no installation.
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
loadEnv:
|
|
28
|
+
files: '.env'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. **Place `loadEnv` first**: Always list `loadEnv` before any other components in `config.yaml` so that environment variables are available on `process.env` before dependent components start.
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
# config.yaml — loadEnv must come first
|
|
35
|
+
loadEnv:
|
|
36
|
+
files: '.env'
|
|
37
|
+
|
|
38
|
+
rest: true
|
|
39
|
+
|
|
40
|
+
myApp:
|
|
41
|
+
files: './src/*.js'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. **Configure the `files` option**: Provide one or more paths or glob patterns pointing to the env files to load. This option is required.
|
|
45
|
+
|
|
46
|
+
4. **Set `override` if needed**: By default, existing environment variables take precedence over values in `.env` files. Set `override: true` to reverse this and have loaded values win.
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
loadEnv:
|
|
50
|
+
files: '.env'
|
|
51
|
+
override: true
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
5. **Load multiple files when required**: Supply a list of files or a glob pattern. Files are loaded in the order specified.
|
|
55
|
+
```yaml
|
|
56
|
+
loadEnv:
|
|
57
|
+
files:
|
|
58
|
+
- '.env'
|
|
59
|
+
- '.env.local'
|
|
60
|
+
```
|
|
61
|
+
or
|
|
62
|
+
```yaml
|
|
63
|
+
loadEnv:
|
|
64
|
+
files: 'env-vars/*'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Configuration Options
|
|
68
|
+
|
|
69
|
+
| Option | Type | Required | Description |
|
|
70
|
+
| ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------- |
|
|
71
|
+
| `files` | `string \| string[]` | **Yes** | Path(s) or glob pattern(s) to the env file(s) to load. |
|
|
72
|
+
| `override` | `boolean` | No | If `true`, loaded values override existing environment variables. Defaults to `false`. |
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
**Minimal setup — single `.env` file:**
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
loadEnv:
|
|
80
|
+
files: '.env'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Full `config.yaml` with load order, multiple files, and override:**
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
# config.yaml — loadEnv must come first
|
|
87
|
+
loadEnv:
|
|
88
|
+
files:
|
|
89
|
+
- '.env'
|
|
90
|
+
- '.env.local'
|
|
91
|
+
override: true
|
|
92
|
+
|
|
93
|
+
rest: true
|
|
94
|
+
|
|
95
|
+
myApp:
|
|
96
|
+
files: './src/*.js'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Glob pattern:**
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
loadEnv:
|
|
103
|
+
files: 'env-vars/*'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Notes
|
|
107
|
+
|
|
108
|
+
- `loadEnv` is built into Harper — do not install it separately; only declare it in `config.yaml`.
|
|
109
|
+
- Because Harper is a single-process application, variables loaded onto `process.env` are shared across all components.
|
|
110
|
+
- Without `override: true`, variables already set in the shell or container environment will not be overwritten by values in `.env` files.
|
|
111
|
+
- `files` is the only required option; omitting it will produce an invalid configuration.
|
|
@@ -1,70 +1,161 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: schema-design-tooling
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Best practices for Harper schema design, including core directives and GraphQL
|
|
5
|
+
tooling configuration.
|
|
4
6
|
metadata:
|
|
5
|
-
mode:
|
|
7
|
+
mode: generate
|
|
8
|
+
sources:
|
|
9
|
+
- reference/v5/database/schema.md#Overview
|
|
10
|
+
- reference/v5/database/schema.md#Type Directives
|
|
11
|
+
- reference/v5/database/schema.md#Field Directives
|
|
12
|
+
sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
|
|
13
|
+
inputHash: 4faa3baed7cfa854
|
|
6
14
|
---
|
|
7
15
|
|
|
8
|
-
# Schema Design
|
|
16
|
+
# Schema Design and Tooling
|
|
9
17
|
|
|
10
|
-
|
|
18
|
+
Instructions for the agent to follow when designing Harper schemas, applying core directives, and configuring GraphQL tooling.
|
|
11
19
|
|
|
12
|
-
##
|
|
20
|
+
## When to Use
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
Apply this rule when creating or modifying Harper schema files, configuring `graphqlSchema` in `config.yaml`, or deciding which directives to apply to tables and fields. Use it any time a component needs tables, indexes, primary keys, or exported endpoints defined.
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
## How It Works
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
- `@export`: Automatically generates REST and WebSocket APIs for the table.
|
|
20
|
-
- `@table(expiration: Int)`: Configures a time-to-expire for records in the table (useful for caching).
|
|
26
|
+
1. **Create a GraphQL schema file** with Harper-specific directives. Name it (e.g., `schema.graphql`) and place it in your component directory.
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
```graphql
|
|
29
|
+
type Dog @table {
|
|
30
|
+
id: Long @primaryKey
|
|
31
|
+
name: String
|
|
32
|
+
breed: String
|
|
33
|
+
age: Int
|
|
34
|
+
}
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
type Breed @table {
|
|
37
|
+
id: Long @primaryKey
|
|
38
|
+
name: String @indexed
|
|
39
|
+
}
|
|
40
|
+
```
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
2. **Register the schema in `config.yaml`** using the `graphqlSchema` plugin key:
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
```yaml
|
|
45
|
+
graphqlSchema:
|
|
46
|
+
files: 'schema.graphql'
|
|
47
|
+
```
|
|
31
48
|
|
|
32
|
-
|
|
49
|
+
Both plugins and applications can specify schemas this way.
|
|
33
50
|
|
|
34
|
-
|
|
51
|
+
3. **Mark every table type with `@table`**. The type name becomes the table name by default. Use optional arguments to override behavior:
|
|
35
52
|
|
|
36
|
-
|
|
53
|
+
| Argument | Type | Default | Description |
|
|
54
|
+
| -------------- | --------- | ----------------------------- | ------------------------------------------------------------- |
|
|
55
|
+
| `table` | `String` | type name | Override the table name |
|
|
56
|
+
| `database` | `String` | `"data"` | Database to place the table in |
|
|
57
|
+
| `expiration` | `Int` | — | Seconds until a record goes stale |
|
|
58
|
+
| `eviction` | `Int` | `0` | Additional seconds after `expiration` before physical removal |
|
|
59
|
+
| `scanInterval` | `Int` | `(expiration + eviction) / 4` | Seconds between eviction scans |
|
|
60
|
+
| `replicate` | `Boolean` | `true` | Enable replication of this table |
|
|
37
61
|
|
|
38
|
-
|
|
62
|
+
4. **Designate a primary key on every table** using `@primaryKey`. Primary keys must be unique; duplicate-key inserts are rejected. If no key is provided on insert, Harper auto-generates one:
|
|
63
|
+
- `String` or `ID` → UUID string
|
|
64
|
+
- `Int`, `Long`, or `Any` → auto-incrementing integer
|
|
39
65
|
|
|
40
|
-
|
|
66
|
+
Prefer `Long` or `Any` for auto-generated numeric keys; `Int` is 32-bit and may be insufficient for large tables.
|
|
41
67
|
|
|
42
|
-
|
|
68
|
+
5. **Index fields that need fast querying** with `@indexed`. This is required for filtering by that attribute in REST queries, SQL, or NoSQL operations. If the field value is an array, each element is individually indexed.
|
|
43
69
|
|
|
44
|
-
|
|
70
|
+
```graphql
|
|
71
|
+
type Product @table {
|
|
72
|
+
id: Long @primaryKey
|
|
73
|
+
category: String @indexed
|
|
74
|
+
price: Float @indexed
|
|
75
|
+
}
|
|
76
|
+
```
|
|
45
77
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
6. **Expose a table as an external resource endpoint** with `@export`. This makes the table accessible via REST, MQTT, and other interfaces. The optional `name` parameter sets the URL path segment; without it, the type name is used.
|
|
79
|
+
|
|
80
|
+
```graphql
|
|
81
|
+
type MyTable @table @export(name: "my-table") {
|
|
82
|
+
id: Long @primaryKey
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
7. **Restrict extra properties** with `@sealed` when records must not include attributes beyond those declared. By default, Harper allows additional properties.
|
|
87
|
+
|
|
88
|
+
```graphql
|
|
89
|
+
type StrictRecord @table @sealed {
|
|
90
|
+
id: Long @primaryKey
|
|
91
|
+
name: String
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
8. **Configure expiration, eviction, and scan behavior** together when building caching tables. These three arguments control the full record lifecycle:
|
|
96
|
+
- `expiration` — record becomes stale; next request triggers a source fetch
|
|
97
|
+
- `eviction` — additional time after `expiration` before physical removal
|
|
98
|
+
- `scanInterval` — how often Harper scans for records to evict; clock-aligned, not startup-aligned
|
|
99
|
+
|
|
100
|
+
## Examples
|
|
101
|
+
|
|
102
|
+
**Caching table with tuned expiration:**
|
|
103
|
+
|
|
104
|
+
```graphql
|
|
105
|
+
# Expire after 5 minutes, evict after 1 hour, scan every 10 minutes
|
|
106
|
+
type WeatherCache @table(expiration: 300, eviction: 3300, scanInterval: 600) {
|
|
107
|
+
id: ID @primaryKey
|
|
108
|
+
temperature: Float
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Table in a named database with expiration and an indexed field:**
|
|
113
|
+
|
|
114
|
+
```graphql
|
|
115
|
+
type Event @table(database: "analytics", expiration: 86400) {
|
|
116
|
+
id: Long @primaryKey
|
|
117
|
+
name: String @indexed
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Session cache with auto-expiry:**
|
|
122
|
+
|
|
123
|
+
```graphql
|
|
124
|
+
type Session @table(expiration: 3600) {
|
|
125
|
+
id: Long @primaryKey
|
|
126
|
+
userId: String
|
|
127
|
+
}
|
|
51
128
|
```
|
|
52
129
|
|
|
53
|
-
|
|
130
|
+
**Table with audit timestamps:**
|
|
54
131
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
132
|
+
```graphql
|
|
133
|
+
type Order @table @export(name: "orders") {
|
|
134
|
+
id: Long @primaryKey
|
|
135
|
+
createdAt: Long @createdTime
|
|
136
|
+
updatedAt: Long @updatedTime
|
|
137
|
+
status: String @indexed
|
|
138
|
+
}
|
|
139
|
+
```
|
|
58
140
|
|
|
59
|
-
|
|
141
|
+
**Overriding the table name and disabling replication:**
|
|
60
142
|
|
|
61
|
-
|
|
143
|
+
```graphql
|
|
144
|
+
type Product @table(table: "products") {
|
|
145
|
+
id: Long @primaryKey
|
|
146
|
+
name: String
|
|
147
|
+
}
|
|
62
148
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
├── package.json
|
|
68
|
-
├── schema.graphql
|
|
69
|
-
└── resources.js
|
|
149
|
+
type LocalRecord @table(replicate: false) {
|
|
150
|
+
id: Long @primaryKey
|
|
151
|
+
value: String
|
|
152
|
+
}
|
|
70
153
|
```
|
|
154
|
+
|
|
155
|
+
## Notes
|
|
156
|
+
|
|
157
|
+
- Use unique `database` names in plugins and applications to avoid table naming collisions, since all tables default to the `"data"` database.
|
|
158
|
+
- Eviction removes non-indexed record data but does **not** remove a record from its secondary indexes. Indexes remain functional for evicted records; Harper fetches the full record from the source on demand when a query matches an evicted record.
|
|
159
|
+
- `scanInterval` is clock-aligned to the server's local timezone. The server's startup time does not affect when eviction runs.
|
|
160
|
+
- If replication is disabled on a table and later re-enabled, it will not catch up on writes made while replication was disabled.
|
|
161
|
+
- Null values are indexed by `@indexed` fields, enabling queries such as `GET /Product/?category=null`.
|
|
@@ -2,33 +2,63 @@
|
|
|
2
2
|
name: typescript-type-stripping
|
|
3
3
|
description: How to run TypeScript files directly in Harper without a build step.
|
|
4
4
|
metadata:
|
|
5
|
-
mode:
|
|
5
|
+
mode: generate
|
|
6
|
+
sources:
|
|
7
|
+
- reference/v5/components/javascript-environment.md#TypeScript Support
|
|
8
|
+
sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
|
|
9
|
+
inputHash: 4e6bd8b610edd595
|
|
6
10
|
---
|
|
7
11
|
|
|
8
|
-
# TypeScript Type Stripping
|
|
12
|
+
# TypeScript Type Stripping in Harper
|
|
9
13
|
|
|
10
|
-
Instructions for the agent to
|
|
14
|
+
Instructions for the agent to run `.ts` files directly in Harper without a build step using Node.js's built-in type stripping.
|
|
11
15
|
|
|
12
16
|
## When to Use
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
Apply this rule when writing Harper resource files in TypeScript. Use it any time you need to reference `.ts` source files from `config.yaml` or import between local TypeScript modules in a Harper project.
|
|
15
19
|
|
|
16
20
|
## How It Works
|
|
17
21
|
|
|
18
|
-
1. **
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import { Resource } from 'harper';
|
|
23
|
-
export class MyResource extends Resource {
|
|
24
|
-
async get(): Promise<{ message: string }> {
|
|
25
|
-
return { message: 'Running TS directly!' };
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
4. **Use Explicit Extensions in Imports**: When importing other local modules, include the `.ts` extension: `import { helper } from './helper.ts'`.
|
|
30
|
-
5. **Configure `config.yaml`**: Ensure `jsResource` points to your `.ts` files:
|
|
22
|
+
1. **Ensure Node.js version**: Require Node.js 22.6 or later. Type stripping is unavailable on earlier versions.
|
|
23
|
+
|
|
24
|
+
2. **Point `jsResource` at `.ts` files**: The `jsResource` plugin loads both `.js` and `.ts` files. Set its `files` glob in `config.yaml` to target your `.ts` source files:
|
|
25
|
+
|
|
31
26
|
```yaml
|
|
32
27
|
jsResource:
|
|
33
28
|
files: 'resources/*.ts'
|
|
34
29
|
```
|
|
30
|
+
|
|
31
|
+
3. **Use explicit `.ts` extensions in local imports**: Node's loader does not resolve `'./helper'` to `'./helper.ts'`, so always include the full extension:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { helper } from './helper.ts';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
4. **Stay within type-stripping limits**: Only type annotations and declarations are removed. Do not use enums with runtime values, namespaces with runtime semantics, or any other features that require code transformation beyond type stripping.
|
|
38
|
+
|
|
39
|
+
## Examples
|
|
40
|
+
|
|
41
|
+
A complete Harper resource written in TypeScript, using imports from the `harper` package:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { type RequestTargetOrId, Resource, tables } from 'harper';
|
|
45
|
+
|
|
46
|
+
export class MyResource extends Resource {
|
|
47
|
+
async get(target?: RequestTargetOrId): Promise<{ message: string }> {
|
|
48
|
+
return { message: 'Hello from TS' };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Paired `config.yaml` entry loading the file via `jsResource`:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
jsResource:
|
|
57
|
+
files: 'resources/*.ts'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Notes
|
|
61
|
+
|
|
62
|
+
- No build step or transpiler is required — Harper runs `.ts` files directly.
|
|
63
|
+
- Type imports (e.g., `import { type RequestTargetOrId }`) from the `harper` package work as usual.
|
|
64
|
+
- Unsupported TypeScript features include: enums with runtime values, namespaces with runtime semantics, and anything requiring code transformation beyond simple type stripping.
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
# Phase 3 flips 7 obvious 1:1 rules to mode: generate (automatic-apis,
|
|
10
10
|
# querying-rest-apis, real-time-apps, checking-authentication, logging,
|
|
11
11
|
# deploying-to-harper-fabric, caching). All others remain synthesized.
|
|
12
|
+
# Phase 4 migrates schema-design-tooling and typescript-type-stripping to
|
|
13
|
+
# mode: generate. creating-harper-apps stays synthesized pending dedicated
|
|
14
|
+
# create-harper CLI documentation.
|
|
12
15
|
|
|
13
16
|
rules:
|
|
14
17
|
# ===========================================================================
|
|
@@ -27,7 +30,23 @@ rules:
|
|
|
27
30
|
category: schema
|
|
28
31
|
priority: 1
|
|
29
32
|
order: 2
|
|
30
|
-
mode:
|
|
33
|
+
mode: generate
|
|
34
|
+
sources:
|
|
35
|
+
- path: reference/v5/database/schema.md
|
|
36
|
+
section: 'Overview'
|
|
37
|
+
role: primary
|
|
38
|
+
- path: reference/v5/database/schema.md
|
|
39
|
+
section: 'Type Directives'
|
|
40
|
+
role: primary
|
|
41
|
+
- path: reference/v5/database/schema.md
|
|
42
|
+
section: 'Field Directives'
|
|
43
|
+
role: primary
|
|
44
|
+
must_cover:
|
|
45
|
+
- '@table'
|
|
46
|
+
- '@export'
|
|
47
|
+
- '@primaryKey'
|
|
48
|
+
- '@indexed'
|
|
49
|
+
- 'graphqlSchema'
|
|
31
50
|
|
|
32
51
|
- rule: defining-relationships
|
|
33
52
|
description: How to define and use relationships between tables in Harper using GraphQL.
|
|
@@ -178,7 +197,16 @@ rules:
|
|
|
178
197
|
category: logic
|
|
179
198
|
priority: 3
|
|
180
199
|
order: 4
|
|
181
|
-
mode:
|
|
200
|
+
mode: generate
|
|
201
|
+
sources:
|
|
202
|
+
- path: reference/v5/components/javascript-environment.md
|
|
203
|
+
section: 'TypeScript Support'
|
|
204
|
+
role: primary
|
|
205
|
+
must_cover:
|
|
206
|
+
- '.ts'
|
|
207
|
+
- 'jsResource'
|
|
208
|
+
- 'Node.js 22.6'
|
|
209
|
+
- 'type stripping'
|
|
182
210
|
|
|
183
211
|
- rule: caching
|
|
184
212
|
description: How to implement integrated data caching in Harper from external sources.
|
|
@@ -256,3 +284,19 @@ rules:
|
|
|
256
284
|
- 'console.log'
|
|
257
285
|
- 'logger'
|
|
258
286
|
- 'withTag('
|
|
287
|
+
|
|
288
|
+
- rule: load-env
|
|
289
|
+
description: How to load environment variables from .env files into a Harper application using the loadEnv plugin.
|
|
290
|
+
category: ops
|
|
291
|
+
priority: 4
|
|
292
|
+
order: 6
|
|
293
|
+
mode: generate
|
|
294
|
+
sources:
|
|
295
|
+
- path: reference/v5/environment-variables/overview.md
|
|
296
|
+
role: primary
|
|
297
|
+
must_cover:
|
|
298
|
+
- 'loadEnv'
|
|
299
|
+
- 'files'
|
|
300
|
+
- 'process.env'
|
|
301
|
+
- 'override'
|
|
302
|
+
- 'config.yaml'
|
package/package.json
CHANGED
package/skills-lock.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"source": "harperfast/skills",
|
|
6
6
|
"sourceType": "github",
|
|
7
7
|
"skillPath": "harper-best-practices/SKILL.md",
|
|
8
|
-
"computedHash": "
|
|
8
|
+
"computedHash": "921b0848ec9f7ee375fec2d4191c99325e1d7160c0ab463832b3e0443e80576a"
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
}
|