@uniweb/api 0.5.1 → 0.6.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/README.md +0 -1
- package/package.json +1 -1
- package/src/mock/index.js +4 -10
- package/src/mock/seed.js +7 -8
- package/src/mock/store.js +17 -10
package/README.md
CHANGED
|
@@ -179,7 +179,6 @@ export default createMockBackend({
|
|
|
179
179
|
],
|
|
180
180
|
schemas: {
|
|
181
181
|
'@acme/track': {
|
|
182
|
-
creatable_by: 'unit_members', // the operator only; 'any_user' is the default
|
|
183
182
|
sections: {
|
|
184
183
|
track: { kind: 'single', brief: true, fields: { name: { type: 'string', required: true } } },
|
|
185
184
|
sessions: { kind: 'multi', fields: { title: { type: 'string', required: true } } },
|
package/package.json
CHANGED
package/src/mock/index.js
CHANGED
|
@@ -39,7 +39,7 @@ import { DEFAULT_SEED } from './seed.js'
|
|
|
39
39
|
* One operator (`operator: true` on a seeded account) and members; a member reads
|
|
40
40
|
* and writes their own entities and nothing of another's (unless the seed's
|
|
41
41
|
* `memberFloor` says the service lets them), the operator reads and writes all;
|
|
42
|
-
*
|
|
42
|
+
* anyone signed in may create entries of any Model (Models are open); sign-up
|
|
43
43
|
* leaves an account unverified until the link in `mock.outbox` is followed. It
|
|
44
44
|
* does not model entitlements behind `via`, nested sections, or second factors.
|
|
45
45
|
*
|
|
@@ -286,15 +286,9 @@ export function createMockBackend({ seed = DEFAULT_SEED, prefix = '/_api', signe
|
|
|
286
286
|
}
|
|
287
287
|
const model = store.model(q.get(PARAM.model))
|
|
288
288
|
if (!model) return notFound('model', q.get(PARAM.model))
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
title: 'Forbidden',
|
|
293
|
-
detail: `use_model on model ${model.uuid} not permitted`,
|
|
294
|
-
op: 'use_model',
|
|
295
|
-
target: `model ${model.uuid}`,
|
|
296
|
-
})
|
|
297
|
-
}
|
|
289
|
+
// ⭐ No creation gate: Models are open (2026-09-23) — anyone signed in may create
|
|
290
|
+
// entries of any Model. Until then `creatable_by: unit_members` answered `403
|
|
291
|
+
// use_model` here for a member.
|
|
298
292
|
// Any other top-level key is ignored, exactly as the backend ignores it.
|
|
299
293
|
return answer(store.create(model, items.map((i) => ({ ...i, data: i.data ?? {} }))))
|
|
300
294
|
}
|
package/src/mock/seed.js
CHANGED
|
@@ -7,24 +7,24 @@
|
|
|
7
7
|
*
|
|
8
8
|
* - **`accounts`** — `operator: true` marks the account that runs the site's
|
|
9
9
|
* service. Everyone else is a member. Seeded accounts are verified.
|
|
10
|
-
* - **`schemas`** — per Model: `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* - **`schemas`** — per Model: `sections`, the Model's sections as the framework
|
|
11
|
+
* lowers them. With `sections`, writes are shape-checked. Models are open:
|
|
12
|
+
* anyone signed in may create entries of any of them.
|
|
13
13
|
* - **`entities`** — `uuid` must be a UUID, as it is on the wire; content is items,
|
|
14
14
|
* each naming its section. The entity's `brief` is derived from its brief
|
|
15
15
|
* section's item, as the backend derives it.
|
|
16
16
|
*/
|
|
17
17
|
export const DEFAULT_SEED = {
|
|
18
18
|
accounts: [
|
|
19
|
-
// The organiser runs the site,
|
|
19
|
+
// The organiser runs the site, and the programme's tracks are theirs.
|
|
20
20
|
{ username: 'organiser', password: 'organiser', operator: true },
|
|
21
|
-
// The attendee is a member
|
|
22
|
-
//
|
|
21
|
+
// The attendee is a member. They may make entries of their own — recording
|
|
22
|
+
// attending is the demo — and may not edit the organiser's programme: the entry
|
|
23
|
+
// decides, server-side, and that refusal is the other half of the demo.
|
|
23
24
|
{ username: 'attendee', password: 'attendee' },
|
|
24
25
|
],
|
|
25
26
|
schemas: {
|
|
26
27
|
'@/track': {
|
|
27
|
-
creatable_by: 'unit_members',
|
|
28
28
|
sections: {
|
|
29
29
|
track: { kind: 'single', brief: true, fields: { name: { type: 'string', required: true } } },
|
|
30
30
|
sessions: {
|
|
@@ -36,7 +36,6 @@ export const DEFAULT_SEED = {
|
|
|
36
36
|
// Check-ins are insert-only: an attendee may record attending, and nobody —
|
|
37
37
|
// including them — may edit or remove it afterwards.
|
|
38
38
|
'@/attendance': {
|
|
39
|
-
creatable_by: 'any_user',
|
|
40
39
|
sections: {
|
|
41
40
|
attendance: { kind: 'single', brief: true, fields: { who: { type: 'string' } } },
|
|
42
41
|
checkins: { kind: 'multi', append_only: true, fields: { session: { type: 'string' }, at: { type: 'string' } } },
|
package/src/mock/store.js
CHANGED
|
@@ -94,7 +94,8 @@ export class MockStore {
|
|
|
94
94
|
* @param {object[]} [seed.accounts] - `{ username, password, email?, handle?, operator? }`.
|
|
95
95
|
* Seeded accounts are verified. (`units: [...]` non-empty, the older spelling,
|
|
96
96
|
* also marks the operator.)
|
|
97
|
-
* @param {object} [seed.schemas] - `{ '@scope/name': {
|
|
97
|
+
* @param {object} [seed.schemas] - `{ '@scope/name': { sections?, append_only?, migration_debt? } }`.
|
|
98
|
+
* ⛔ No `creatable_by` — retired 2026-09-23, Models are open; a seed carrying it is refused.
|
|
98
99
|
* `sections` is the LOWERED sections map (`{ [name]: { kind | multiple, brief, append_only, fields } }`)
|
|
99
100
|
* as the framework's normalizer produces it; with it, writes are shape-checked
|
|
100
101
|
* (`./schema-shape.js`). Without it the sections are inferred: a one-item brief
|
|
@@ -117,6 +118,13 @@ export class MockStore {
|
|
|
117
118
|
for (const a of seed.accounts || []) this.addAccount(a, { verified: true })
|
|
118
119
|
|
|
119
120
|
this.schemas = seed.schemas || {}
|
|
121
|
+
for (const [name, decl] of Object.entries(this.schemas)) {
|
|
122
|
+
if (decl && (decl.creatable_by !== undefined || decl.creatableBy !== undefined)) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`[uniweb/api mock] ${name}: creatable_by is retired — Models are open: anyone signed in may create entries of any Model, and who may see or edit an entry is decided by the entry. Remove it.`
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
120
128
|
const floors = seed.memberFloor != null && typeof seed.memberFloor === 'object' ? Object.values(seed.memberFloor) : [seed.memberFloor]
|
|
121
129
|
for (const floor of floors) {
|
|
122
130
|
if (floor != null && !['read', 'edit'].includes(floor)) {
|
|
@@ -269,7 +277,6 @@ export class MockStore {
|
|
|
269
277
|
id: (this.ids.model += 1),
|
|
270
278
|
uuid: newUuid(),
|
|
271
279
|
name,
|
|
272
|
-
creatable_by: decl?.creatable_by || 'any_user',
|
|
273
280
|
decl: decl || {},
|
|
274
281
|
sections: [],
|
|
275
282
|
}
|
|
@@ -314,14 +321,14 @@ export class MockStore {
|
|
|
314
321
|
return null
|
|
315
322
|
}
|
|
316
323
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
maySeeSchema(
|
|
324
|
-
return !!this.account
|
|
324
|
+
/**
|
|
325
|
+
* The definition is readable by anyone signed in. ⭐ Models are open (2026-09-23):
|
|
326
|
+
* anyone signed in may create entries of any Model, and what protects content is the
|
|
327
|
+
* entry — `mayEdit`, `mayRead` — never who may create. *(Until then a Model declaring
|
|
328
|
+
* `creatable_by: unit_members` was the operator's alone, and so was its definition.)*
|
|
329
|
+
*/
|
|
330
|
+
maySeeSchema() {
|
|
331
|
+
return !!this.account
|
|
325
332
|
}
|
|
326
333
|
|
|
327
334
|
/** `GET /models/@scope/name`, in the backend's shape. */
|