@abide/abide 0.123.0 → 0.123.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.123.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 4ce217a: `validateJson` reads a member left `undefined` as absent, so an `args: jsonSchema(...)` gate accepts `note: undefined` where `note` is optional rather than refusing it 422, as the derived args schema already did (RULEBOOK 19.25).
8
+
9
+ ## 0.123.1
10
+
11
+ ### Patch Changes
12
+
13
+ - the args-as-sent walk returns the args it does not copy, walks a cycle once, and keeps a parsed __proto__ a member (RULEBOOK 19.25) ([`ee02cac`](https://github.com/briancray/abide/commit/ee02cacbb64f00374ae98154e788c59c5a4ec49b))
14
+ - bf77c28: `abide check` no longer fails when an inline component is passed to a component that sits inside another component's body: the outer close tag's read in the check's projection now ends with a `;`, so the braces that scope the passed component are not parsed as an arrow function.
15
+ - 7c24094: A no-argument mutation behind `middleware` no longer starts its body before its rungs run. Dropping the previous call's answer built the request's instance of the handler, which started the body ahead of every rung, so a refusing rung (`signedIn`) answered after the body had already run. Calling the same mutation twice in one request now writes twice behind rungs as well (RULEBOOK 16.7, 16.11, 16.16).
16
+ - 94fbea7: A handler's derived args schema reads a member left `undefined` as absent, so an in-process call like `getResearch({ home, topic: undefined })` is answered as the wire answers it rather than refused 422 (RULEBOOK 19.25, 15.8).
17
+
3
18
  ## 0.123.0
4
19
 
5
20
  ### Minor Changes
package/docs/REGISTRY.md CHANGED
@@ -279,7 +279,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
279
279
  | `Paths<T>` | `string` | The dot-joined leaf paths of a type. | 19.5 |
280
280
  | `JsonSchema` | `JsonValue` | A JSON Schema document, the native form. | 19.7, 19.14 |
281
281
  | `jsonSchema` | `<T>(document: JsonSchema) => StandardSchemaV1<T>` | A JSON Schema document as a `Schema`, carrying its validator. | 19.13, 19.14, 19.23, 20.11 |
282
- | `validateJson` | `<T>(schema: JsonSchema, value: unknown) => Issues<T> \| null` | The native validator. | 19.13 |
282
+ | `validateJson` | `<T>(schema: JsonSchema, value: unknown) => Issues<T> \| null` | The native validator. | 19.13, 19.25 |
283
283
 
284
284
  ## Headers abide generates
285
285
 
package/docs/RULEBOOK.md CHANGED
@@ -1335,6 +1335,9 @@ to, and MUST NOT change the type of the value that slot gates.
1335
1335
  19.24 The value a body produces MUST be typed from the body alone, and a `schema` declared beside it
1336
1336
  MUST NOT change that type.
1337
1337
 
1338
+ 19.25 `validateJson` MUST read a member left `undefined`, at any depth of a record, as absent to
1339
+ `required`, `properties` and `additionalProperties` — in process as the wire carries it (30.28).
1340
+
1338
1341
  # 20. Generated surfaces
1339
1342
 
1340
1343
  20.1 Every surface `Clients` names MUST be derived from the route table, and MUST NOT be authored
package/docs/abide.md CHANGED
@@ -8158,7 +8158,7 @@ Transports: what is written in place rather than imported.
8158
8158
 
8159
8159
  | Name | Signature | Meaning | Rules |
8160
8160
  | --- | --- | --- | --- |
8161
- | `validateJson` | `<T>(schema: JsonSchema, value: unknown) => Issues<T> \| null` | The native validator. | [19.13](/rulebook#19.13) |
8161
+ | `validateJson` | `<T>(schema: JsonSchema, value: unknown) => Issues<T> \| null` | The native validator. | [19.13](/rulebook#19.13), [19.25](/rulebook#19.25) |
8162
8162
 
8163
8163
  ## Taught in
8164
8164
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abide/abide",
3
- "version": "0.123.0",
3
+ "version": "0.123.2",
4
4
  "type": "module",
5
5
  "description": "Isomorphic type-safe framework for reactive async interfaces for humans and machines",
6
6
  "license": "MIT",
@@ -391,11 +391,13 @@ function project(file: AbideFile): Projection {
391
391
  }
392
392
  text += '}))\n'
393
393
  // 38.116 — a close tag of its own is a second read of the name, so it is renamed with it.
394
+ // Ended by its `;`: the braces a passed component is scoped in (32.51) can follow, and
395
+ // `(Name)\n{` parses as an arrow function's head.
394
396
  const closed = file.origins.closes.get(node)
395
397
  if (closed !== undefined) {
396
398
  text += ';('
397
399
  copied(node.tag, closed)
398
- text += ')\n'
400
+ text += ');\n'
399
401
  }
400
402
  for (const attribute of refused)
401
403
  slots.set(
package/src/shared/rpc.ts CHANGED
@@ -407,6 +407,12 @@ export function declare(method: Method): Declare {
407
407
  rungs.length === 0
408
408
  ? null
409
409
  : compose(rungs, async (ctx: RpcContext) => {
410
+ // 16.7 behind rungs — the handler's spent answer is dropped HERE, once the
411
+ // rungs let the call through: reading an unkeyed value's cell builds this
412
+ // request's instance, which starts its body (1.7), and dropped in `call` that
413
+ // ran the body ahead of every rung (16.11, 16.16).
414
+ if (cacheOptions !== undefined && !keyed)
415
+ dropAnswered(value)
410
416
  const reached = reach(argsOf(ctx))
411
417
  const started = performance.now()
412
418
  try {
@@ -464,8 +470,9 @@ export function declare(method: Method): Declare {
464
470
  }
465
471
  // 16.7 — a mutation's call is a write each time, even where the last one settled this
466
472
  // turn: a keyed value's reach evicts an outlived entry itself, and an unkeyed one's
467
- // spent answer is dropped here.
468
- if (cacheOptions !== undefined && !keyed) dropAnswered(value)
473
+ // spent answer is dropped here — or, behind rungs, in the onion's center.
474
+ if (cacheOptions !== undefined && !keyed && wrapped === null)
475
+ dropAnswered(value)
469
476
  const reached = callLoad(
470
477
  reachThrough,
471
478
  args,
@@ -486,6 +486,17 @@ function checkArray(
486
486
  check(items, value[index], join(path, String(index)), found)
487
487
  }
488
488
 
489
+ /**
490
+ * Whether `value` carries `key` as JSON would: an own member, and not one left `undefined`.
491
+ *
492
+ * 19.25 — `undefined` IS NOT A JSON VALUE, so a member holding it is the member the wire leaves out:
493
+ * JSON drops it and a URL omits it (30.28). Read as present, `note?: string` refused a call in process
494
+ * passing `note: undefined` at 422 that the same call over the wire was answered for.
495
+ */
496
+ function holds(value: Record<string, unknown>, key: string): boolean {
497
+ return value[key] !== undefined && Object.hasOwn(value, key)
498
+ }
499
+
489
500
  function checkObject(
490
501
  node: Node,
491
502
  value: Record<string, unknown>,
@@ -504,7 +515,7 @@ function checkObject(
504
515
  // `required: ['toString']` was satisfied by `{}`, and — the one that matters —
505
516
  // `'__proto__' in declared` answered true, so `additionalProperties: false` accepted
506
517
  // `{"__proto__": …}` as a declared member and handed the object on unchecked.
507
- if (!Object.hasOwn(value, key))
518
+ if (!holds(value, key))
508
519
  noteOr(found, join(path, key), 'is required')
509
520
  }
510
521
  }
@@ -515,7 +526,7 @@ function checkObject(
515
526
  if (!Object.hasOwn(declared, key)) continue
516
527
  // An absent member is `required`'s business, not its schema's — checking it
517
528
  // here would report every optional member as the wrong type.
518
- if (!Object.hasOwn(value, key)) continue
529
+ if (!holds(value, key)) continue
519
530
  check(
520
531
  declared[key] as JsonValue,
521
532
  value[key],
@@ -527,7 +538,7 @@ function checkObject(
527
538
  const extra = node.additionalProperties
528
539
  if (extra === undefined) return
529
540
  for (const key in value) {
530
- if (!Object.hasOwn(value, key)) continue
541
+ if (!holds(value, key)) continue
531
542
  if (declared !== null && Object.hasOwn(declared, key)) continue
532
543
  if (extra === false) {
533
544
  noteOr(found, join(path, key), 'is not a known member')