@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
@@ -19,14 +19,14 @@ Use this skill when you need to define new data structures or modify existing on
19
19
  2. **Use Directives**: All available directives for defining your schema are defined in `node_modules/harper/schema.graphql`. Common directives include `@table`, `@export`, `@primaryKey`, `@indexed`, and `@relationship`.
20
20
  3. **Define Relationships**: Link tables together using the `@relationship` directive. For more details, see the [Defining Relationships](defining-relationships.md) skill.
21
21
  4. **Enable Automatic APIs**: If you add `@table @export` to a schema type, Harper automatically sets up REST and WebSocket APIs for basic CRUD operations against that table. **Important**: REST endpoints also require `rest: true` in `config.yaml` — without it, `@export`ed tables will not respond to HTTP requests. For a detailed list of available endpoints and how to use them, see the [Automatic REST APIs](automatic-apis.md) skill.
22
- - `GET /{TableName}`: Describes the schema itself.
22
+ - `GET /{TableName}`: Describes the resource itself — table, database, and declared attributes. No trailing slash.
23
23
  - `GET /{TableName}/`: Lists all records (supports filtering, sorting, and pagination via query parameters). See the [Querying REST APIs](querying-rest-apis.md) skill for details.
24
- - `GET /{TableName}/{id}`: Retrieves a single record by its ID.
25
- - `POST /{TableName}/`: Creates a new record.
26
- - `PUT /{TableName}/{id}`: Updates an existing record.
27
- - `PATCH /{TableName}/{id}`: Performs a partial update on a record.
28
- - `DELETE /{TableName}/`: Deletes all records or filtered records.
29
- - `DELETE /{TableName}/{id}`: Deletes a single record by its ID.
24
+ - `GET /{TableName}/{id}`: Retrieves a single record by its primary key.
25
+ - `POST /{TableName}/`: Creates a record and returns `201` with the Harper-assigned primary key in the `Location` response header (the bare key, not a URL). **The trailing slash is required** — `POST /{TableName}` returns `404`, and `POST /{TableName}/{id}` returns `405`.
26
+ - `PUT /{TableName}/{id}`: Creates **or replaces** the record at `{id}` (upsert). **Properties omitted from the body are removed** — send the complete record, or use `PATCH` to change a subset. Three exceptions survive the replacement: an `@updatedTime` attribute is re-stamped with the time of the write, a `@createdTime` attribute keeps its original value, and the primary key is forced to match the `{id}` in the URL, so a mismatched key in the body cannot create a second record.
27
+ - `PATCH /{TableName}/{id}`: Merges the request body into the existing record, preserving unspecified properties. The merge is **shallow** — a nested object in the body replaces the stored one wholesale rather than being deep-merged.
28
+ - `DELETE /{TableName}/{id}`: Deletes a single record by its primary key.
29
+ - `DELETE /{TableName}/`: Deletes every record matching the query parameters. **With no query parameters it matches — and deletes — every record in the table.** Always pass a filter unless emptying the table is the intent.
30
30
  5. **Consider Table Extensions**: If you are going to [extend the table](./extending-tables.md) in your resources, then do not `@export` the table from the schema.
31
31
 
32
32
  ## Examples
@@ -4,23 +4,29 @@ description: How to use Harper's automatically generated REST and WebSocket APIs
4
4
  metadata:
5
5
  mode: generate
6
6
  sources:
7
- - reference/v5/rest/overview.md
7
+ - reference/v5/rest/overview.md#How the REST Interface Works
8
+ - reference/v5/rest/overview.md#Configuration
9
+ - reference/v5/rest/overview.md#Tables and Their Automatic Endpoints
10
+ - reference/v5/rest/overview.md#URL Structure
11
+ - reference/v5/rest/overview.md#HTTP Methods
12
+ - reference/v5/rest/overview.md#Content Types
13
+ - reference/v5/rest/overview.md#OpenAPI
8
14
  - reference/v5/rest/websockets.md
9
- sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
10
- inputHash: 6c7013b1307d9f7b
15
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
16
+ inputHash: a9356e92dd3dc106
11
17
  ---
12
18
 
13
19
  # Automatic APIs
14
20
 
15
- Instructions for the agent to follow when enabling and using Harper's automatically generated REST and WebSocket APIs.
21
+ Instructions for the agent to follow when using Harper's automatically generated REST and WebSocket APIs for exported tables and resources.
16
22
 
17
23
  ## When to Use
18
24
 
19
- Apply this rule when adding REST or WebSocket API access to Harper tables or custom resources. Use it when configuring `config.yaml` to expose endpoints, mapping HTTP methods to resource operations, or implementing real-time WebSocket connections on a resource class.
25
+ Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for Harper tables without writing custom handler code. Use it whenever a schema type needs to be served over HTTP, when configuring real-time subscriptions, or when setting up conditional caching behavior for REST responses.
20
26
 
21
27
  ## How It Works
22
28
 
23
- 1. **Enable the REST plugin**: Add `rest: true` to your application's `config.yaml`. This activates the HTTP REST interface on the application server port (default `9926`) and enables WebSocket support automatically.
29
+ 1. **Enable REST in `config.yaml`**: Add `rest: true` to the application configuration file. This registers REST endpoints and, by default, WebSocket subscriptions for all exported resources.
24
30
 
25
31
  ```yaml
26
32
  rest: true
@@ -34,26 +40,47 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
34
40
  webSocket: false # disables automatic WebSocket support (enabled by default)
35
41
  ```
36
42
 
37
- 2. **Export your resource in the schema**: Tables are not exposed by default. Use the `@export` directive in your schema definition to expose a table as a REST endpoint. The exported name defines the base URL path.
43
+ 2. **Export the table in the schema**: Add `@export` to the type definition. Without `@export`, Harper registers no REST route and callers receive `404`. Without `rest: true`, even an exported table does not respond to HTTP requests. Both are required.
38
44
 
39
- 3. **Use the correct URL structure**: Target resources using these path conventions:
45
+ ```graphql
46
+ type Product @table @export {
47
+ id: Long @primaryKey
48
+ name: String
49
+ price: Float
50
+ }
51
+ ```
52
+
53
+ Reference the schema file in `config.yaml`:
54
+
55
+ ```yaml
56
+ graphqlSchema:
57
+ files: schema.graphql
58
+ rest: true
59
+ ```
40
60
 
41
- | Path | Description |
42
- | -------------------------------------------- | ----------------------------------------------------------- |
43
- | `/my-resource` | Returns resource metadata |
44
- | `/my-resource/` | Collection — all records; append query parameters to search |
45
- | `/my-resource/record-id` | Specific record by primary key |
46
- | `/my-resource/record-id/` | Collection of records with the given id prefix |
47
- | `/my-resource/record-id/with/multiple/parts` | Record id with multiple path segments |
61
+ 3. **Use the automatically registered endpoints**: Harper serves the following endpoints on the application HTTP server port (default `9926`) with no route definitions or handler code required.
48
62
 
49
- 4. **Map operations to HTTP methods**: Each HTTP method maps to a resource method:
50
- - **GET** — Retrieve a record or search. Calls `get()`. Responses include an `ETag` header; send `If-None-Match` on subsequent requests to receive `304 Not Modified` when unchanged.
51
- - **PUT** — Create or replace a record (upsert). Calls `put(record)`. The stored record exactly matches the submitted body; omitted properties are removed.
52
- - **POST** — Create a record without specifying a primary key. Calls `post(data)`. The assigned key is returned in the `Location` response header.
53
- - **PATCH** — Partially update a record, merging only provided top-level properties. Calls the resource's patch handler. Merge is **shallow** — nested objects are replaced entirely, not deep-merged.
54
- - **DELETE** — Delete a record by id or all records matching a query.
63
+ | Endpoint | Description |
64
+ | ---------------------------- | --------------------------------------------------------------------------- |
65
+ | `GET /Product` | Returns resource description (table name, database, attributes) |
66
+ | `GET /Product/` | Returns the record collection; append query parameters to filter |
67
+ | `GET /Product/{id}` | Returns a single record by primary key; `404` if not found |
68
+ | `GET /Product/{id}.property` | Returns a single declared property of one record |
69
+ | `POST /Product/` | Creates a record; responds `201`; primary key returned in `Location` header |
70
+ | `PUT /Product/{id}` | Creates or replaces the record at `{id}` (upsert) |
71
+ | `PATCH /Product/{id}` | Merges body into existing record (shallow, top-level only) |
72
+ | `DELETE /Product/{id}` | Deletes the record at `{id}` |
73
+ | `DELETE /Product/?query` | Deletes every record matching the query |
55
74
 
56
- 5. **Connect via WebSocket**: A WebSocket connection to a resource URL subscribes to that resource and streams change events. See [real-time-apps.md](real-time-apps.md) for full real-time patterns.
75
+ 4. **Handle `POST` primary key and `Location`**: On a successful `POST`, the new record's primary key is returned in the `Location` response header — the value the body supplied if it carried the primary-key property, otherwise a Harper-assigned key. The header carries the bare key value, not a URL.
76
+
77
+ 5. **Understand `PUT` write behavior**: `PUT` replaces the stored record exactly. Three exceptions always apply: a `@createdTime` attribute keeps the original value, an `@updatedTime` attribute is re-stamped with the time of the write, and the primary key is forced to match the `{id}` in the URL.
78
+
79
+ 6. **Use conditional requests for caching**: GET responses include an `ETag` header encoding the record's version/last-modification time. Send `If-None-Match` on subsequent requests with the cached `ETag` value. If the record has not changed, Harper returns `304 Not Modified` with no body.
80
+
81
+ 7. **Select content type with `Accept`**: Use the `Accept` header to request a specific response format. The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` are reserved as content-type selectors on property paths and take precedence over property names. See [querying-rest-apis.md](querying-rest-apis.md) for query syntax details.
82
+
83
+ 8. **Connect via WebSocket**: WebSocket support is enabled automatically when `rest` is enabled. Connecting to a resource URL subscribes to changes for that resource. See [real-time-apps.md](real-time-apps.md) for real-time patterns.
57
84
 
58
85
  ```javascript
59
86
  let ws = new WebSocket('wss://server/my-resource/341');
@@ -62,29 +89,84 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
62
89
  };
63
90
  ```
64
91
 
65
- 6. **Implement a custom `connect()` handler** on a resource class to control WebSocket behavior. The method receives `incomingMessages` and must return an async iterable producing messages to send to the client.
66
-
67
- 7. **Retrieve the OpenAPI spec**: Harper auto-generates an OpenAPI specification for all exported resources, available at:
92
+ 9. **Implement a custom `connect()` handler** when default subscription behavior is insufficient. The method must return an async iterable that produces messages to send to the client.
68
93
 
69
- ```
70
- GET /openapi
94
+ ```javascript
95
+ export class Echo extends Resource {
96
+ async *connect(incomingMessages) {
97
+ for await (let message of incomingMessages) {
98
+ yield message; // echo each message back
99
+ }
100
+ }
101
+ }
71
102
  ```
72
103
 
73
104
  ## Examples
74
105
 
75
- **Simple echo WebSocket server**:
106
+ ### Full schema and config setup
76
107
 
77
- ```javascript
78
- export class Echo extends Resource {
79
- async *connect(incomingMessages) {
80
- for await (let message of incomingMessages) {
81
- yield message; // echo each message back
82
- }
83
- }
108
+ ```graphql
109
+ # schema.graphql
110
+ type Product @table @export {
111
+ id: Long @primaryKey
112
+ name: String
113
+ price: Float
84
114
  }
85
115
  ```
86
116
 
87
- **Custom `connect()` using the default iterable with `send()` and `close` event**:
117
+ ```yaml
118
+ # config.yaml
119
+ graphqlSchema:
120
+ files: schema.graphql
121
+ rest: true
122
+ ```
123
+
124
+ ### Conditional GET with ETag caching
125
+
126
+ ```
127
+ GET /Product/123
128
+ # Response includes:
129
+ # ETag: "abc123"
130
+
131
+ GET /Product/123
132
+ If-None-Match: "abc123"
133
+ # Response: 304 Not Modified (no body transferred)
134
+ ```
135
+
136
+ ### POST and read the Location header
137
+
138
+ ```
139
+ POST /Product/
140
+ Content-Type: application/json
141
+
142
+ { "name": "Widget", "price": 9.99 }
143
+
144
+ # Response:
145
+ # 201 Created
146
+ # Location: 7f3a9c
147
+ ```
148
+
149
+ ### PATCH (shallow merge only)
150
+
151
+ ```
152
+ PATCH /Product/123
153
+ Content-Type: application/json
154
+
155
+ { "price": 12.99 }
156
+ ```
157
+
158
+ Only `price` is updated; other top-level properties are preserved. Nested objects in the body replace the stored sub-object wholesale — deep merge does not occur.
159
+
160
+ ### Request MessagePack response
161
+
162
+ ```
163
+ GET /Product/123
164
+ Accept: application/msgpack
165
+ ```
166
+
167
+ Alternatively, use the `.msgpack` suffix on the URL path where supported.
168
+
169
+ ### WebSocket with custom outgoing messages
88
170
 
89
171
  ```javascript
90
172
  export class Example extends Resource {
@@ -96,7 +178,7 @@ export class Example extends Resource {
96
178
  }, 1000);
97
179
 
98
180
  incomingMessages.on('data', (message) => {
99
- outgoingMessages.send(message); // echo incoming messages
181
+ outgoingMessages.send(message);
100
182
  });
101
183
 
102
184
  outgoingMessages.on('close', () => {
@@ -108,26 +190,20 @@ export class Example extends Resource {
108
190
  }
109
191
  ```
110
192
 
111
- **Common REST operations**:
112
-
113
- ```
114
- GET /MyTable/123
115
- GET /MyTable/?name=Harper
116
- PUT /MyTable/123
117
- PATCH /MyTable/123
118
- DELETE /MyTable/?status=archived
119
- ```
193
+ ### Disable WebSocket while keeping REST
120
194
 
121
- ```json
122
- { "name": "some data" }
195
+ ```yaml
196
+ rest:
197
+ webSocket: false
123
198
  ```
124
199
 
125
200
  ## Notes
126
201
 
127
- - `rest: true` is the minimal config to enable both REST and WebSocket support. Set `webSocket: false` under the `rest` key to disable WebSocket only.
128
- - The `@export` directive in the schema is required for any table to appear as a REST endpoint — tables are not exported by default.
129
- - PATCH merges are shallow (top-level only). Nested objects in the request body replace the entire existing sub-object. Dot-path keys (e.g., `"settings.theme"`) are stored as literal keys, not interpreted as paths.
130
- - For MQTT over WebSocket, set the sub-protocol header `Sec-WebSocket-Protocol: mqtt`.
131
- - In distributed environments, non-retained messages are delivered in arrival order; retained messages (PUT/updated records) keep only the latest timestamp as the winning record.
132
- - For full query syntax on GET and DELETE, see [querying-rest-apis.md](querying-rest-apis.md).
133
- - For building real-time features with WebSocket subscriptions, see [real-time-apps.md](real-time-apps.md).
202
+ - The trailing slash is significant: `/Product` addresses the resource itself; `/Product/` addresses its record collection. `POST /Product` (no trailing slash) returns `404`.
203
+ - `HEAD` is served as `GET` with the body omitted. `QUERY` is accepted on the collection path and reads its search from the request body.
204
+ - A `POST` to an existing primary key fails with `409` — it does not overwrite.
205
+ - A component directory with **no configuration file** gets REST enabled by Harper's built-in default. As soon as a `config.yaml` exists it is used verbatim — add `rest: true` explicitly or REST is off.
206
+ - Do not apply `@export` to a schema type and also export a same-named JavaScript subclass of that table — this produces conflicting endpoints.
207
+ - Server-Sent Events subscriptions are served on the same paths, negotiated via `Accept: text/event-stream`. They are not affected by the `webSocket` option.
208
+ - Every non-hidden exported resource is included in the generated OpenAPI document at `GET /openapi`. Mark a type `@hidden` or set `static hidden = true` on a programmatic Resource to omit it.
209
+ - MQTT over WebSockets requires the sub-protocol header `Sec-WebSocket-Protocol: mqtt`.