@harperfast/template-react-ts-studio 1.11.2 → 1.11.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 (54) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +702 -490
  2. package/.agents/skills/harper-best-practices/SKILL.md +1 -0
  3. package/.agents/skills/harper-best-practices/rules/automatic-apis.md +46 -78
  4. package/.agents/skills/harper-best-practices/rules/defining-relationships.md +41 -63
  5. package/.agents/skills/harper-best-practices/rules/delegating-to-the-built-in-agent.md +141 -0
  6. package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +59 -45
  7. package/.agents/skills/harper-best-practices/rules/load-env.md +24 -23
  8. package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +62 -78
  9. package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +92 -65
  10. package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +53 -55
  11. package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +98 -62
  12. package/.agents/skills/harper-best-practices/rules/vector-indexing.md +113 -42
  13. package/.agents/skills/harper-best-practices/rules.manifest.yaml +9 -0
  14. package/.agents/skills/harper-mcp/AGENTS.md +501 -0
  15. package/.agents/skills/harper-mcp/SKILL.md +91 -0
  16. package/.agents/skills/harper-mcp/rules/automatic-verb-tools.md +50 -0
  17. package/.agents/skills/harper-mcp/rules/connecting-clients.md +59 -0
  18. package/.agents/skills/harper-mcp/rules/custom-mcp-prompts.md +52 -0
  19. package/.agents/skills/harper-mcp/rules/custom-mcp-resources.md +64 -0
  20. package/.agents/skills/harper-mcp/rules/custom-mcp-tools.md +72 -0
  21. package/.agents/skills/harper-mcp/rules/durable-quotas.md +73 -0
  22. package/.agents/skills/harper-mcp/rules/enabling-mcp.md +63 -0
  23. package/.agents/skills/harper-mcp/rules/rate-limiting.md +62 -0
  24. package/.agents/skills/harper-mcp/rules/resources-surface.md +44 -0
  25. package/.agents/skills/harper-mcp/rules/security-posture.md +36 -0
  26. package/.agents/skills/harper-mcp/rules.manifest.yaml +203 -0
  27. package/agent/skills/harper-best-practices/AGENTS.md +702 -490
  28. package/agent/skills/harper-best-practices/SKILL.md +1 -0
  29. package/agent/skills/harper-best-practices/rules/automatic-apis.md +46 -78
  30. package/agent/skills/harper-best-practices/rules/defining-relationships.md +41 -63
  31. package/agent/skills/harper-best-practices/rules/delegating-to-the-built-in-agent.md +141 -0
  32. package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +59 -45
  33. package/agent/skills/harper-best-practices/rules/load-env.md +24 -23
  34. package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +62 -78
  35. package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +92 -65
  36. package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +53 -55
  37. package/agent/skills/harper-best-practices/rules/v5-upgrade.md +98 -62
  38. package/agent/skills/harper-best-practices/rules/vector-indexing.md +113 -42
  39. package/agent/skills/harper-best-practices/rules.manifest.yaml +9 -0
  40. package/agent/skills/harper-mcp/AGENTS.md +501 -0
  41. package/agent/skills/harper-mcp/SKILL.md +83 -0
  42. package/agent/skills/harper-mcp/rules/automatic-verb-tools.md +50 -0
  43. package/agent/skills/harper-mcp/rules/connecting-clients.md +59 -0
  44. package/agent/skills/harper-mcp/rules/custom-mcp-prompts.md +52 -0
  45. package/agent/skills/harper-mcp/rules/custom-mcp-resources.md +64 -0
  46. package/agent/skills/harper-mcp/rules/custom-mcp-tools.md +72 -0
  47. package/agent/skills/harper-mcp/rules/durable-quotas.md +73 -0
  48. package/agent/skills/harper-mcp/rules/enabling-mcp.md +63 -0
  49. package/agent/skills/harper-mcp/rules/rate-limiting.md +62 -0
  50. package/agent/skills/harper-mcp/rules/resources-surface.md +44 -0
  51. package/agent/skills/harper-mcp/rules/security-posture.md +36 -0
  52. package/agent/skills/harper-mcp/rules.manifest.yaml +203 -0
  53. package/package.json +1 -1
  54. package/skills-lock.json +7 -1
@@ -5,17 +5,17 @@ metadata:
5
5
  mode: generate
6
6
  sources:
7
7
  - reference/v5/rest/querying.md
8
- sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
9
- inputHash: 9f8c981a629ef606
8
+ sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
9
+ inputHash: d5ed0e937cd2d7e0
10
10
  ---
11
11
 
12
- # Querying REST APIs
12
+ # Querying Harper REST APIs
13
13
 
14
- Instructions for the agent to filter, sort, select, and paginate Harper REST API collections using URL query parameters.
14
+ Instructions for the agent to filter, sort, select, and paginate records using Harper's URL-based query language on REST collection endpoints.
15
15
 
16
16
  ## When to Use
17
17
 
18
- Apply this rule when building or modifying code that queries Harper REST endpoints with filtering, sorting, field selection, or pagination. Use it whenever constructing URLs against collection paths exposed by Harper's automatic REST interface (see [automatic-apis.md](automatic-apis.md)).
18
+ Apply this rule when building or debugging REST API calls against Harper tables that require filtering by attribute values, comparison ranges, sorting, field selection, or result pagination. This rule also covers OR logic, grouping, and querying across related tables via dot-syntax joins. See [automatic-apis.md](automatic-apis.md) for how Harper exposes tables as REST endpoints.
19
19
 
20
20
  ## How It Works
21
21
 
@@ -26,21 +26,27 @@ Apply this rule when building or modifying code that queries Harper REST endpoin
26
26
  GET /Product/?category=software&inStock=true
27
27
  ```
28
28
 
29
- 2. **Apply comparison operators (FIQL syntax)**: Use FIQL operators directly in query parameter values.
29
+ 2. **Filter for null values**: Use `=null` as the value to match null or non-null records.
30
30
 
31
- | Operator | Meaning |
32
- | ------------ | -------------------------------------- |
33
- | `==` | Equal |
34
- | `=lt=` | Less than |
35
- | `=le=` | Less than or equal |
36
- | `=gt=` | Greater than |
37
- | `=ge=` | Greater than or equal |
38
- | `=ne=`, `!=` | Not equal |
39
- | `=ct=` | Contains (strings) |
40
- | `=sw=` | Starts with (strings) |
41
- | `=ew=` | Ends with (strings) |
42
- | `=`, `===` | Strict equality (no type conversion) |
43
- | `!==` | Strict inequality (no type conversion) |
31
+ ```
32
+ GET /Product/?discount=null
33
+ ```
34
+
35
+ 3. **Apply comparison operators (FIQL syntax)**: Use FIQL operators in query parameters for range and string matching.
36
+
37
+ | Operator | Meaning |
38
+ | -------------------- | -------------------------------------- |
39
+ | `==` | Equal |
40
+ | `=lt=` | Less than |
41
+ | `=le=` | Less than or equal |
42
+ | `=gt=` | Greater than |
43
+ | `=ge=` | Greater than or equal |
44
+ | `=ne=`, `!=` | Not equal |
45
+ | `=ct=` | Contains (strings) |
46
+ | `=sw=`, `==<value>*` | Starts with (strings) |
47
+ | `=ew=` | Ends with (strings) |
48
+ | `=`, `===` | Strict equality (no type conversion) |
49
+ | `!==` | Strict inequality (no type conversion) |
44
50
 
45
51
  ```
46
52
  GET /Product/?price=gt=100
@@ -55,32 +61,52 @@ Apply this rule when building or modifying code that queries Harper REST endpoin
55
61
  GET /Product/?listDate=gt=2017-03-08T09%3A30%3A00.000Z
56
62
  ```
57
63
 
58
- 3. **Chain conditions for range queries**: Omit the attribute name on the second condition to apply it to the same attribute. Only `gt`/`ge` combined with `lt`/`le` is supported.
64
+ 4. **Chain conditions for range queries**: Omit the attribute name on the second condition to apply it to the same attribute. Only `gt`/`ge` combined with `lt`/`le` is supported for chaining.
59
65
 
60
66
  ```
61
67
  GET /Product/?price=gt=100&lt=200
62
68
  ```
63
69
 
64
- 4. **Combine conditions with OR logic**: Use `|` instead of `&`.
70
+ 5. **Apply type conversion**: For FIQL comparators, Harper converts values automatically. Use explicit prefixes to force a type.
71
+
72
+ | Syntax | Behavior |
73
+ | ----------------------------------------- | ------------------------------------------- |
74
+ | `name==null` | Converts to `null` |
75
+ | `name==123` | Converts to number if attribute is untyped |
76
+ | `name==true` | Converts to boolean if attribute is untyped |
77
+ | `name==number:123` | Explicit number conversion |
78
+ | `name==boolean:true` | Explicit boolean conversion |
79
+ | `name==string:some%20text` | Keep as string with URL decode |
80
+ | `name==date:2024-01-05T20%3A07%3A27.955Z` | Explicit Date conversion |
81
+
82
+ For strict operators (`=`, `===`, `!==`), no automatic type conversion is applied.
83
+
84
+ 6. **Combine conditions with OR logic**: Use `|` instead of `&` to express OR between conditions.
65
85
 
66
86
  ```
67
87
  GET /Product/?rating=5|featured=true
68
88
  ```
69
89
 
70
- 5. **Group conditions**: Use parentheses or square brackets to control order of operations. Prefer square brackets when constructing queries from user input, since standard URI encoding safely encodes `[` and `]`.
90
+ 7. **Group conditions**: Use parentheses or square brackets to control evaluation order. Prefer square brackets when building queries from user input, since `[` and `]` are safely URI-encoded.
71
91
 
72
92
  ```
73
93
  GET /Product/?rating=5|(price=gt=100&price=lt=200)
74
94
  GET /Product/?rating=5&[tag=fast|tag=scalable|tag=efficient]
75
95
  ```
76
96
 
77
- Construct grouped queries from JavaScript:
97
+ Build grouped queries in JavaScript:
78
98
 
79
99
  ```javascript
80
100
  let url = `/Product/?rating=5&[${tags.map(encodeURIComponent).join('|')}]`;
81
101
  ```
82
102
 
83
- 6. **Select specific properties with `select(`**: Use `select()` to control which fields are returned.
103
+ Nest groups for complex conditions:
104
+
105
+ ```
106
+ GET /Product/?price=lt=100|[rating=5&[tag=fast|tag=scalable|tag=efficient]&inStock=true]
107
+ ```
108
+
109
+ 8. **Select specific properties with `select(`**: Append `select(...)` as a query function separated by `&` to control which fields are returned.
84
110
 
85
111
  | Syntax | Returns |
86
112
  | -------------------------------------- | ------------------------------------------- |
@@ -95,108 +121,109 @@ Apply this rule when building or modifying code that queries Harper REST endpoin
95
121
  GET /Product/?brand.name=Microsoft&select(name,brand{name})
96
122
  ```
97
123
 
98
- 7. **Limit results with `limit(`**: Use `limit(end)` or `limit(start,end)` to paginate.
124
+ 9. **Paginate results with `limit(`**: Use `limit(end)` or `limit(start,end)` to restrict the number of records returned.
99
125
 
100
126
  ```
101
127
  GET /Product/?rating=gt=3&inStock=true&select(rating,name)&limit(20)
102
128
  GET /Product/?rating=gt=3&limit(10,30)
103
129
  ```
104
130
 
105
- 8. **Sort results with `sort(`**: Use `sort(property)` or `sort(+property,-property,...)`. Prefix `+` or no prefix = ascending; `-` = descending.
131
+ 10. **Sort results with `sort(`**: Use `sort(property)` or `sort(+property,-property,...)` to order results. Prefix `+` or no prefix = ascending; `-` = descending.
106
132
 
107
- ```
108
- GET /Product/?rating=gt=3&sort(+name)
109
- GET /Product/?sort(+rating,-price)
110
- ```
133
+ ```
134
+ GET /Product/?rating=gt=3&sort(+name)
135
+ GET /Product/?sort(+rating,-price)
136
+ ```
111
137
 
112
- 9. **Query across relationships**: Use dot-syntax to filter by related table attributes. Relationships must be defined in the schema using `@relation`.
138
+ 11. **Query across relationships using dot-syntax**: Filter on related table attributes using dot-chained property names. Relationships must be defined in the schema with `@relationship`.
113
139
 
114
- ```
115
- GET /Product/?brand.name=Microsoft
116
- GET /Brand/?products.name=Keyboard
117
- ```
140
+ ```
141
+ GET /Product/?brand.name=Microsoft
142
+ GET /Brand/?products.name=Keyboard
143
+ ```
118
144
 
119
- Use `select()` to include relationship attributes in the response (they are not included by default):
145
+ Use `select()` to include relationship attributes in the response (they are excluded by default):
120
146
 
121
- ```
122
- GET /Product/?brand.name=Microsoft&select(name,brand{name})
123
- ```
147
+ ```
148
+ GET /Product/?brand.name=Microsoft&select(name,brand)
149
+ GET /Product/?brand.name=Microsoft&select(name,brand{name})
150
+ ```
124
151
 
125
- 10. **Access a specific property by URL**: Append the property name with dot syntax to the record ID. Only works for properties declared in the schema.
152
+ 12. **Access a specific property by URL**: Append `.propertyName` to a record ID in the URL path. Only works for properties declared in the schema.
126
153
  ```
127
154
  GET /MyTable/123.propertyName
128
155
  ```
129
156
 
130
157
  ## Examples
131
158
 
132
- **Range filter with select and limit:**
159
+ **Range filter with select and limit**:
133
160
 
134
161
  ```
135
162
  GET /Product/?category=software&price=gt=100&price=lt=200&select(name,price)&limit(20)
136
163
  ```
137
164
 
138
- **Sort descending with multiple fields:**
165
+ **Sort and paginate**:
139
166
 
140
167
  ```
141
- GET /Product/?sort(+rating,-price)
168
+ GET /Product/?rating=gt=3&sort(+rating,-price)&limit(10,30)
142
169
  ```
143
170
 
144
- **OR logic with grouping:**
171
+ **OR with grouping**:
145
172
 
146
173
  ```
147
174
  GET /Product/?price=lt=100|[rating=5&[tag=fast|tag=scalable|tag=efficient]&inStock=true]
148
175
  ```
149
176
 
150
- **Relationship join with nested select:**
151
-
152
- ```
153
- GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
154
- ```
155
-
156
- **Schema defining a relationship for join queries:**
177
+ **Join query with nested select** — schema first:
157
178
 
158
179
  ```graphql
159
180
  type Product @table @export {
160
181
  id: Long @primaryKey
161
182
  name: String
162
183
  brandId: Long @indexed
163
- brand: Brand @relation(from: "brandId")
184
+ brand: Brand @relationship(from: "brandId")
164
185
  }
165
186
  type Brand @table @export {
166
187
  id: Long @primaryKey
167
188
  name: String
168
- products: [Product] @relation(to: "brandId")
189
+ products: [Product] @relationship(to: "brandId")
169
190
  }
170
191
  ```
171
192
 
172
- **Many-to-many relationship query:**
193
+ Then query:
194
+
195
+ ```
196
+ GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
197
+ ```
198
+
199
+ **Many-to-many relationship** — schema:
173
200
 
174
201
  ```graphql
175
202
  type Product @table @export {
176
203
  id: Long @primaryKey
177
204
  name: String
178
205
  resellerIds: [Long] @indexed
179
- resellers: [Reseller] @relation(from: "resellerId")
206
+ resellers: [Reseller] @relationship(from: "resellerIds")
180
207
  }
181
208
  ```
182
209
 
210
+ Query:
211
+
183
212
  ```
184
213
  GET /Product/?resellers.name=Cool Shop&select(id,name,resellers{name,id})
185
214
  ```
186
215
 
187
- **Type conversion with explicit prefix:**
216
+ **Date range with URL-encoded colons**:
188
217
 
189
218
  ```
190
- GET /Product/?price==number:123
191
- GET /Product/?active==boolean:true
192
- GET /Product/?listDate==date:2024-01-05T20%3A07%3A27.955Z
219
+ GET /Product/?listDate=gt=2017-03-08T09%3A30%3A00.000Z
193
220
  ```
194
221
 
195
222
  ## Notes
196
223
 
197
- - Only indexed attributes can be used as the primary filter; additional unindexed attributes can be combined with `&` once at least one indexed attribute is present.
198
- - For null value queries, use `?attribute=null`. Indexes must have been created with null indexing support; existing indexes must be removed and re-added to support null queries.
199
- - FIQL comparators (`==`, `!=`, `=gt=`, etc.) apply automatic type conversion based on value syntax or schema-declared type. Strict operators (`=`, `===`, `!==`) skip automatic type conversion.
200
- - Filtering by a related attribute produces INNER JOIN behavior (only records with a matching related record are returned). Using `select()` on a relationship without a filter produces LEFT JOIN behavior.
201
- - The array order of foreign key values in many-to-many relationships is preserved when resolving the relationship.
202
- - See [automatic-apis.md](automatic-apis.md) for how Harper tables are automatically exposed as REST endpoints.
224
+ - All filtered attributes must be indexed unless at least one other attribute in the same query is indexed.
225
+ - Null queries (`?attr=null`) require indexes created after null indexing support was added. Rebuild existing indexes (remove and re-add) to enable null queries on them.
226
+ - When selecting a related attribute without filtering on it, the join behaves as a LEFT JOIN — the relationship property is omitted if the foreign key is null or references a non-existent record.
227
+ - The array order of foreign key values (e.g., `resellerIds`) is preserved when resolving many-to-many relationships.
228
+ - Square brackets (`[`, `]`) are preferred over parentheses for grouping when constructing queries programmatically, because standard URI encoding safely encodes them.
229
+ - `directURLMapping: true` can be set on a resource to change URL path handling semantics; see your schema configuration for details.
@@ -9,8 +9,8 @@ metadata:
9
9
  - reference/v5/database/schema.md#Overview
10
10
  - reference/v5/database/schema.md#Type Directives
11
11
  - reference/v5/database/schema.md#Field Directives
12
- sourceCommit: 4fe4c9c95e0974eaa77032f6f10e36fbd8ec64ac
13
- inputHash: 15b9c72decc1f05d
12
+ sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
13
+ inputHash: ce22f3fefb660c6e
14
14
  ---
15
15
 
16
16
  # Schema Design and GraphQL Tooling
@@ -19,13 +19,11 @@ Instructions for the agent to follow when designing Harper schemas, applying cor
19
19
 
20
20
  ## When to Use
21
21
 
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.
22
+ Apply this rule when creating or modifying Harper schema files (`.graphql`), configuring `graphqlSchema` in `config.yaml`, or deciding which directives to apply to types and fields. Use it any time you need to define tables, primary keys, indexes, or exported endpoints.
23
23
 
24
24
  ## How It Works
25
25
 
26
- 1. **Create a schema file** using standard GraphQL type definitions with Harper-specific directives. Name it (e.g., `schema.graphql`) and place it in your component directory.
27
-
28
- 2. **Register the schema** in the component's `config.yaml` using the `graphqlSchema` plugin:
26
+ 1. **Declare the schema file** in the component's `config.yaml` using the `graphqlSchema` plugin:
29
27
 
30
28
  ```yaml
31
29
  graphqlSchema:
@@ -34,7 +32,7 @@ Apply this rule when creating or modifying Harper schema files, configuring `gra
34
32
 
35
33
  Both plugins and applications can specify schemas.
36
34
 
37
- 3. **Mark types as tables** with `@table`. The type name becomes the table name by default:
35
+ 2. **Mark types as tables** with `@table`. The type name becomes the table name by default:
38
36
 
39
37
  ```graphql
40
38
  type Dog @table {
@@ -45,13 +43,13 @@ Apply this rule when creating or modifying Harper schema files, configuring `gra
45
43
  }
46
44
  ```
47
45
 
48
- 4. **Designate a primary key** with `@primaryKey` on exactly one field per type. Primary keys must be unique; duplicate-key inserts are rejected. If no primary key is provided on insert, Harper auto-generates one:
46
+ 3. **Set a primary key** on every table using `@primaryKey`. Primary keys must be unique; duplicate-key inserts are rejected. If no primary key is provided on insert, Harper auto-generates one based on the field type:
49
47
  - `String` or `ID` → UUID string
50
48
  - `Int`, `Long`, or `Any` → auto-incrementing integer
51
49
 
52
50
  Use `Long` or `Any` for auto-generated numeric keys; `Int` is 32-bit and may be insufficient for large tables.
53
51
 
54
- 5. **Add secondary indexes** with `@indexed` on any field that will be used for filtering in REST queries, SQL, or NoSQL operations:
52
+ 4. **Index fields for querying** with `@indexed`. Required for filtering by an attribute in REST queries, SQL, or NoSQL operations:
55
53
 
56
54
  ```graphql
57
55
  type Breed @table {
@@ -62,7 +60,7 @@ Apply this rule when creating or modifying Harper schema files, configuring `gra
62
60
 
63
61
  If the field value is an array, each element is individually indexed. Null values are indexed by default.
64
62
 
65
- 6. **Expose a table as an external endpoint** with `@export`. The optional `name` parameter sets the URL path segment:
63
+ 5. **Expose a table as an external endpoint** with `@export`. Available via REST, MQTT, and other interfaces. The optional `name` parameter sets the URL path segment:
66
64
 
67
65
  ```graphql
68
66
  type MyTable @table @export(name: "my-table") {
@@ -70,9 +68,9 @@ Apply this rule when creating or modifying Harper schema files, configuring `gra
70
68
  }
71
69
  ```
72
70
 
73
- Without `name`, the type name is used as the path segment.
71
+ Without `name`, the type name is used.
74
72
 
75
- 7. **Configure `@table` arguments** as needed for database placement, expiration, eviction, and replication:
73
+ 6. **Configure `@table` arguments** as needed. All arguments are optional:
76
74
 
77
75
  | Argument | Type | Default | Description |
78
76
  | -------------- | --------- | ----------------------------- | ------------------------------------------------------------- |
@@ -83,13 +81,15 @@ Apply this rule when creating or modifying Harper schema files, configuring `gra
83
81
  | `scanInterval` | `Int` | `(expiration + eviction) / 4` | Seconds between eviction scans |
84
82
  | `replicate` | `Boolean` | `true` | Enable replication of this table |
85
83
 
86
- 8. **Apply additional field directives** where needed:
87
- - `@createdTime` — auto-assigns Unix epoch milliseconds on record creation
88
- - `@updatedTime` — auto-assigns Unix epoch milliseconds on each update
89
- - `@embed(source: "fieldName", model: "modelName")` — computes an embedding vector on write; attribute type must be `[Float]`
90
- - `@hidden` — suppresses the field from MCP tool descriptors and OpenAPI documents (not an access-control mechanism)
84
+ 7. **Apply additional field directives** where needed:
85
+ - `@createdTime` — auto-assigns creation timestamp (Unix epoch ms)
86
+ - `@updatedTime` — auto-assigns update timestamp (Unix epoch ms)
87
+ - `@expiresAt` — marks a field as the record's absolute expiration time (Unix epoch ms)
88
+ - `@embed(source:, model:)` — computes an embedding vector when the source field is written; field type must be `[Float]`
89
+ - `@hidden` — suppresses the field from MCP tool descriptors and OpenAPI document (not an access-control mechanism)
90
+
91
+ 8. **Use `@sealed`** on a type to prevent records from including properties beyond those declared in the schema:
91
92
 
92
- 9. **Restrict extra properties** with `@sealed` if records must not include properties beyond those declared:
93
93
  ```graphql
94
94
  type StrictRecord @table @sealed {
95
95
  id: Long @primaryKey
@@ -97,9 +97,11 @@ Apply this rule when creating or modifying Harper schema files, configuring `gra
97
97
  }
98
98
  ```
99
99
 
100
+ 9. **Use unique database names** in plugins or applications to avoid table naming collisions, since all tables default to the `data` database.
101
+
100
102
  ## Examples
101
103
 
102
- **Minimal two-table schema:**
104
+ **Minimal schema with two tables:**
103
105
 
104
106
  ```graphql
105
107
  type Dog @table {
@@ -115,7 +117,7 @@ type Breed @table {
115
117
  }
116
118
  ```
117
119
 
118
- **Table with expiration, eviction, and custom scan interval:**
120
+ **Table with expiration, eviction, and scan tuning:**
119
121
 
120
122
  ```graphql
121
123
  # Expire after 5 minutes, evict after 1 hour, scan every 10 minutes
@@ -125,60 +127,56 @@ type WeatherCache @table(expiration: 300, eviction: 3300, scanInterval: 600) {
125
127
  }
126
128
  ```
127
129
 
128
- **Exported table with timestamps and a hidden field:**
130
+ **Table with multiple `@table` arguments combined:**
129
131
 
130
132
  ```graphql
131
- type Customer @table @export(name: "customers") {
133
+ type Event @table(database: "analytics", expiration: 86400) {
132
134
  id: Long @primaryKey
133
135
  name: String @indexed
134
- createdAt: Long @createdTime
135
- updatedAt: Long @updatedTime
136
-
137
- """
138
- Internal — do not surface to external consumers.
139
- """
140
- creditScore: Int @hidden
141
136
  }
142
137
  ```
143
138
 
144
- **Multiple `@table` argument combinations:**
139
+ **Exported table with overridden table name:**
145
140
 
146
141
  ```graphql
147
- # Override table name
148
- type Product @table(table: "products") {
142
+ type Product @table(table: "products") @export(name: "products") {
149
143
  id: Long @primaryKey
144
+ category: String @indexed
145
+ price: Float @indexed
150
146
  }
147
+ ```
151
148
 
152
- # Place in a specific database
153
- type Order @table(database: "commerce") {
154
- id: Long @primaryKey
155
- }
149
+ **Table with timestamps and per-record expiration:**
156
150
 
157
- # Auto-expire records after 1 hour
158
- type Session @table(expiration: 3600) {
159
- id: Long @primaryKey
160
- userId: String
151
+ ```graphql
152
+ type Session @table {
153
+ id: ID @primaryKey
154
+ token: String
155
+ createdAt: Long @createdTime
156
+ updatedAt: Long @updatedTime
157
+ expiresAt: Long @expiresAt
161
158
  }
159
+ ```
162
160
 
163
- # Disable replication
164
- type LocalRecord @table(replicate: false) {
165
- id: Long @primaryKey
166
- value: String
167
- }
161
+ **Table with a hidden internal field:**
168
162
 
169
- # Combine multiple arguments
170
- type Event @table(database: "analytics", expiration: 86400) {
163
+ ```graphql
164
+ type Customer @table {
171
165
  id: Long @primaryKey
172
- name: String @indexed
166
+ name: String
167
+
168
+ """
169
+ Internal — do not surface to external consumers.
170
+ """
171
+ creditScore: Int @hidden
173
172
  }
174
173
  ```
175
174
 
176
175
  ## Notes
177
176
 
178
- - All tables default to the `"data"` database. When designing plugins or applications, use unique database names to avoid table naming collisions.
179
- - Schemas are flexible by default — records may include properties beyond those declared. Use `@sealed` to prevent this.
180
- - `expiration` marks a record stale; `eviction` controls how long after expiration the record is physically removed. Eviction does not remove records from secondary indexes — Harper fetches the full record on demand if an evicted record matches a query.
181
- - `scanInterval` is clock-aligned to the server's local timezone, not startup-aligned. The eviction schedule is deterministic across restarts.
182
- - If replication is disabled on a table and later re-enabled, writes made during the disabled period are not replicated retroactively.
183
- - `@hidden` (on types or fields) is a metadata-visibility directive only. Use `attribute_permissions` on roles to enforce data access control.
184
- - Disabling replication (`replicate: false`) and re-enabling it later will not catch up on writes made while replication was disabled.
177
+ - `@table`, `@export`, `@sealed`, and `@hidden` are type-level directives; `@primaryKey`, `@indexed`, `@embed`, `@createdTime`, `@updatedTime`, `@expiresAt`, and `@hidden` are field-level directives.
178
+ - `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 on demand when a query matches an evicted entry.
179
+ - `scanInterval` is clock-aligned to the server's local timezone, not startup-aligned. The server's startup time does not affect when eviction runs.
180
+ - Replication is enabled by default. If you disable replication on a table and re-enable it later, it will not catch up on writes made while replication was disabled.
181
+ - `@hidden` is a metadata-visibility directive only. Use `attribute_permissions` on roles to enforce data access control.
182
+ - A full-record `put` that omits an `@expiresAt` field clears it; a `patch` of other fields preserves it.