@nubbin/core 0.1.0-rc.0 → 0.1.0-rc.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.
- package/README.md +37 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +20 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @nubbin/core
|
|
2
|
+
|
|
3
|
+
The contract every other Nubbin package is built around. It defines what a block is, keeps the
|
|
4
|
+
catalog and the registry apart, and compiles a page document into an immutable artifact.
|
|
5
|
+
|
|
6
|
+
It has one runtime dependency — [Standard Schema](https://standardschema.dev) — and imports no
|
|
7
|
+
validator, no framework and no node builtin. A build gate fails on any of the three, so the
|
|
8
|
+
claim that it runs in a browser, a worker and a build step is checked rather than asserted.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @nubbin/core@rc
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { defineBlock, defineCatalog, createRegistry, compile } from "@nubbin/core";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
const heroSchema = z.object({ title: z.string() });
|
|
19
|
+
|
|
20
|
+
export const heroBlock = defineBlock({
|
|
21
|
+
name: "Hero",
|
|
22
|
+
schema: heroSchema,
|
|
23
|
+
component: Hero,
|
|
24
|
+
version: 1,
|
|
25
|
+
slots: {},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const artifact = compile(documentVersion, catalog, registry, "/promotions/summer");
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Props are inferred from the schema with `InferProps<typeof heroSchema>`, so nothing declares a
|
|
32
|
+
block's shape twice.
|
|
33
|
+
|
|
34
|
+
**Release candidate.** The API is settled enough to build against and not yet stable.
|
|
35
|
+
|
|
36
|
+
The design record, including the paths not taken, is at
|
|
37
|
+
<https://effekt.github.io/nubbin/>. MIT.
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ type UnknownProps = Record<string, unknown>;
|
|
|
11
11
|
*/
|
|
12
12
|
type InferProps<Schema extends StandardSchemaV1> = StandardSchemaV1.InferOutput<Schema>;
|
|
13
13
|
interface SlotConstraint {
|
|
14
|
-
/** Block names permitted here. Omitted means any registered block. */
|
|
14
|
+
/** Block names permitted here, each resolved at registration. Omitted means any registered block. */
|
|
15
15
|
allow?: readonly string[];
|
|
16
16
|
min?: number;
|
|
17
17
|
max?: number;
|
|
@@ -179,6 +179,9 @@ declare function compile(version: DocumentVersion, catalog: Catalog, registry: R
|
|
|
179
179
|
* Sorted by name so registration order cannot change the fingerprint, and built from name and
|
|
180
180
|
* version alone so unrelated edits — a slot constraint, a deprecation — do not invalidate every
|
|
181
181
|
* artifact compiled before them.
|
|
182
|
+
*
|
|
183
|
+
* Slot `allow` lists resolve once the whole array is ingested, so a block may name a sibling
|
|
184
|
+
* registered after it.
|
|
182
185
|
*/
|
|
183
186
|
declare function createRegistry(blocks: readonly Block[]): Registry;
|
|
184
187
|
|
package/dist/index.js
CHANGED
|
@@ -401,7 +401,7 @@ function validateStructure(version, registry) {
|
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
// src/version.constants.ts
|
|
404
|
-
var NUBBIN_VERSION = "0.0.
|
|
404
|
+
var NUBBIN_VERSION = "0.1.0-rc.4";
|
|
405
405
|
|
|
406
406
|
// src/compile.ts
|
|
407
407
|
function compile(version, catalog, registry, route) {
|
|
@@ -423,6 +423,24 @@ function compile(version, catalog, registry, route) {
|
|
|
423
423
|
return { ...content, hash: hashArtifact(content) };
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
// src/unknownAllowEntries.ts
|
|
427
|
+
function unknownAllowEntries(block, known) {
|
|
428
|
+
return Object.entries(block.slots).flatMap(
|
|
429
|
+
([slot, constraint]) => (constraint.allow ?? []).filter((allowed) => !known.has(allowed)).map((allowed) => `"${allowed}" (${block.name}.${slot})`)
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/assertSlotAllows.ts
|
|
434
|
+
function assertSlotAllows(blocks) {
|
|
435
|
+
const known = new Set(blocks.map((block) => block.name));
|
|
436
|
+
const unresolved = blocks.flatMap((block) => unknownAllowEntries(block, known));
|
|
437
|
+
if (unresolved.length > 0) {
|
|
438
|
+
throw new Error(
|
|
439
|
+
`Slot allow lists name ${unresolved.join(", ")}, which no registered block defines. Registered blocks: ${[...known].sort().join(", ")}`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
426
444
|
// src/createRegistry.ts
|
|
427
445
|
function createRegistry(blocks) {
|
|
428
446
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -434,6 +452,7 @@ function createRegistry(blocks) {
|
|
|
434
452
|
}
|
|
435
453
|
byName.set(block.name, block);
|
|
436
454
|
}
|
|
455
|
+
assertSlotAllows([...byName.values()]);
|
|
437
456
|
const signature = [...byName.values()].map((block) => `${block.name}@${block.version}`).sort().join("\n");
|
|
438
457
|
const fingerprint = fnv1a(signature);
|
|
439
458
|
return {
|
package/package.json
CHANGED