@contentrail/publisher 0.0.0-stage → 0.2.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 +137 -2
- package/dist/engine.d.ts +34 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +196 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/fakes/memory-git.d.ts +12 -0
- package/dist/fakes/memory-git.d.ts.map +1 -0
- package/dist/fakes/memory-git.js +56 -0
- package/dist/fakes/memory-git.js.map +1 -0
- package/dist/fakes/memory-r2.d.ts +12 -0
- package/dist/fakes/memory-r2.d.ts.map +1 -0
- package/dist/fakes/memory-r2.js +59 -0
- package/dist/fakes/memory-r2.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/paths.d.ts +4 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +43 -0
- package/dist/paths.js.map +1 -0
- package/dist/providers/git-client.d.ts +31 -0
- package/dist/providers/git-client.d.ts.map +1 -0
- package/dist/providers/git-client.js +2 -0
- package/dist/providers/git-client.js.map +1 -0
- package/dist/providers/git.d.ts +10 -0
- package/dist/providers/git.d.ts.map +1 -0
- package/dist/providers/git.js +311 -0
- package/dist/providers/git.js.map +1 -0
- package/dist/providers/r2-client.d.ts +19 -0
- package/dist/providers/r2-client.d.ts.map +1 -0
- package/dist/providers/r2-client.js +2 -0
- package/dist/providers/r2-client.js.map +1 -0
- package/dist/providers/r2.d.ts +14 -0
- package/dist/providers/r2.d.ts.map +1 -0
- package/dist/providers/r2.js +305 -0
- package/dist/providers/r2.js.map +1 -0
- package/dist/rebuild.d.ts +34 -0
- package/dist/rebuild.d.ts.map +1 -0
- package/dist/rebuild.js +51 -0
- package/dist/rebuild.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +26 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContentRail contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,138 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@contentrail/publisher`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Server-only publishing engine for ContentRail.
|
|
4
|
+
|
|
5
|
+
**Do not import from browser code.** Git tokens, R2 credentials and rebuild hook secrets stay in the host process (Worker, CI job, or private server).
|
|
6
|
+
|
|
7
|
+
## Concepts
|
|
8
|
+
|
|
9
|
+
| Term | Meaning |
|
|
10
|
+
| ------------------- | ------------------------------------------------------------- |
|
|
11
|
+
| Publication request | Validated intent to publish entries for a site + collection |
|
|
12
|
+
| Publication version | Opaque version id activated after a successful provider write |
|
|
13
|
+
| Provider revision | Git commit SHA or R2 etag from the activation step |
|
|
14
|
+
| Rebuild | Optional deploy hook — independent of publication success |
|
|
15
|
+
|
|
16
|
+
See ADR 013 in `docs/DECISIONS.md` for concurrency, retries, partial failure and rollback.
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Node.js **≥ 22** (server / CI / Worker host — not for browsers)
|
|
21
|
+
- `@contentrail/core`
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
### Monorepo
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
pnpm install
|
|
29
|
+
pnpm --filter @contentrail/publisher build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### From npm (upcoming `0.2.0` after authorized publish)
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pnpm add @contentrail/core@0.2.0 @contentrail/publisher@0.2.0 zod@^4.6.7
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { defineCollection } from "@contentrail/core";
|
|
42
|
+
import {
|
|
43
|
+
createPublisher,
|
|
44
|
+
createGitPublishProvider,
|
|
45
|
+
createR2PublishProvider,
|
|
46
|
+
createRebuildHook,
|
|
47
|
+
} from "@contentrail/publisher";
|
|
48
|
+
import { z } from "zod";
|
|
49
|
+
|
|
50
|
+
const articles = defineCollection({
|
|
51
|
+
name: "articles",
|
|
52
|
+
schema: z.object({
|
|
53
|
+
title: z.string().min(1),
|
|
54
|
+
slug: z.string().min(1),
|
|
55
|
+
published: z.boolean(),
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Inject real clients in production. Examples/tests use memory fakes.
|
|
60
|
+
const publisher = createPublisher({
|
|
61
|
+
provider: createR2PublishProvider({
|
|
62
|
+
client: r2Client,
|
|
63
|
+
keyPrefix: "sites",
|
|
64
|
+
hasCredential: Boolean(process.env.R2_ACCESS_KEY_ID),
|
|
65
|
+
}),
|
|
66
|
+
rebuild: createRebuildHook({
|
|
67
|
+
client: rebuildClient,
|
|
68
|
+
hasSecret: Boolean(process.env.REBUILD_HOOK_URL),
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const result = await publisher.publish({
|
|
73
|
+
siteId: "demo",
|
|
74
|
+
collection: articles,
|
|
75
|
+
expectedVersion: null, // first publish
|
|
76
|
+
entries: [
|
|
77
|
+
{
|
|
78
|
+
id: "welcome",
|
|
79
|
+
data: { title: "Welcome", slug: "welcome", published: true },
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
idempotencyKey: "editor-save-1",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (result.success) {
|
|
86
|
+
// Content is published. result.rebuild.state may still be "pending" or "failed".
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Required `expectedVersion`
|
|
91
|
+
|
|
92
|
+
Always pass the active version you read earlier, or `null` when none exists. Stale values throw `PublishConflictError`.
|
|
93
|
+
|
|
94
|
+
### Providers
|
|
95
|
+
|
|
96
|
+
#### Git (`createGitPublishProvider`)
|
|
97
|
+
|
|
98
|
+
- Writes `{contentRoot}/{collection}/{id}.json`
|
|
99
|
+
- Stores meta under `{contentRoot}/contentrail-meta/{siteId}/{collection}/meta.json`
|
|
100
|
+
- Activation is a branch commit requiring the expected parent tip
|
|
101
|
+
- Inject `GitRepositoryClient` (wrap GitHub/GitLab HTTP APIs yourself)
|
|
102
|
+
|
|
103
|
+
Environment (example only — not committed secrets):
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
GITHUB_TOKEN=... # server-only
|
|
107
|
+
CONTENTRAIL_GIT_REPO=org/repo
|
|
108
|
+
CONTENTRAIL_GIT_BRANCH=main
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### R2 (`createR2PublishProvider`)
|
|
112
|
+
|
|
113
|
+
- Snapshot: `{prefix}/{siteId}/collections/{collection}/versions/{version}/...`
|
|
114
|
+
- Pointer: `{prefix}/{siteId}/collections/{collection}/current.json`
|
|
115
|
+
- Pointer uses conditional put (`if-match` / `if-none-match`)
|
|
116
|
+
- Inject `ObjectStorageClient`
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
R2_ACCOUNT_ID=...
|
|
120
|
+
R2_ACCESS_KEY_ID=...
|
|
121
|
+
R2_SECRET_ACCESS_KEY=...
|
|
122
|
+
R2_BUCKET=...
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Rebuild hooks
|
|
126
|
+
|
|
127
|
+
`createRebuildHook` keeps the hook URL/secret server-side. A successful publish with `rebuild.state === "failed"` means **content was published** but the site rebuild was not confirmed.
|
|
128
|
+
|
|
129
|
+
## Guarantees and limitations
|
|
130
|
+
|
|
131
|
+
- Validated before write; no silent stale overwrites
|
|
132
|
+
- No multi-object atomic transaction claim for Git or R2
|
|
133
|
+
- Orphaned R2 snapshots may remain after a lost activation race
|
|
134
|
+
- Tests use `createMemoryGitClient` / `createMemoryR2Client` — no live credentials
|
|
135
|
+
|
|
136
|
+
## Auth
|
|
137
|
+
|
|
138
|
+
Phase 3 does **not** implement authentication. Callers must only invoke this API from an already-trusted server context (Phase 4).
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ContentCollection } from "@contentrail/core";
|
|
2
|
+
import type { ActiveContentSnapshot, PublishProvider, PublishRequest, PublishResult, PublicationSummary, RebuildStatus } from "./types.js";
|
|
3
|
+
export interface ContentPublisher {
|
|
4
|
+
publish<TCollection extends ContentCollection>(request: PublishRequest<TCollection>): Promise<PublishResult>;
|
|
5
|
+
getStatus(siteId: string, collectionName: string): Promise<{
|
|
6
|
+
activeVersion: string | null;
|
|
7
|
+
versions: PublicationSummary[];
|
|
8
|
+
}>;
|
|
9
|
+
readActiveContent(siteId: string, collectionName: string): Promise<ActiveContentSnapshot | null>;
|
|
10
|
+
listVersions(siteId: string, collectionName: string): Promise<PublicationSummary[]>;
|
|
11
|
+
rollback(input: {
|
|
12
|
+
siteId: string;
|
|
13
|
+
collectionName: string;
|
|
14
|
+
targetVersion: string;
|
|
15
|
+
expectedVersion: string | null;
|
|
16
|
+
publicationId?: string;
|
|
17
|
+
}): Promise<PublishResult>;
|
|
18
|
+
}
|
|
19
|
+
export interface CreatePublisherOptions {
|
|
20
|
+
readonly provider: PublishProvider;
|
|
21
|
+
readonly rebuild?: {
|
|
22
|
+
trigger(input: {
|
|
23
|
+
siteId: string;
|
|
24
|
+
collection: string;
|
|
25
|
+
version: string;
|
|
26
|
+
publicationId: string;
|
|
27
|
+
}): Promise<RebuildStatus>;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a server-only publisher that validates content then delegates to a provider.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createPublisher(options: CreatePublisherOptions): ContentPublisher;
|
|
34
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAiB,MAAM,mBAAmB,CAAC;AAG1E,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,aAAa,EAEd,MAAM,YAAY,CAAC;AAOpB,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,WAAW,SAAS,iBAAiB,EAC3C,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC,GACnC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,SAAS,CACP,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAAC,CAAC;IAC7E,iBAAiB,CACf,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IACzC,YAAY,CACV,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,OAAO,CAAC,KAAK,EAAE;YACb,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;YACnB,OAAO,EAAE,MAAM,CAAC;YAChB,aAAa,EAAE,MAAM,CAAC;SACvB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;KAC5B,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,gBAAgB,CA6MlB"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { validateEntry } from "@contentrail/core";
|
|
2
|
+
import { PublishConflictError, PublishError } from "./errors.js";
|
|
3
|
+
import { assertSafeSegment } from "./paths.js";
|
|
4
|
+
import { createContentVersion, createPublicationId, publicationIdFromIdempotencyKey, } from "./version.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a server-only publisher that validates content then delegates to a provider.
|
|
7
|
+
*/
|
|
8
|
+
export function createPublisher(options) {
|
|
9
|
+
const { provider, rebuild } = options;
|
|
10
|
+
return {
|
|
11
|
+
async publish(request) {
|
|
12
|
+
const siteId = request.siteId;
|
|
13
|
+
const collection = request.collection;
|
|
14
|
+
assertSafeSegment(siteId, "site id");
|
|
15
|
+
assertSafeSegment(collection.name, "collection name");
|
|
16
|
+
if (!Array.isArray(request.entries) || request.entries.length === 0) {
|
|
17
|
+
throw new PublishError("VALIDATION", "Publish requests require a non-empty entries array.", { siteId, collection: collection.name });
|
|
18
|
+
}
|
|
19
|
+
if (!("expectedVersion" in request)) {
|
|
20
|
+
throw new PublishError("VALIDATION", 'Publish requests require "expectedVersion" (use null for the first publish).', { siteId, collection: collection.name });
|
|
21
|
+
}
|
|
22
|
+
const publicationId = createPublicationId(request.publicationId ??
|
|
23
|
+
(request.idempotencyKey !== undefined
|
|
24
|
+
? publicationIdFromIdempotencyKey(request.idempotencyKey)
|
|
25
|
+
: undefined));
|
|
26
|
+
if (request.idempotencyKey !== undefined) {
|
|
27
|
+
const prior = await provider.readIdempotentResult(siteId, collection.name, request.idempotencyKey);
|
|
28
|
+
if (prior !== null) {
|
|
29
|
+
return { ...prior, idempotentReplay: true };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const validated = validatePublishEntries(collection, request.entries, {
|
|
33
|
+
siteId,
|
|
34
|
+
publicationId,
|
|
35
|
+
});
|
|
36
|
+
const version = createContentVersion({
|
|
37
|
+
siteId,
|
|
38
|
+
collection: collection.name,
|
|
39
|
+
publicationId,
|
|
40
|
+
entries: validated,
|
|
41
|
+
});
|
|
42
|
+
if (request.idempotencyKey !== undefined) {
|
|
43
|
+
const recoveredEarly = await provider.recoverIdempotentPublish({
|
|
44
|
+
siteId,
|
|
45
|
+
collection: collection.name,
|
|
46
|
+
idempotencyKey: request.idempotencyKey,
|
|
47
|
+
publicationId,
|
|
48
|
+
version,
|
|
49
|
+
});
|
|
50
|
+
if (recoveredEarly !== null) {
|
|
51
|
+
return { ...recoveredEarly, idempotentReplay: true };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const activeVersion = await provider.getActiveVersion(siteId, collection.name);
|
|
55
|
+
if (activeVersion !== request.expectedVersion) {
|
|
56
|
+
if (request.idempotencyKey !== undefined) {
|
|
57
|
+
const recovered = await provider.recoverIdempotentPublish({
|
|
58
|
+
siteId,
|
|
59
|
+
collection: collection.name,
|
|
60
|
+
idempotencyKey: request.idempotencyKey,
|
|
61
|
+
publicationId,
|
|
62
|
+
version,
|
|
63
|
+
});
|
|
64
|
+
if (recovered !== null) {
|
|
65
|
+
return { ...recovered, idempotentReplay: true };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
throw new PublishConflictError(`Stale expectedVersion for "${siteId}/${collection.name}": expected ${String(request.expectedVersion)}, current ${String(activeVersion)}.`, {
|
|
69
|
+
siteId,
|
|
70
|
+
collection: collection.name,
|
|
71
|
+
publicationId,
|
|
72
|
+
currentVersion: activeVersion,
|
|
73
|
+
expectedVersion: request.expectedVersion,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
let result;
|
|
77
|
+
try {
|
|
78
|
+
result = await provider.publishSnapshot({
|
|
79
|
+
siteId,
|
|
80
|
+
collection: collection.name,
|
|
81
|
+
publicationId,
|
|
82
|
+
version,
|
|
83
|
+
previousVersion: activeVersion,
|
|
84
|
+
expectedVersion: request.expectedVersion,
|
|
85
|
+
entries: validated,
|
|
86
|
+
...(request.message !== undefined
|
|
87
|
+
? { message: request.message }
|
|
88
|
+
: {}),
|
|
89
|
+
...(request.idempotencyKey !== undefined
|
|
90
|
+
? { idempotencyKey: request.idempotencyKey }
|
|
91
|
+
: {}),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (request.idempotencyKey !== undefined &&
|
|
96
|
+
error instanceof PublishError &&
|
|
97
|
+
(error.code === "CONFLICT" || error.code === "PARTIAL_FAILURE")) {
|
|
98
|
+
const recovered = await provider.recoverIdempotentPublish({
|
|
99
|
+
siteId,
|
|
100
|
+
collection: collection.name,
|
|
101
|
+
idempotencyKey: request.idempotencyKey,
|
|
102
|
+
publicationId,
|
|
103
|
+
version,
|
|
104
|
+
});
|
|
105
|
+
if (recovered !== null) {
|
|
106
|
+
return { ...recovered, idempotentReplay: true };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (error instanceof PublishError) {
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
throw new PublishError("PROVIDER", `Provider publish failed for "${siteId}/${collection.name}".`, { siteId, collection: collection.name, publicationId, cause: error });
|
|
113
|
+
}
|
|
114
|
+
if (rebuild !== undefined) {
|
|
115
|
+
try {
|
|
116
|
+
const rebuildStatus = await rebuild.trigger({
|
|
117
|
+
siteId,
|
|
118
|
+
collection: collection.name,
|
|
119
|
+
version: result.version,
|
|
120
|
+
publicationId: result.publicationId,
|
|
121
|
+
});
|
|
122
|
+
return { ...result, rebuild: rebuildStatus };
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
...result,
|
|
127
|
+
rebuild: {
|
|
128
|
+
state: "failed",
|
|
129
|
+
detail: error instanceof Error
|
|
130
|
+
? error.message
|
|
131
|
+
: "Rebuild trigger failed after successful publication.",
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
},
|
|
138
|
+
async getStatus(siteId, collectionName) {
|
|
139
|
+
assertSafeSegment(siteId, "site id");
|
|
140
|
+
assertSafeSegment(collectionName, "collection name");
|
|
141
|
+
const activeVersion = await provider.getActiveVersion(siteId, collectionName);
|
|
142
|
+
const versions = await provider.listVersions(siteId, collectionName);
|
|
143
|
+
return { activeVersion, versions };
|
|
144
|
+
},
|
|
145
|
+
async readActiveContent(siteId, collectionName) {
|
|
146
|
+
assertSafeSegment(siteId, "site id");
|
|
147
|
+
assertSafeSegment(collectionName, "collection name");
|
|
148
|
+
return provider.readActiveContent(siteId, collectionName);
|
|
149
|
+
},
|
|
150
|
+
async listVersions(siteId, collectionName) {
|
|
151
|
+
assertSafeSegment(siteId, "site id");
|
|
152
|
+
assertSafeSegment(collectionName, "collection name");
|
|
153
|
+
return provider.listVersions(siteId, collectionName);
|
|
154
|
+
},
|
|
155
|
+
async rollback(input) {
|
|
156
|
+
assertSafeSegment(input.siteId, "site id");
|
|
157
|
+
assertSafeSegment(input.collectionName, "collection name");
|
|
158
|
+
assertSafeSegment(input.targetVersion, "target version");
|
|
159
|
+
const publicationId = createPublicationId(input.publicationId);
|
|
160
|
+
return provider.rollback({
|
|
161
|
+
siteId: input.siteId,
|
|
162
|
+
collection: input.collectionName,
|
|
163
|
+
targetVersion: input.targetVersion,
|
|
164
|
+
expectedVersion: input.expectedVersion,
|
|
165
|
+
publicationId,
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function validatePublishEntries(collection, entries, context) {
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
const validated = [];
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
assertSafeSegment(entry.id, "entry id");
|
|
175
|
+
if (seen.has(entry.id)) {
|
|
176
|
+
throw new PublishError("VALIDATION", `Duplicate entry id "${entry.id}" in publish request.`, {
|
|
177
|
+
siteId: context.siteId,
|
|
178
|
+
collection: collection.name,
|
|
179
|
+
publicationId: context.publicationId,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
seen.add(entry.id);
|
|
183
|
+
const result = validateEntry(collection, entry.data);
|
|
184
|
+
if (!result.success) {
|
|
185
|
+
throw new PublishError("VALIDATION", `Invalid content for entry "${entry.id}": ${result.error.message}`, {
|
|
186
|
+
siteId: context.siteId,
|
|
187
|
+
collection: collection.name,
|
|
188
|
+
publicationId: context.publicationId,
|
|
189
|
+
cause: result.error,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
validated.push({ id: entry.id, data: result.data });
|
|
193
|
+
}
|
|
194
|
+
return validated;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type PublishErrorCode = "VALIDATION" | "CONFLICT" | "PATH_ESCAPE" | "PROVIDER" | "PARTIAL_FAILURE" | "CREDENTIALS" | "NOT_FOUND" | "REBUILD";
|
|
2
|
+
export declare class PublishError extends Error {
|
|
3
|
+
readonly code: PublishErrorCode;
|
|
4
|
+
readonly siteId?: string;
|
|
5
|
+
readonly collection?: string;
|
|
6
|
+
readonly publicationId?: string;
|
|
7
|
+
readonly currentVersion?: string | null;
|
|
8
|
+
readonly expectedVersion?: string | null;
|
|
9
|
+
constructor(code: PublishErrorCode, message: string, options?: {
|
|
10
|
+
siteId?: string;
|
|
11
|
+
collection?: string;
|
|
12
|
+
publicationId?: string;
|
|
13
|
+
currentVersion?: string | null;
|
|
14
|
+
expectedVersion?: string | null;
|
|
15
|
+
cause?: unknown;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export declare class PublishConflictError extends PublishError {
|
|
19
|
+
constructor(message: string, options?: {
|
|
20
|
+
siteId?: string;
|
|
21
|
+
collection?: string;
|
|
22
|
+
publicationId?: string;
|
|
23
|
+
currentVersion?: string | null;
|
|
24
|
+
expectedVersion?: string | null;
|
|
25
|
+
cause?: unknown;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,UAAU,GACV,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,aAAa,GACb,WAAW,GACX,SAAS,CAAC;AAEd,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;gBAGvC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,KAAK,CAAC,EAAE,OAAO,CAAC;KACZ;CAwBT;AAED,qBAAa,oBAAqB,SAAQ,YAAY;gBAElD,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,KAAK,CAAC,EAAE,OAAO,CAAC;KACZ;CAKT"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class PublishError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
siteId;
|
|
4
|
+
collection;
|
|
5
|
+
publicationId;
|
|
6
|
+
currentVersion;
|
|
7
|
+
expectedVersion;
|
|
8
|
+
constructor(code, message, options = {}) {
|
|
9
|
+
super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
|
|
10
|
+
this.name = "PublishError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
if (options.siteId !== undefined) {
|
|
13
|
+
this.siteId = options.siteId;
|
|
14
|
+
}
|
|
15
|
+
if (options.collection !== undefined) {
|
|
16
|
+
this.collection = options.collection;
|
|
17
|
+
}
|
|
18
|
+
if (options.publicationId !== undefined) {
|
|
19
|
+
this.publicationId = options.publicationId;
|
|
20
|
+
}
|
|
21
|
+
if (options.currentVersion !== undefined) {
|
|
22
|
+
this.currentVersion = options.currentVersion;
|
|
23
|
+
}
|
|
24
|
+
if (options.expectedVersion !== undefined) {
|
|
25
|
+
this.expectedVersion = options.expectedVersion;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class PublishConflictError extends PublishError {
|
|
30
|
+
constructor(message, options = {}) {
|
|
31
|
+
super("CONFLICT", message, options);
|
|
32
|
+
this.name = "PublishConflictError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,CAAmB;IACvB,MAAM,CAAU;IAChB,UAAU,CAAU;IACpB,aAAa,CAAU;IACvB,cAAc,CAAiB;IAC/B,eAAe,CAAiB;IAEzC,YACE,IAAsB,EACtB,OAAe,EACf,UAOI,EAAE;QAEN,KAAK,CACH,OAAO,EACP,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CACnE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC/B,CAAC;QACD,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACvC,CAAC;QACD,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC7C,CAAC;QACD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YACzC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC/C,CAAC;QACD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YAC1C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QACjD,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IACpD,YACE,OAAe,EACf,UAOI,EAAE;QAEN,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GitRepositoryClient } from "../providers/git-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic in-memory Git repository for tests. No network. No credentials.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createMemoryGitClient(options?: {
|
|
6
|
+
branch?: string;
|
|
7
|
+
failNextCommit?: "conflict" | "timeout" | null;
|
|
8
|
+
}): GitRepositoryClient & {
|
|
9
|
+
setFailNextCommit(mode: "conflict" | "timeout" | null): void;
|
|
10
|
+
filesAtTip(): Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=memory-git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-git.d.ts","sourceRoot":"","sources":["../../src/fakes/memory-git.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,CAAC;CAChD,GAAG,mBAAmB,GAAG;IACxB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IAC7D,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAoDA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic in-memory Git repository for tests. No network. No credentials.
|
|
4
|
+
*/
|
|
5
|
+
export function createMemoryGitClient(options) {
|
|
6
|
+
const branch = options?.branch ?? "main";
|
|
7
|
+
let tip = createHash("sha1").update("root").digest("hex");
|
|
8
|
+
const filesByCommit = new Map([[tip, {}]]);
|
|
9
|
+
let failNext = options?.failNextCommit ?? null;
|
|
10
|
+
return {
|
|
11
|
+
setFailNextCommit(mode) {
|
|
12
|
+
failNext = mode;
|
|
13
|
+
},
|
|
14
|
+
filesAtTip() {
|
|
15
|
+
return { ...(filesByCommit.get(tip) ?? {}) };
|
|
16
|
+
},
|
|
17
|
+
async getBranchTip(requested) {
|
|
18
|
+
if (requested !== branch) {
|
|
19
|
+
throw new Error(`Unknown branch ${requested}`);
|
|
20
|
+
}
|
|
21
|
+
return tip;
|
|
22
|
+
},
|
|
23
|
+
async readFile(path, ref) {
|
|
24
|
+
const tree = filesByCommit.get(ref);
|
|
25
|
+
if (tree === undefined) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return tree[path] ?? null;
|
|
29
|
+
},
|
|
30
|
+
async commitFiles(input) {
|
|
31
|
+
if (input.branch !== branch) {
|
|
32
|
+
throw new Error(`Unknown branch ${input.branch}`);
|
|
33
|
+
}
|
|
34
|
+
if (failNext === "timeout") {
|
|
35
|
+
failNext = null;
|
|
36
|
+
throw new Error("timeout");
|
|
37
|
+
}
|
|
38
|
+
if (failNext === "conflict" || input.expectedParentSha !== tip) {
|
|
39
|
+
failNext = null;
|
|
40
|
+
throw new Error("CONFLICT");
|
|
41
|
+
}
|
|
42
|
+
const parent = filesByCommit.get(tip) ?? {};
|
|
43
|
+
const next = { ...parent };
|
|
44
|
+
for (const file of input.files) {
|
|
45
|
+
next[file.path] = file.content;
|
|
46
|
+
}
|
|
47
|
+
const commitSha = createHash("sha1")
|
|
48
|
+
.update(`${tip}:${randomUUID()}:${input.message}`)
|
|
49
|
+
.digest("hex");
|
|
50
|
+
filesByCommit.set(commitSha, next);
|
|
51
|
+
tip = commitSha;
|
|
52
|
+
return { commitSha, branch };
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=memory-git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-git.js","sourceRoot":"","sources":["../../src/fakes/memory-git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGrD;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAGrC;IAIC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,MAAM,CAAC;IACzC,IAAI,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1D,MAAM,aAAa,GAAG,IAAI,GAAG,CAAiC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,IAAI,QAAQ,GAAkC,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;IAE9E,OAAO;QACL,iBAAiB,CAAC,IAAI;YACpB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,UAAU;YACR,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,SAAS;YAC1B,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CAAC,kBAAkB,SAAS,EAAE,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG;YACtB,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,KAAK;YACrB,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;YAC7B,CAAC;YACD,IAAI,QAAQ,KAAK,UAAU,IAAI,KAAK,CAAC,iBAAiB,KAAK,GAAG,EAAE,CAAC;gBAC/D,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;YAC9B,CAAC;YAED,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;YACjC,CAAC;YACD,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC;iBACjC,MAAM,CAAC,GAAG,GAAG,IAAI,UAAU,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;iBACjD,MAAM,CAAC,KAAK,CAAC,CAAC;YACjB,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACnC,GAAG,GAAG,SAAS,CAAC;YAChB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAC/B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ObjectStorageClient } from "../providers/r2-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory object store with conditional put support for tests.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createMemoryR2Client(options?: {
|
|
6
|
+
failNextPut?: "conflict" | "timeout" | "error" | null;
|
|
7
|
+
failOnKeyIncludes?: string;
|
|
8
|
+
}): ObjectStorageClient & {
|
|
9
|
+
setFailNextPut(mode: "conflict" | "timeout" | "error" | null): void;
|
|
10
|
+
dump(): Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=memory-r2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-r2.d.ts","sourceRoot":"","sources":["../../src/fakes/memory-r2.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAC7C,WAAW,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,mBAAmB,GAAG;IACxB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;IACpE,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC,CA6DA"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory object store with conditional put support for tests.
|
|
4
|
+
*/
|
|
5
|
+
export function createMemoryR2Client(options) {
|
|
6
|
+
const objects = new Map();
|
|
7
|
+
let failNext = options?.failNextPut ?? null;
|
|
8
|
+
return {
|
|
9
|
+
setFailNextPut(mode) {
|
|
10
|
+
failNext = mode;
|
|
11
|
+
},
|
|
12
|
+
dump() {
|
|
13
|
+
const out = {};
|
|
14
|
+
for (const [key, value] of objects) {
|
|
15
|
+
out[key] = value.body;
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
},
|
|
19
|
+
async getObject(key) {
|
|
20
|
+
return objects.get(key) ?? null;
|
|
21
|
+
},
|
|
22
|
+
async listKeys(prefix) {
|
|
23
|
+
return [...objects.keys()].filter((key) => key.startsWith(prefix)).sort();
|
|
24
|
+
},
|
|
25
|
+
async putObject(key, body, putOptions) {
|
|
26
|
+
if (options?.failOnKeyIncludes !== undefined &&
|
|
27
|
+
key.includes(options.failOnKeyIncludes) &&
|
|
28
|
+
failNext === null) {
|
|
29
|
+
// allow selective failure via setFailNextPut instead
|
|
30
|
+
}
|
|
31
|
+
if (failNext === "timeout") {
|
|
32
|
+
failNext = null;
|
|
33
|
+
throw new Error("timeout");
|
|
34
|
+
}
|
|
35
|
+
if (failNext === "error") {
|
|
36
|
+
failNext = null;
|
|
37
|
+
throw new Error("provider error");
|
|
38
|
+
}
|
|
39
|
+
if (failNext === "conflict") {
|
|
40
|
+
failNext = null;
|
|
41
|
+
throw new Error("CONFLICT");
|
|
42
|
+
}
|
|
43
|
+
const existing = objects.get(key);
|
|
44
|
+
if (putOptions?.ifNoneMatch === "*" && existing !== undefined) {
|
|
45
|
+
throw new Error("CONFLICT");
|
|
46
|
+
}
|
|
47
|
+
if (putOptions?.ifMatch !== undefined &&
|
|
48
|
+
(existing === undefined || existing.etag !== putOptions.ifMatch)) {
|
|
49
|
+
throw new Error("CONFLICT");
|
|
50
|
+
}
|
|
51
|
+
const etag = createHash("md5")
|
|
52
|
+
.update(`${key}:${body}:${randomUUID()}`)
|
|
53
|
+
.digest("hex");
|
|
54
|
+
objects.set(key, { body, etag });
|
|
55
|
+
return { etag };
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=memory-r2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-r2.js","sourceRoot":"","sources":["../../src/fakes/memory-r2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGrD;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAGpC;IAIC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0C,CAAC;IAClE,IAAI,QAAQ,GACV,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC;IAE/B,OAAO;QACL,cAAc,CAAC,IAAI;YACjB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,IAAI;YACF,MAAM,GAAG,GAA2B,EAAE,CAAC;YACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;gBACnC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YACxB,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,GAAG;YACjB,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;QAClC,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,MAAM;YACnB,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5E,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU;YACnC,IACE,OAAO,EAAE,iBAAiB,KAAK,SAAS;gBACxC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC;gBACvC,QAAQ,KAAK,IAAI,EACjB,CAAC;gBACD,qDAAqD;YACvD,CAAC;YACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;YAC7B,CAAC;YACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACzB,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACpC,CAAC;YACD,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;gBAC5B,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;YAC9B,CAAC;YAED,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,UAAU,EAAE,WAAW,KAAK,GAAG,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC9D,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;YAC9B,CAAC;YACD,IACE,UAAU,EAAE,OAAO,KAAK,SAAS;gBACjC,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,CAAC,EAChE,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;YAC9B,CAAC;YAED,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC;iBAC3B,MAAM,CAAC,GAAG,GAAG,IAAI,IAAI,IAAI,UAAU,EAAE,EAAE,CAAC;iBACxC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
|