@ekka-ai/shelves 0.2.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 +44 -0
- package/LICENSE +8 -0
- package/README.md +70 -0
- package/dist/check.d.ts +26 -0
- package/dist/check.js +151 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +124 -0
- package/dist/contracts/PINNED.json +8 -0
- package/dist/contracts/gate/attested/shelf.v1.json +77 -0
- package/dist/contracts/shelves/protocol-v1.schema.json +595 -0
- package/dist/contracts/shelves/registry.json +129 -0
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +44 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +22 -0
- package/dist/plugin.d.ts +43 -0
- package/dist/plugin.js +125 -0
- package/dist/protocol.d.ts +131 -0
- package/dist/protocol.js +166 -0
- package/dist/registry.d.ts +46 -0
- package/dist/registry.js +131 -0
- package/dist/scaffold.d.ts +20 -0
- package/dist/scaffold.js +200 -0
- package/dist/shelf.d.ts +85 -0
- package/dist/shelf.js +71 -0
- package/package.json +56 -0
package/dist/shelf.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A SHELF, AS A BUSINESS API WRITES IT (E1 spec, section 6 P1).
|
|
3
|
+
*
|
|
4
|
+
* `npx @ekka-ai/shelves add hr.salary` writes a class that extends `Shelf` with empty typed methods. The
|
|
5
|
+
* company fills the method bodies with its own code: that code IS the mapping from its data to the
|
|
6
|
+
* shelf, and EKKA keeps no mapping table (owner rule 3).
|
|
7
|
+
*
|
|
8
|
+
* - A PREDEFINED shelf takes its keys, fields, verbs and page cap from EKKA's registry.
|
|
9
|
+
* `predefined('hr.salary')` refuses an id the registry does not define.
|
|
10
|
+
* - A PRIVATE shelf declares its shape in the file. `privateShelf(...)` refuses an id the registry
|
|
11
|
+
* DOES define (owner rule 2): a company may not publish its own `hr.salary`.
|
|
12
|
+
* - `roles` is read from ONE policy file, `src/ekka/policy.ts`: a provider role is written in
|
|
13
|
+
* exactly one place (spec v3.2, section 12).
|
|
14
|
+
*/
|
|
15
|
+
import { assertShape, loadRegistry, SEGMENT } from './registry.js';
|
|
16
|
+
export const predefined = (id) => {
|
|
17
|
+
const reg = loadRegistry();
|
|
18
|
+
const shelf = reg.shelves.get(id);
|
|
19
|
+
if (!shelf) {
|
|
20
|
+
throw new Error(`"${id}" is not a predefined EKKA shelf (registry ${reg.version}: ${[...reg.shelves.keys()].join(', ')}). ` +
|
|
21
|
+
'A shelf of your own is declared with privateShelf(id, shape).');
|
|
22
|
+
}
|
|
23
|
+
return { id, interfaceMajor: shelf.interfaceMajor, isPrivate: false, shape: shelf };
|
|
24
|
+
};
|
|
25
|
+
export const privateShelf = (id, shape) => {
|
|
26
|
+
const reg = loadRegistry();
|
|
27
|
+
// ⛔ OWNER RULE 2: a private shelf may never reuse a predefined id. A plan naming hr.salary must
|
|
28
|
+
// mean EKKA's hr.salary everywhere, or third-party plans mean nothing.
|
|
29
|
+
if (reg.shelves.has(id)) {
|
|
30
|
+
throw new Error(`"${id}" is a predefined EKKA shelf; a private shelf may not reuse its id. Use predefined('${id}').`);
|
|
31
|
+
}
|
|
32
|
+
const full = { keys: shape.keys, fields: shape.fields, verbs: shape.verbs, pageCap: shape.pageCap ?? 100 };
|
|
33
|
+
assertShape(id, full);
|
|
34
|
+
return { id, interfaceMajor: null, isPrivate: true, shape: full };
|
|
35
|
+
};
|
|
36
|
+
export class ShelfRefusal extends Error {
|
|
37
|
+
code;
|
|
38
|
+
constructor(code, message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.name = 'ShelfRefusal';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const notImplemented = (id, verb) => new ShelfRefusal('not_implemented', `${id} ${verb} has no method body yet`);
|
|
45
|
+
/**
|
|
46
|
+
* `By` is the unique key set `read` takes; `Filter` is the equality filter `list` takes (any subset
|
|
47
|
+
* of the declared keys, unique or not); `R` is one record, exactly the shelf's fields.
|
|
48
|
+
*/
|
|
49
|
+
export class Shelf {
|
|
50
|
+
}
|
|
51
|
+
/** What a verb needs before it may be served: a method, a role, and a fixture to probe it with. */
|
|
52
|
+
export const verbProblems = (s) => {
|
|
53
|
+
const out = [];
|
|
54
|
+
const { id, shape } = s.descriptor;
|
|
55
|
+
for (const v of shape.verbs) {
|
|
56
|
+
if (typeof s[v] !== 'function')
|
|
57
|
+
out.push(`${id} serves "${v}" but the class has no ${v}() method`);
|
|
58
|
+
const role = s.roles[v];
|
|
59
|
+
if (!role)
|
|
60
|
+
out.push(`${id} "${v}" has no provider role in src/ekka/policy.ts`);
|
|
61
|
+
else if (!SEGMENT.test(role))
|
|
62
|
+
out.push(`${id} "${v}" role "${role}" must be one lowercase word (letters, digits, _)`);
|
|
63
|
+
if (!s.fixtures[v])
|
|
64
|
+
out.push(`${id} "${v}" has no synthetic fixture, so it cannot be probed`);
|
|
65
|
+
}
|
|
66
|
+
for (const v of ['read', 'list']) {
|
|
67
|
+
if (!shape.verbs.includes(v) && typeof s[v] === 'function')
|
|
68
|
+
out.push(`${id} has a ${v}() method but does not serve "${v}"`);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ekka-ai/shelves",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "EKKA shelves for a Business API: scaffold a typed shelf class, check it, mount the shelf protocol v1 (E1, ekka-ai/.github#674)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ekka-shelves": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/**/*",
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/ekka-ai/ekka-shelves.git"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"registry": "https://registry.npmjs.org",
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"packageManager": "pnpm@10.20.0",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.json && node scripts/bundle-contracts.mjs && chmod +x dist/cli.js",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.test.json",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"prepublishOnly": "node scripts/publish-guard.mjs"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.10.0",
|
|
37
|
+
"typescript": "^5.7.0",
|
|
38
|
+
"vitest": "^2.1.0",
|
|
39
|
+
"fastify": "5.12.5",
|
|
40
|
+
"typebox": "1.3.34",
|
|
41
|
+
"ajv": "8.20.0",
|
|
42
|
+
"tsx": "4.23.15",
|
|
43
|
+
"@ekka-ai/contracts": "0.42.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"fastify": "^5.0.0",
|
|
47
|
+
"typebox": "^1.3.0",
|
|
48
|
+
"tsx": "^4.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"tsx": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"license": "SEE LICENSE IN LICENSE"
|
|
56
|
+
}
|