@sentropic/artifacts 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +86 -2
- package/dist/adapters/postgres.d.ts +35 -0
- package/dist/adapters/postgres.js +301 -0
- package/dist/adapters/s3.d.ts +19 -0
- package/dist/adapters/s3.js +70 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.js +44 -0
- package/dist/codecs.d.ts +12 -0
- package/dist/codecs.js +77 -0
- package/dist/content/api-profile.d.ts +38 -0
- package/dist/content/api-profile.js +40 -0
- package/dist/content/grants.d.ts +59 -0
- package/dist/content/grants.js +134 -0
- package/dist/content/handler.d.ts +34 -0
- package/dist/content/handler.js +127 -0
- package/dist/content/headers.d.ts +25 -0
- package/dist/content/headers.js +56 -0
- package/dist/content/routing.d.ts +13 -0
- package/dist/content/routing.js +59 -0
- package/dist/contracts.d.ts +102 -0
- package/dist/contracts.js +25 -0
- package/dist/http.d.ts +43 -0
- package/dist/http.js +502 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +29 -0
- package/dist/mcp.d.ts +68 -0
- package/dist/mcp.js +293 -0
- package/dist/parameters.d.ts +29 -0
- package/dist/parameters.js +65 -0
- package/dist/provider-definition.d.ts +36 -0
- package/dist/provider-definition.js +45 -0
- package/dist/server/access.d.ts +39 -0
- package/dist/server/access.js +53 -0
- package/dist/server/authorization.d.ts +17 -0
- package/dist/server/authorization.js +12 -0
- package/dist/server/bindings.d.ts +34 -0
- package/dist/server/bindings.js +60 -0
- package/dist/server/fork.d.ts +51 -0
- package/dist/server/fork.js +75 -0
- package/dist/server/gc.d.ts +24 -0
- package/dist/server/gc.js +90 -0
- package/dist/server/idempotency.d.ts +23 -0
- package/dist/server/idempotency.js +38 -0
- package/dist/server/lifecycle.d.ts +57 -0
- package/dist/server/lifecycle.js +184 -0
- package/dist/server/object-intents.d.ts +41 -0
- package/dist/server/object-intents.js +62 -0
- package/dist/server/ports.d.ts +34 -0
- package/dist/server/ports.js +50 -0
- package/dist/server/publication.d.ts +67 -0
- package/dist/server/publication.js +154 -0
- package/dist/server/reader-profile.d.ts +27 -0
- package/dist/server/reader-profile.js +18 -0
- package/dist/server/runtime.d.ts +33 -0
- package/dist/server/runtime.js +44 -0
- package/dist/server/service.d.ts +19 -0
- package/dist/server/service.js +32 -0
- package/dist/server/sources.d.ts +27 -0
- package/dist/server/sources.js +56 -0
- package/dist/server/templates.d.ts +33 -0
- package/dist/server/templates.js +86 -0
- package/dist/server/uploads.d.ts +29 -0
- package/dist/server/uploads.js +86 -0
- package/package.json +162 -4
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L-5e — template kit + API guide catalog (spec-v8 §9 5e).
|
|
3
|
+
*
|
|
4
|
+
* The generic provider ships a minimal static-web kit and a versioned guide
|
|
5
|
+
* describing the artifacts_* surface. Hosts inject their own catalog port
|
|
6
|
+
* (approved template/guide assets); the default catalog carries no
|
|
7
|
+
* credentials, no host data and no external URLs. A template kit contains
|
|
8
|
+
* approved sources plus a build recipe — never executable server builds.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_TEMPLATE_ID = 'static-html';
|
|
11
|
+
export const DEFAULT_API_GUIDE_VERSION = 'v1';
|
|
12
|
+
const INDEX_HTML = [
|
|
13
|
+
'<!doctype html>',
|
|
14
|
+
'<html lang="en">',
|
|
15
|
+
'<head>',
|
|
16
|
+
'<meta charset="utf-8">',
|
|
17
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
18
|
+
'<title>Artifact page</title>',
|
|
19
|
+
'<link rel="stylesheet" href="./styles.css">',
|
|
20
|
+
'</head>',
|
|
21
|
+
'<body>',
|
|
22
|
+
'<main id="app">',
|
|
23
|
+
'<h1>Artifact page</h1>',
|
|
24
|
+
'<p>Static page published with the artifacts provider. Reader data calls',
|
|
25
|
+
'go through the host reader profile; this kit performs none.</p>',
|
|
26
|
+
'</main>',
|
|
27
|
+
'<script src="./app.js"></script>',
|
|
28
|
+
'</body>',
|
|
29
|
+
'</html>',
|
|
30
|
+
'',
|
|
31
|
+
].join('\n');
|
|
32
|
+
const STYLES_CSS = [
|
|
33
|
+
'body { font-family: system-ui, sans-serif; margin: 2rem; }',
|
|
34
|
+
'main { max-width: 44rem; }',
|
|
35
|
+
'',
|
|
36
|
+
].join('\n');
|
|
37
|
+
const APP_JS = [
|
|
38
|
+
"document.getElementById('app')?.setAttribute('data-ready', 'true');",
|
|
39
|
+
'',
|
|
40
|
+
].join('\n');
|
|
41
|
+
const GUIDE_V1 = [
|
|
42
|
+
'# Artifacts API guide v1',
|
|
43
|
+
'',
|
|
44
|
+
'Publish and share static web pages through the `artifacts_*` tools:',
|
|
45
|
+
'',
|
|
46
|
+
'- `artifacts_get_template` / `artifacts_get_api_guide`: start here.',
|
|
47
|
+
'- `artifacts_ensure`: resolve (or create) your private artifact view.',
|
|
48
|
+
'- `artifacts_publish`: publish inline files as a new release.',
|
|
49
|
+
'- `artifacts_create_upload` + bounded HTTP append +',
|
|
50
|
+
' `artifacts_commit_upload`: stage large bytes, then commit them.',
|
|
51
|
+
'- `artifacts_list` / `artifacts_get` / `artifacts_get_source`: discover.',
|
|
52
|
+
'- `artifacts_fork`: private independent copy (no ACL copied).',
|
|
53
|
+
'- `artifacts_set_access`: grant/revoke read or source, toggle public.',
|
|
54
|
+
'- `artifacts_promote`: explicit version selection (expected-current).',
|
|
55
|
+
'- `artifacts_renew`, `artifacts_delete`, `artifacts_restore`: lifecycle.',
|
|
56
|
+
'',
|
|
57
|
+
'Rules: artifacts are private by default; "public" means approved',
|
|
58
|
+
'accounts of the host, never anonymous. Source export is a separate',
|
|
59
|
+
'right. Pages never receive credentials; reader data access goes through',
|
|
60
|
+
'the host reader profile bound to the artifact and release.',
|
|
61
|
+
'',
|
|
62
|
+
].join('\n');
|
|
63
|
+
/** Generic in-package catalog; hosts override with approved local assets. */
|
|
64
|
+
export function createDefaultTemplateCatalog() {
|
|
65
|
+
const template = {
|
|
66
|
+
templateId: DEFAULT_TEMPLATE_ID,
|
|
67
|
+
version: 'v1',
|
|
68
|
+
description: 'Minimal static HTML page kit (no build step, no external calls).',
|
|
69
|
+
files: [
|
|
70
|
+
{ path: 'index.html', content: INDEX_HTML },
|
|
71
|
+
{ path: 'styles.css', content: STYLES_CSS },
|
|
72
|
+
{ path: 'app.js', content: APP_JS },
|
|
73
|
+
],
|
|
74
|
+
buildRecipe: ['Publish the files as-is with artifacts_publish; no build step.'],
|
|
75
|
+
};
|
|
76
|
+
const guide = { version: DEFAULT_API_GUIDE_VERSION, markdown: GUIDE_V1 };
|
|
77
|
+
return {
|
|
78
|
+
getTemplate(templateId) {
|
|
79
|
+
return Promise.resolve(templateId === template.templateId ? template : undefined);
|
|
80
|
+
},
|
|
81
|
+
getApiGuide(version) {
|
|
82
|
+
const wanted = version ?? guide.version;
|
|
83
|
+
return Promise.resolve(wanted === guide.version ? guide : undefined);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface BlobRef {
|
|
2
|
+
digest: string;
|
|
3
|
+
size: number;
|
|
4
|
+
}
|
|
5
|
+
export interface StagingStore {
|
|
6
|
+
write(uploadId: string, bytes: Uint8Array): Promise<void>;
|
|
7
|
+
read(uploadId: string): Promise<Uint8Array>;
|
|
8
|
+
discard(uploadId: string): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/** In-memory StagingStore double for unit tests. */
|
|
11
|
+
export declare function createMemoryStaging(): StagingStore;
|
|
12
|
+
export interface BeginUploadInput {
|
|
13
|
+
mimeType: string;
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface UploadServiceOptions {
|
|
17
|
+
maxUploadBytes: number;
|
|
18
|
+
}
|
|
19
|
+
export interface UploadService {
|
|
20
|
+
begin(input: BeginUploadInput): Promise<{
|
|
21
|
+
uploadId: string;
|
|
22
|
+
}>;
|
|
23
|
+
append(uploadId: string, offset: number, chunk: Uint8Array): Promise<{
|
|
24
|
+
size: number;
|
|
25
|
+
}>;
|
|
26
|
+
commit(uploadId: string, expected: BlobRef): Promise<BlobRef>;
|
|
27
|
+
}
|
|
28
|
+
/** Create an upload service over an injected staging store (no S3 imports). */
|
|
29
|
+
export declare function createUploadService(staging: StagingStore, options: UploadServiceOptions, ids?: () => string): UploadService;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L-5b — bounded uploads (spec-v8 §9).
|
|
3
|
+
*
|
|
4
|
+
* Uploads stage bytes under a bounded prefix with a strict size budget and
|
|
5
|
+
* sequential offsets. Commit verifies the staged bytes against the declared
|
|
6
|
+
* SHA-256 digest and size before returning a blob reference; false
|
|
7
|
+
* hashes/sizes, over-budget appends, out-of-order offsets and any write
|
|
8
|
+
* after commit are refused fail-closed. Staging is ephemeral: sessions live
|
|
9
|
+
* in memory and never become visible releases (publication owns durability
|
|
10
|
+
* via object intents). The byte store is injected; the memory double serves
|
|
11
|
+
* unit tests and the S3 adapter serves the L-5b integration proof.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
/** In-memory StagingStore double for unit tests. */
|
|
15
|
+
export function createMemoryStaging() {
|
|
16
|
+
const staged = new Map();
|
|
17
|
+
return {
|
|
18
|
+
write(uploadId, bytes) {
|
|
19
|
+
staged.set(uploadId, bytes);
|
|
20
|
+
return Promise.resolve();
|
|
21
|
+
},
|
|
22
|
+
read(uploadId) {
|
|
23
|
+
const bytes = staged.get(uploadId);
|
|
24
|
+
if (bytes === undefined)
|
|
25
|
+
throw new Error(`unknown upload: ${uploadId}`);
|
|
26
|
+
return Promise.resolve(bytes);
|
|
27
|
+
},
|
|
28
|
+
discard(uploadId) {
|
|
29
|
+
staged.delete(uploadId);
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Create an upload service over an injected staging store (no S3 imports). */
|
|
35
|
+
export function createUploadService(staging, options, ids = () => crypto.randomUUID()) {
|
|
36
|
+
const sessions = new Map();
|
|
37
|
+
return {
|
|
38
|
+
async begin(input) {
|
|
39
|
+
const uploadId = ids();
|
|
40
|
+
sessions.set(uploadId, {
|
|
41
|
+
committed: false,
|
|
42
|
+
maxSize: input.maxSize ?? options.maxUploadBytes,
|
|
43
|
+
size: 0,
|
|
44
|
+
});
|
|
45
|
+
await staging.write(uploadId, new Uint8Array(0));
|
|
46
|
+
return { uploadId };
|
|
47
|
+
},
|
|
48
|
+
async append(uploadId, offset, chunk) {
|
|
49
|
+
const session = sessions.get(uploadId);
|
|
50
|
+
if (session === undefined)
|
|
51
|
+
throw new Error(`unknown upload: ${uploadId}`);
|
|
52
|
+
if (session.committed)
|
|
53
|
+
throw new Error(`upload already committed: ${uploadId}`);
|
|
54
|
+
if (offset !== session.size) {
|
|
55
|
+
throw new Error(`upload offset mismatch for ${uploadId}: expected ${session.size}, got ${offset}`);
|
|
56
|
+
}
|
|
57
|
+
if (session.size + chunk.length > session.maxSize) {
|
|
58
|
+
throw new Error(`upload budget exceeded for ${uploadId}: max ${session.maxSize} bytes`);
|
|
59
|
+
}
|
|
60
|
+
const current = await staging.read(uploadId);
|
|
61
|
+
const next = new Uint8Array(current.length + chunk.length);
|
|
62
|
+
next.set(current, 0);
|
|
63
|
+
next.set(chunk, current.length);
|
|
64
|
+
await staging.write(uploadId, next);
|
|
65
|
+
session.size = next.length;
|
|
66
|
+
return { size: session.size };
|
|
67
|
+
},
|
|
68
|
+
async commit(uploadId, expected) {
|
|
69
|
+
const session = sessions.get(uploadId);
|
|
70
|
+
if (session === undefined)
|
|
71
|
+
throw new Error(`unknown upload: ${uploadId}`);
|
|
72
|
+
if (session.committed)
|
|
73
|
+
throw new Error(`upload already committed: ${uploadId}`);
|
|
74
|
+
const bytes = await staging.read(uploadId);
|
|
75
|
+
if (bytes.length !== expected.size) {
|
|
76
|
+
throw new Error(`upload size mismatch for ${uploadId}: staged ${bytes.length}, declared ${expected.size}`);
|
|
77
|
+
}
|
|
78
|
+
const actual = `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
|
|
79
|
+
if (actual !== expected.digest) {
|
|
80
|
+
throw new Error(`upload digest mismatch for ${uploadId}: staged ${actual}`);
|
|
81
|
+
}
|
|
82
|
+
session.committed = true;
|
|
83
|
+
return { digest: actual, size: bytes.length };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,164 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentropic/artifacts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generic configurable artifacts MCP and artifact service: StorageScope/Artifact/Binding/Release model, canonical manifests, object authorization, idempotent intents (spec-v8 §9). Single 0.1.0 candidate for L-5a–5e; unpublished.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rhanka/sentropic.git",
|
|
10
|
+
"directory": "packages/artifacts"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./contracts": {
|
|
20
|
+
"types": "./dist/contracts.d.ts",
|
|
21
|
+
"import": "./dist/contracts.js"
|
|
22
|
+
},
|
|
23
|
+
"./codecs": {
|
|
24
|
+
"types": "./dist/codecs.d.ts",
|
|
25
|
+
"import": "./dist/codecs.js"
|
|
26
|
+
},
|
|
27
|
+
"./server/ports": {
|
|
28
|
+
"types": "./dist/server/ports.d.ts",
|
|
29
|
+
"import": "./dist/server/ports.js"
|
|
30
|
+
},
|
|
31
|
+
"./server/service": {
|
|
32
|
+
"types": "./dist/server/service.d.ts",
|
|
33
|
+
"import": "./dist/server/service.js"
|
|
34
|
+
},
|
|
35
|
+
"./server/authorization": {
|
|
36
|
+
"types": "./dist/server/authorization.d.ts",
|
|
37
|
+
"import": "./dist/server/authorization.js"
|
|
38
|
+
},
|
|
39
|
+
"./server/bindings": {
|
|
40
|
+
"types": "./dist/server/bindings.d.ts",
|
|
41
|
+
"import": "./dist/server/bindings.js"
|
|
42
|
+
},
|
|
43
|
+
"./server/idempotency": {
|
|
44
|
+
"types": "./dist/server/idempotency.d.ts",
|
|
45
|
+
"import": "./dist/server/idempotency.js"
|
|
46
|
+
},
|
|
47
|
+
"./server/object-intents": {
|
|
48
|
+
"types": "./dist/server/object-intents.d.ts",
|
|
49
|
+
"import": "./dist/server/object-intents.js"
|
|
50
|
+
},
|
|
51
|
+
"./server/uploads": {
|
|
52
|
+
"types": "./dist/server/uploads.d.ts",
|
|
53
|
+
"import": "./dist/server/uploads.js"
|
|
54
|
+
},
|
|
55
|
+
"./server/publication": {
|
|
56
|
+
"types": "./dist/server/publication.d.ts",
|
|
57
|
+
"import": "./dist/server/publication.js"
|
|
58
|
+
},
|
|
59
|
+
"./server/access": {
|
|
60
|
+
"types": "./dist/server/access.d.ts",
|
|
61
|
+
"import": "./dist/server/access.js"
|
|
62
|
+
},
|
|
63
|
+
"./server/sources": {
|
|
64
|
+
"types": "./dist/server/sources.d.ts",
|
|
65
|
+
"import": "./dist/server/sources.js"
|
|
66
|
+
},
|
|
67
|
+
"./server/fork": {
|
|
68
|
+
"types": "./dist/server/fork.d.ts",
|
|
69
|
+
"import": "./dist/server/fork.js"
|
|
70
|
+
},
|
|
71
|
+
"./server/lifecycle": {
|
|
72
|
+
"types": "./dist/server/lifecycle.d.ts",
|
|
73
|
+
"import": "./dist/server/lifecycle.js"
|
|
74
|
+
},
|
|
75
|
+
"./server/gc": {
|
|
76
|
+
"types": "./dist/server/gc.d.ts",
|
|
77
|
+
"import": "./dist/server/gc.js"
|
|
78
|
+
},
|
|
79
|
+
"./server/runtime": {
|
|
80
|
+
"types": "./dist/server/runtime.d.ts",
|
|
81
|
+
"import": "./dist/server/runtime.js"
|
|
82
|
+
},
|
|
83
|
+
"./server/reader-profile": {
|
|
84
|
+
"types": "./dist/server/reader-profile.d.ts",
|
|
85
|
+
"import": "./dist/server/reader-profile.js"
|
|
86
|
+
},
|
|
87
|
+
"./server/templates": {
|
|
88
|
+
"types": "./dist/server/templates.d.ts",
|
|
89
|
+
"import": "./dist/server/templates.js"
|
|
90
|
+
},
|
|
91
|
+
"./mcp": {
|
|
92
|
+
"types": "./dist/mcp.d.ts",
|
|
93
|
+
"import": "./dist/mcp.js"
|
|
94
|
+
},
|
|
95
|
+
"./content/grants": {
|
|
96
|
+
"types": "./dist/content/grants.d.ts",
|
|
97
|
+
"import": "./dist/content/grants.js"
|
|
98
|
+
},
|
|
99
|
+
"./content/handler": {
|
|
100
|
+
"types": "./dist/content/handler.d.ts",
|
|
101
|
+
"import": "./dist/content/handler.js"
|
|
102
|
+
},
|
|
103
|
+
"./content/routing": {
|
|
104
|
+
"types": "./dist/content/routing.d.ts",
|
|
105
|
+
"import": "./dist/content/routing.js"
|
|
106
|
+
},
|
|
107
|
+
"./content/headers": {
|
|
108
|
+
"types": "./dist/content/headers.d.ts",
|
|
109
|
+
"import": "./dist/content/headers.js"
|
|
110
|
+
},
|
|
111
|
+
"./content/api-profile": {
|
|
112
|
+
"types": "./dist/content/api-profile.d.ts",
|
|
113
|
+
"import": "./dist/content/api-profile.js"
|
|
114
|
+
},
|
|
115
|
+
"./http": {
|
|
116
|
+
"types": "./dist/http.d.ts",
|
|
117
|
+
"import": "./dist/http.js"
|
|
118
|
+
},
|
|
119
|
+
"./parameters": {
|
|
120
|
+
"types": "./dist/parameters.d.ts",
|
|
121
|
+
"import": "./dist/parameters.js"
|
|
122
|
+
},
|
|
123
|
+
"./provider": {
|
|
124
|
+
"types": "./dist/provider-definition.d.ts",
|
|
125
|
+
"import": "./dist/provider-definition.js"
|
|
126
|
+
},
|
|
127
|
+
"./client": {
|
|
128
|
+
"types": "./dist/client.d.ts",
|
|
129
|
+
"import": "./dist/client.js"
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"files": [
|
|
133
|
+
"dist",
|
|
134
|
+
"README.md",
|
|
135
|
+
"LICENSE"
|
|
136
|
+
],
|
|
137
|
+
"publishConfig": {
|
|
138
|
+
"provenance": true,
|
|
139
|
+
"access": "public"
|
|
140
|
+
},
|
|
141
|
+
"scripts": {
|
|
142
|
+
"build": "tsc -p tsconfig.json",
|
|
143
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
144
|
+
"test": "vitest run tests"
|
|
145
|
+
},
|
|
146
|
+
"dependencies": {
|
|
147
|
+
"@aws-sdk/client-s3": "^3.980.0",
|
|
148
|
+
"pg": "^8.12.0"
|
|
149
|
+
},
|
|
150
|
+
"peerDependencies": {
|
|
151
|
+
"@sentropic/mcp-platform": ">=0.3.0 <0.4.0"
|
|
152
|
+
},
|
|
153
|
+
"peerDependenciesMeta": {
|
|
154
|
+
"@sentropic/mcp-platform": {
|
|
155
|
+
"optional": true
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"devDependencies": {
|
|
159
|
+
"@types/node": "^22.10.10",
|
|
160
|
+
"@types/pg": "^8.15.5",
|
|
161
|
+
"typescript": "5.4.5",
|
|
162
|
+
"vitest": "4.1.0"
|
|
163
|
+
}
|
|
164
|
+
}
|