@abeedoo/radish-schemas 1.9.0 → 1.11.0
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/index.js +1 -1
- package/package.json +1 -1
- package/prompts/radish-types-generation.md +36 -6
- package/schemas/types.schema.json +15 -2
package/index.js
CHANGED
|
@@ -31,7 +31,7 @@ export {
|
|
|
31
31
|
* @property {string} minCliVersion - Minimum radish-cli version compatible with this package
|
|
32
32
|
*/
|
|
33
33
|
export const VERSIONING = {
|
|
34
|
-
packageVersion: '1.
|
|
34
|
+
packageVersion: '1.11.0',
|
|
35
35
|
currentSpecVersion: 1,
|
|
36
36
|
supportedSpecVersions: [1],
|
|
37
37
|
minCliVersion: '0.1.0'
|
package/package.json
CHANGED
|
@@ -127,7 +127,21 @@ The following entities are already provided by the system. DO NOT recreate them
|
|
|
127
127
|
```
|
|
128
128
|
`matchUserField` supports array match — if `user.orgIds` is an array, checks if `record.orgId` is in that array.
|
|
129
129
|
|
|
130
|
-
9. **
|
|
130
|
+
9. **Tenancy** (optional): If the app is multi-tenant — every record belongs to an organization, workspace, clinic, or similar — mark that one entity as the boundary:
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"Org": { "plural": "orgs", "tenancy": "boundary", "fields": { ... } },
|
|
134
|
+
"User": { "plural": "users", "fields": { "orgIds": { "type": "objectId[]", "ref": "Org" } } },
|
|
135
|
+
"Invoice": { "plural": "invoices", "fields": { "orgId": { "type": "objectId", "ref": "Org", "required": true } } }
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
- **`"boundary"`**: at most one entity per blueprint. Every entity with a ref to it is automatically confined to the caller's active tenant — no per-entity declaration needed. `User` must have exactly one field ref-ing it (`objectId[]` if a user can belong to several tenants, `objectId` for exactly one); that field is discovered automatically as the membership field.
|
|
139
|
+
- **`"global"`**: for an entity that refs the boundary entity but must NOT be confined by it — a tenant id recorded for provenance rather than ownership, such as an audit row naming the org an action occurred in that admins read across orgs. Rare; only needed when the ref exists.
|
|
140
|
+
- **Omit entirely** for ordinary entities, including reference data with no ref to the boundary entity — those are global by construction, since there is nothing to partition on.
|
|
141
|
+
|
|
142
|
+
Do not guess at this. A tenant and a group look identical in a blueprint (an entity, a membership field, an id on the records they cover), but a tenant **confines** results while a group **widens** them. Mark `"boundary"` only when the app description actually describes tenant isolation; if it describes teams, clans, or groups that share access, leave `tenancy` off and model it with `scope` instead.
|
|
143
|
+
|
|
144
|
+
10. **Enhanced Enums**: Use key-value pairs for better UX:
|
|
131
145
|
```json
|
|
132
146
|
{
|
|
133
147
|
"type": "enum",
|
|
@@ -139,9 +153,9 @@ The following entities are already provided by the system. DO NOT recreate them
|
|
|
139
153
|
}
|
|
140
154
|
```
|
|
141
155
|
|
|
142
|
-
|
|
156
|
+
11. **Performance**: Add `filters` array for searchable fields and `indexes` for performance
|
|
143
157
|
|
|
144
|
-
|
|
158
|
+
12. **Nested Objects**: Use `"type": "object"` with `"fields"` for nested structures:
|
|
145
159
|
```json
|
|
146
160
|
{
|
|
147
161
|
"type": "object",
|
|
@@ -153,7 +167,7 @@ The following entities are already provided by the system. DO NOT recreate them
|
|
|
153
167
|
}
|
|
154
168
|
```
|
|
155
169
|
|
|
156
|
-
|
|
170
|
+
13. **Search Index** (optional): Enable full-text search with engine-specific adapters:
|
|
157
171
|
```json
|
|
158
172
|
{
|
|
159
173
|
"Product": {
|
|
@@ -188,8 +202,24 @@ The following entities are already provided by the system. DO NOT recreate them
|
|
|
188
202
|
- **sync**: `inline` (immediate) or `background` (via jobs)
|
|
189
203
|
- **vector**: Enable vector/embedding search with `sourceFields` to generate embeddings from
|
|
190
204
|
- **searchable**: Each entry is either a plain field name or `{ "field": "name", "stem": true }`. Stemming reduces terms to their word stems at index time (`computers` → `computer`), so a plural query matches singular text as an exact match rather than relying on typo correction. Set `stem: true` only on prose fields (name, description, summary, body). Never stem identifiers (`sku`, `slug`, `code`), brand or category values, anything listed under `filterable`/`facetable`, or non-string fields — stemming a facet value breaks the filter that reads it.
|
|
205
|
+
- **indexFilter**: A MongoDB-style query object controlling which documents belong in the index, e.g. `{ "status": { "$ne": "discontinued" } }`. Note the delete semantics: it is applied on every create/update sync, and a document that stops matching is **deleted** from the search engine, not merely skipped — a product flipped to `discontinued` disappears from search immediately. Use it for excluding archived, draft, or discontinued records; do not use it for anything a user should be able to filter on themselves (that is `filterable`).
|
|
206
|
+
- **configureBy**: `"rows"` marks an entity whose *records* are vocabulary for other entities' search indexes — each row (or group of rows) becomes a candidate field in the consuming entity's search configuration. Use it for entities that define terminology rather than content: `Attribute`, `Term`, `Tag`, `Taxonomy`. The system discovers code/label fields and grouping refs from the entity's own field definitions, so no extra configuration is needed. A vocabulary entity that is not itself searchable needs only `"search": { "configureBy": "rows" }`; combine with `"enabled": true` when the entity should also have its own index:
|
|
207
|
+
```json
|
|
208
|
+
"Term": {
|
|
209
|
+
"plural": "terms",
|
|
210
|
+
"search": {
|
|
211
|
+
"enabled": true,
|
|
212
|
+
"configureBy": "rows",
|
|
213
|
+
"fields": {
|
|
214
|
+
"searchable": [{ "field": "name", "stem": true }, "slug"],
|
|
215
|
+
"filterable": ["taxonomyId"]
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"fields": { ... }
|
|
219
|
+
}
|
|
220
|
+
```
|
|
191
221
|
|
|
192
|
-
|
|
222
|
+
14. **Expiration / TTL** (optional): For entities whose records should be purged automatically (sessions, tokens, logs, temporary uploads), use the entity-level `ttl` shorthand:
|
|
193
223
|
```json
|
|
194
224
|
{
|
|
195
225
|
"Session": {
|
|
@@ -209,7 +239,7 @@ The following entities are already provided by the system. DO NOT recreate them
|
|
|
209
239
|
]
|
|
210
240
|
```
|
|
211
241
|
|
|
212
|
-
|
|
242
|
+
15. **Automatic Fields** (DO NOT add these manually):
|
|
213
243
|
- When `"defaults": { "timestamps": true }` is set, `createdAt` and `updatedAt` are added automatically
|
|
214
244
|
- When `"defaults": { "owned": true }` is set, `ownerId` is added automatically
|
|
215
245
|
- Adding these fields manually causes duplication
|
|
@@ -58,6 +58,11 @@
|
|
|
58
58
|
"enum": ["user", "system", "tenant"],
|
|
59
59
|
"default": "user"
|
|
60
60
|
},
|
|
61
|
+
"tenancy": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"enum": ["boundary", "global"],
|
|
64
|
+
"description": "\"boundary\": this entity is the tenancy boundary (Org, Workspace, Clinic) — every entity with a ref to it is confined to the caller's active tenant. \"global\": this entity has a ref to the boundary entity but is NOT confined by it (a tenant id kept for provenance, not ownership). Absent: ordinary — confined if it refs the boundary entity, unconstrained if it does not."
|
|
65
|
+
},
|
|
61
66
|
"extends": {
|
|
62
67
|
"type": "string",
|
|
63
68
|
"description": "Base type to extend from. Can be EntityBase, SystemEntityBase, ContentBase, or another entity name.",
|
|
@@ -90,8 +95,7 @@
|
|
|
90
95
|
},
|
|
91
96
|
"search": {
|
|
92
97
|
"type": "object",
|
|
93
|
-
"description": "Search index configuration for this entity. Generates search adapters and typed search index classes.",
|
|
94
|
-
"required": ["enabled"],
|
|
98
|
+
"description": "Search index configuration for this entity. Generates search adapters and typed search index classes. An entity with configureBy: 'rows' declares its records as vocabulary for other entities' indexes.",
|
|
95
99
|
"additionalProperties": false,
|
|
96
100
|
"properties": {
|
|
97
101
|
"enabled": {
|
|
@@ -173,6 +177,15 @@
|
|
|
173
177
|
"description": "Fields to generate embeddings from"
|
|
174
178
|
}
|
|
175
179
|
}
|
|
180
|
+
},
|
|
181
|
+
"configureBy": {
|
|
182
|
+
"type": "string",
|
|
183
|
+
"enum": ["rows"],
|
|
184
|
+
"description": "How this entity participates in search configuration. 'rows' means this entity's records are vocabulary for other entities' indexes — each row (or group of rows) becomes a candidate field in the consumer's search config UI. The system discovers code/label fields and grouping refs from the entity's own field definitions. Combinable with enabled: true for entities that are both searchable and vocabulary sources (e.g. Term)."
|
|
185
|
+
},
|
|
186
|
+
"indexFilter": {
|
|
187
|
+
"type": "object",
|
|
188
|
+
"description": "MongoDB-style query object controlling index membership. Documents matching are upserted to the search engine; documents NOT matching are DELETED from it. Applied on every create/update sync — a product flipped to discontinued is removed, not skipped. Example: { \"status\": { \"$ne\": \"discontinued\" } }"
|
|
176
189
|
}
|
|
177
190
|
}
|
|
178
191
|
},
|