@laikacms/github 0.1.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 ADDED
@@ -0,0 +1,8 @@
1
+ # @laikacms/github
2
+
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - laikacms@0.1.2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sem Postma
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 ADDED
@@ -0,0 +1,41 @@
1
+ # @laikacms/github
2
+
3
+ GitHub-backed `StorageRepository` for Laika CMS. Stores content as commits in a GitHub
4
+ repository; the package authenticates as a **GitHub App** using installation tokens.
5
+
6
+ This is the production storage adapter that lets the same `documents-contentbase` flow
7
+ work against a real git repo, no FS required. Pair it with the FS adapter
8
+ (`laikacms/storage-fs`) for local dev to keep behavior identical across environments.
9
+
10
+ ## Why a GitHub App (not OAuth App or PAT)
11
+
12
+ - App installation tokens are **scoped to a single repo** (the install). Tokens are short-lived
13
+ and minted on demand from the App's private key — no long-lived secrets in the running Worker.
14
+ - The user's GitHub identity (logged in via PKCE) is **independent** of the writer identity.
15
+ Editors don't need write access to the content repo; the App does.
16
+ - Audit trail: every commit is attributed to `<App name>[bot]` with the editor's user info in
17
+ the commit message.
18
+
19
+ ## Usage
20
+
21
+ ```ts
22
+ import { GithubStorageRepository } from '@laikacms/github/storage-gh';
23
+ import { storageSerializerYaml } from 'laikacms/storage-serializers-yaml';
24
+ import { storageSerializerMarkdown } from 'laikacms/storage-serializers-markdown';
25
+
26
+ const storage = new GithubStorageRepository({
27
+ appId: env.GITHUB_APP_ID,
28
+ privateKey: env.GITHUB_APP_PRIVATE_KEY,
29
+ installationId: env.GITHUB_APP_INSTALLATION_ID,
30
+ owner: 'esstudio',
31
+ repo: 'content',
32
+ branch: 'main',
33
+ serializerRegistry: {
34
+ yaml: storageSerializerYaml(),
35
+ md: storageSerializerMarkdown(),
36
+ },
37
+ defaultFileExtension: 'md',
38
+ });
39
+ ```
40
+
41
+ Then pass `storage` to `decapApi({ storage, ... })`.
@@ -0,0 +1,2 @@
1
+ export * from './storage-gh/index.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './storage-gh/index.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,97 @@
1
+ import { Octokit } from '@octokit/rest';
2
+ import { ConflictError } from 'laikacms/core';
3
+ import type { LaikaResult } from 'laikacms/core';
4
+ export interface GithubDataSourceOptions {
5
+ appId: string | number;
6
+ privateKey: string;
7
+ installationId: string | number;
8
+ owner: string;
9
+ repo: string;
10
+ branch: string;
11
+ /** Optional Octokit override — useful for tests. */
12
+ octokit?: Octokit;
13
+ /** Token cache TTL in seconds. Installation tokens last ~1h; default refresh well before. */
14
+ tokenTtlSeconds?: number;
15
+ /** User-Agent string sent on all requests. */
16
+ userAgent?: string;
17
+ }
18
+ interface GithubDirEntry {
19
+ name: string;
20
+ path: string;
21
+ type: 'file' | 'dir' | 'submodule' | 'symlink';
22
+ sha: string;
23
+ }
24
+ /**
25
+ * I/O against a single GitHub repository. Stateless aside from a cached
26
+ * installation token. All errors are mapped onto laikacms error types.
27
+ */
28
+ export declare class GithubDataSource {
29
+ private readonly owner;
30
+ private readonly repo;
31
+ private readonly branch;
32
+ private readonly userAgent;
33
+ private readonly tokenTtlMs;
34
+ private readonly externalOctokit?;
35
+ private readonly auth?;
36
+ private cachedOctokit?;
37
+ private cachedTokenExpiry;
38
+ constructor(opts: GithubDataSourceOptions);
39
+ /**
40
+ * GitHub App private keys are PEM strings. When passed via env vars they often arrive
41
+ * with literal `\n` sequences and surrounding quotes — normalize those.
42
+ */
43
+ private normalizePrivateKey;
44
+ /** Get an authenticated Octokit; mints a fresh installation token when the cached one expires. */
45
+ private getOctokit;
46
+ /**
47
+ * Fetch a file's content + sha. Returns NotFound when the path is a directory or missing.
48
+ */
49
+ getFileContents(relativePath: string): Promise<LaikaResult<{
50
+ content: string;
51
+ sha: string;
52
+ path: string;
53
+ }>>;
54
+ /** Get just the metadata (sha, lastModified) without downloading content. */
55
+ getFileMeta(relativePath: string): Promise<LaikaResult<{
56
+ sha: string;
57
+ createdAt: Date;
58
+ updatedAt: Date;
59
+ }>>;
60
+ /** Earliest and latest commit timestamps for a path. */
61
+ private getFirstAndLastCommitForFile;
62
+ /** List immediate children of a directory. Empty array when missing (mirrors storage-fs). */
63
+ listDirectory(relativePath: string): Promise<LaikaResult<GithubDirEntry[]>>;
64
+ /**
65
+ * Create or update a file. If `expectedSha` is supplied, the API enforces optimistic
66
+ * concurrency: a 409 response maps to VersionMismatchError.
67
+ */
68
+ createOrUpdate(relativePath: string, content: string, options?: {
69
+ expectedSha?: string;
70
+ commitMessage?: string;
71
+ author?: {
72
+ name: string;
73
+ email: string;
74
+ };
75
+ }): Promise<LaikaResult<{
76
+ sha: string;
77
+ path: string;
78
+ }>>;
79
+ /** Delete a file by sha. Empty-folder semantics are handled at the repository layer. */
80
+ deleteFile(relativePath: string, sha: string, options?: {
81
+ commitMessage?: string;
82
+ author?: {
83
+ name: string;
84
+ email: string;
85
+ };
86
+ }): Promise<LaikaResult<{
87
+ path: string;
88
+ }>>;
89
+ /**
90
+ * Distinguish file vs dir vs missing. Throws NotFoundError for missing paths so the caller
91
+ * can decide whether that's terminal — matches storage-fs.isDir behavior.
92
+ */
93
+ pathType(relativePath: string): Promise<'file' | 'dir'>;
94
+ private mapError;
95
+ }
96
+ export { ConflictError };
97
+ //# sourceMappingURL=github-datasource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-datasource.d.ts","sourceRoot":"","sources":["../../src/storage-gh/github-datasource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,286 @@
1
+ import { createAppAuth } from '@octokit/auth-app';
2
+ import { Octokit } from '@octokit/rest';
3
+ import { ConflictError, DirInsteadOfFile, FileInsteadOfDir, ForbiddenError, InternalError, NotFoundError, VersionMismatchError, } from 'laikacms/core';
4
+ import * as Result from 'effect/Result';
5
+ const DEFAULT_TOKEN_TTL_SECONDS = 50 * 60;
6
+ const DEFAULT_USER_AGENT = '@laikacms/github';
7
+ /**
8
+ * Convert a base64 string to UTF-8 text. Works in both Node and Workers
9
+ * (no `Buffer` dependency).
10
+ */
11
+ function base64ToText(b64) {
12
+ const binary = atob(b64.replace(/\s+/g, ''));
13
+ const bytes = new Uint8Array(binary.length);
14
+ for (let i = 0; i < binary.length; i++)
15
+ bytes[i] = binary.charCodeAt(i);
16
+ return new TextDecoder('utf-8').decode(bytes);
17
+ }
18
+ /** Convert UTF-8 text to a base64 string. */
19
+ function textToBase64(text) {
20
+ const bytes = new TextEncoder().encode(text);
21
+ let binary = '';
22
+ for (let i = 0; i < bytes.length; i++)
23
+ binary += String.fromCharCode(bytes[i]);
24
+ return btoa(binary);
25
+ }
26
+ const isOctokitError = (e) => typeof e === 'object' && e !== null && 'status' in e && typeof e.status === 'number';
27
+ /**
28
+ * I/O against a single GitHub repository. Stateless aside from a cached
29
+ * installation token. All errors are mapped onto laikacms error types.
30
+ */
31
+ export class GithubDataSource {
32
+ owner;
33
+ repo;
34
+ branch;
35
+ userAgent;
36
+ tokenTtlMs;
37
+ externalOctokit;
38
+ auth;
39
+ cachedOctokit;
40
+ cachedTokenExpiry = 0;
41
+ constructor(opts) {
42
+ this.owner = opts.owner;
43
+ this.repo = opts.repo;
44
+ this.branch = opts.branch;
45
+ this.userAgent = opts.userAgent ?? DEFAULT_USER_AGENT;
46
+ this.tokenTtlMs = (opts.tokenTtlSeconds ?? DEFAULT_TOKEN_TTL_SECONDS) * 1000;
47
+ if (opts.octokit) {
48
+ this.externalOctokit = opts.octokit;
49
+ }
50
+ else {
51
+ this.auth = createAppAuth({
52
+ appId: opts.appId,
53
+ privateKey: this.normalizePrivateKey(opts.privateKey),
54
+ installationId: opts.installationId,
55
+ });
56
+ }
57
+ }
58
+ /**
59
+ * GitHub App private keys are PEM strings. When passed via env vars they often arrive
60
+ * with literal `\n` sequences and surrounding quotes — normalize those.
61
+ */
62
+ normalizePrivateKey(raw) {
63
+ return raw.replace(/\\n/g, '\n').replace(/^"+|"+$/g, '');
64
+ }
65
+ /** Get an authenticated Octokit; mints a fresh installation token when the cached one expires. */
66
+ async getOctokit() {
67
+ if (this.externalOctokit)
68
+ return this.externalOctokit;
69
+ if (this.cachedOctokit && Date.now() < this.cachedTokenExpiry)
70
+ return this.cachedOctokit;
71
+ const result = await this.auth({ type: 'installation' });
72
+ const token = result.token;
73
+ this.cachedOctokit = new Octokit({ auth: token, userAgent: this.userAgent });
74
+ this.cachedTokenExpiry = Date.now() + this.tokenTtlMs;
75
+ return this.cachedOctokit;
76
+ }
77
+ /**
78
+ * Fetch a file's content + sha. Returns NotFound when the path is a directory or missing.
79
+ */
80
+ async getFileContents(relativePath) {
81
+ try {
82
+ const octokit = await this.getOctokit();
83
+ const { data } = await octokit.repos.getContent({
84
+ owner: this.owner,
85
+ repo: this.repo,
86
+ path: relativePath,
87
+ ref: this.branch,
88
+ });
89
+ if (Array.isArray(data)) {
90
+ return Result.fail(new DirInsteadOfFile(`Expected a file at ${relativePath} but found a directory`));
91
+ }
92
+ if (data.type !== 'file' || !('content' in data)) {
93
+ return Result.fail(new DirInsteadOfFile(`Expected a file at ${relativePath} but found ${data.type}`));
94
+ }
95
+ const content = data.encoding === 'base64' ? base64ToText(data.content) : data.content;
96
+ return Result.succeed({ content, sha: data.sha, path: relativePath });
97
+ }
98
+ catch (e) {
99
+ return this.mapError(e, relativePath);
100
+ }
101
+ }
102
+ /** Get just the metadata (sha, lastModified) without downloading content. */
103
+ async getFileMeta(relativePath) {
104
+ try {
105
+ const octokit = await this.getOctokit();
106
+ const { data } = await octokit.repos.getContent({
107
+ owner: this.owner,
108
+ repo: this.repo,
109
+ path: relativePath,
110
+ ref: this.branch,
111
+ });
112
+ if (Array.isArray(data) || data.type !== 'file') {
113
+ return Result.fail(new DirInsteadOfFile(`Expected a file at ${relativePath}`));
114
+ }
115
+ const commitInfo = await this.getFirstAndLastCommitForFile(relativePath);
116
+ return Result.succeed({
117
+ sha: data.sha,
118
+ createdAt: commitInfo.createdAt ?? new Date(0),
119
+ updatedAt: commitInfo.updatedAt ?? new Date(0),
120
+ });
121
+ }
122
+ catch (e) {
123
+ return this.mapError(e, relativePath);
124
+ }
125
+ }
126
+ /** Earliest and latest commit timestamps for a path. */
127
+ async getFirstAndLastCommitForFile(relativePath) {
128
+ try {
129
+ const octokit = await this.getOctokit();
130
+ const { data: recent, headers } = await octokit.rest.repos.listCommits({
131
+ owner: this.owner,
132
+ repo: this.repo,
133
+ path: relativePath,
134
+ sha: this.branch,
135
+ per_page: 1,
136
+ });
137
+ if (recent.length === 0)
138
+ return {};
139
+ const updatedAt = recent[0].commit.author?.date ?? recent[0].commit.committer?.date;
140
+ const link = headers.link;
141
+ const lastPage = link?.match(/<[^>]*[?&]page=(\d+)[^>]*>;\s*rel="last"/i)?.[1];
142
+ let createdAt = updatedAt;
143
+ if (lastPage && Number(lastPage) > 1) {
144
+ const { data: first } = await octokit.rest.repos.listCommits({
145
+ owner: this.owner,
146
+ repo: this.repo,
147
+ path: relativePath,
148
+ sha: this.branch,
149
+ per_page: 1,
150
+ page: Number(lastPage),
151
+ });
152
+ createdAt = first[0]?.commit.author?.date ?? first[0]?.commit.committer?.date ?? updatedAt;
153
+ }
154
+ return {
155
+ createdAt: createdAt ? new Date(createdAt) : undefined,
156
+ updatedAt: updatedAt ? new Date(updatedAt) : undefined,
157
+ };
158
+ }
159
+ catch {
160
+ return {};
161
+ }
162
+ }
163
+ /** List immediate children of a directory. Empty array when missing (mirrors storage-fs). */
164
+ async listDirectory(relativePath) {
165
+ try {
166
+ const octokit = await this.getOctokit();
167
+ const { data } = await octokit.repos.getContent({
168
+ owner: this.owner,
169
+ repo: this.repo,
170
+ path: relativePath,
171
+ ref: this.branch,
172
+ });
173
+ if (!Array.isArray(data)) {
174
+ return Result.fail(new FileInsteadOfDir(`Expected a directory at ${relativePath} but found a file`));
175
+ }
176
+ return Result.succeed(data
177
+ .filter(e => e.type === 'file' || e.type === 'dir')
178
+ .map(e => ({ name: e.name, path: e.path, type: e.type, sha: e.sha })));
179
+ }
180
+ catch (e) {
181
+ if (isOctokitError(e) && e.status === 404) {
182
+ // GitHub returns 404 for an empty directory. Treat as empty rather than missing —
183
+ // matches storage-fs semantics where listing an empty dir yields [].
184
+ return Result.succeed([]);
185
+ }
186
+ return this.mapError(e, relativePath);
187
+ }
188
+ }
189
+ /**
190
+ * Create or update a file. If `expectedSha` is supplied, the API enforces optimistic
191
+ * concurrency: a 409 response maps to VersionMismatchError.
192
+ */
193
+ async createOrUpdate(relativePath, content, options = {}) {
194
+ try {
195
+ const octokit = await this.getOctokit();
196
+ const message = options.commitMessage
197
+ ?? `${options.expectedSha ? 'Update' : 'Create'} ${relativePath}`;
198
+ // GitHub's createOrUpdateFileContents API requires a `sha` to update an existing file.
199
+ // When no expectedSha is given but a file exists, we look it up first to allow upserts.
200
+ let sha = options.expectedSha;
201
+ if (!sha) {
202
+ const existing = await this.getFileContents(relativePath);
203
+ if (Result.isSuccess(existing))
204
+ sha = existing.success.sha;
205
+ }
206
+ const { data } = await octokit.repos.createOrUpdateFileContents({
207
+ owner: this.owner,
208
+ repo: this.repo,
209
+ path: relativePath,
210
+ message,
211
+ content: textToBase64(content),
212
+ branch: this.branch,
213
+ sha,
214
+ committer: options.author,
215
+ });
216
+ const newSha = data.content?.sha;
217
+ if (!newSha) {
218
+ return Result.fail(new InternalError('GitHub did not return a sha for the written file'));
219
+ }
220
+ return Result.succeed({ sha: newSha, path: relativePath });
221
+ }
222
+ catch (e) {
223
+ return this.mapError(e, relativePath);
224
+ }
225
+ }
226
+ /** Delete a file by sha. Empty-folder semantics are handled at the repository layer. */
227
+ async deleteFile(relativePath, sha, options = {}) {
228
+ try {
229
+ const octokit = await this.getOctokit();
230
+ await octokit.repos.deleteFile({
231
+ owner: this.owner,
232
+ repo: this.repo,
233
+ path: relativePath,
234
+ message: options.commitMessage ?? `Delete ${relativePath}`,
235
+ sha,
236
+ branch: this.branch,
237
+ committer: options.author,
238
+ });
239
+ return Result.succeed({ path: relativePath });
240
+ }
241
+ catch (e) {
242
+ return this.mapError(e, relativePath);
243
+ }
244
+ }
245
+ /**
246
+ * Distinguish file vs dir vs missing. Throws NotFoundError for missing paths so the caller
247
+ * can decide whether that's terminal — matches storage-fs.isDir behavior.
248
+ */
249
+ async pathType(relativePath) {
250
+ const octokit = await this.getOctokit();
251
+ try {
252
+ const { data } = await octokit.repos.getContent({
253
+ owner: this.owner,
254
+ repo: this.repo,
255
+ path: relativePath,
256
+ ref: this.branch,
257
+ });
258
+ return Array.isArray(data) ? 'dir' : 'file';
259
+ }
260
+ catch (e) {
261
+ if (isOctokitError(e) && e.status === 404) {
262
+ throw new NotFoundError(`The path at ${relativePath} does not exist`);
263
+ }
264
+ throw e;
265
+ }
266
+ }
267
+ mapError(error, contextPath) {
268
+ if (!isOctokitError(error)) {
269
+ return Result.fail(new InternalError(`GitHub request failed: ${error instanceof Error ? error.message : String(error)}`));
270
+ }
271
+ switch (error.status) {
272
+ case 404:
273
+ return Result.fail(new NotFoundError(`The file at ${contextPath} does not exist`));
274
+ case 401:
275
+ case 403:
276
+ return Result.fail(new ForbiddenError(`Access denied for ${contextPath}: ${error.message ?? 'forbidden'}`));
277
+ case 409:
278
+ case 422:
279
+ return Result.fail(new VersionMismatchError(`Conflict writing ${contextPath}: someone else modified the file since you last viewed it.`));
280
+ default:
281
+ return Result.fail(new InternalError(`GitHub returned ${error.status} for ${contextPath}: ${error.message ?? 'error'}`));
282
+ }
283
+ }
284
+ }
285
+ export { ConflictError };
286
+ //# sourceMappingURL=github-datasource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-datasource.js","sourceRoot":"","sources":["../../src/storage-gh/github-datasource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { LaikaResult } from 'laikacms/core';
2
+ import { type Atom, type AtomSummary, type Folder, type FolderCreate, type ListAtomsOptions, type StorageObject, type StorageObjectCreate, type StorageObjectUpdate, StorageRepository, type StorageSerializerRegistry } from 'laikacms/storage';
3
+ import { type GithubDataSourceOptions } from './github-datasource.js';
4
+ export interface GithubStorageRepositoryOptions extends GithubDataSourceOptions {
5
+ serializerRegistry: StorageSerializerRegistry;
6
+ defaultFileExtension: string;
7
+ /** Glob patterns to exclude when listing. Defaults match storage-fs. */
8
+ ignoreList?: string[];
9
+ /** Optional commit author/committer applied to every write. */
10
+ commitAuthor?: {
11
+ name: string;
12
+ email: string;
13
+ };
14
+ }
15
+ /**
16
+ * StorageRepository backed by a GitHub repository. Mirrors the surface and semantics of
17
+ * `laikacms/storage-fs` so that swapping FS for GitHub is purely a wiring change.
18
+ */
19
+ export declare class GithubStorageRepository extends StorageRepository {
20
+ private readonly dataSource;
21
+ private readonly serializerRegistry;
22
+ private readonly defaultFileExtension;
23
+ private readonly availableExtensions;
24
+ private readonly excludeFilter;
25
+ private readonly commitAuthor?;
26
+ constructor(options: GithubStorageRepositoryOptions);
27
+ private stripExtension;
28
+ private resolvePathWithExtension;
29
+ private serialize;
30
+ private deserialize;
31
+ getObject(key: string): AsyncGenerator<LaikaResult<StorageObject>>;
32
+ createObject(create: StorageObjectCreate): AsyncGenerator<LaikaResult<StorageObject>>;
33
+ updateObject(update: StorageObjectUpdate): AsyncGenerator<LaikaResult<StorageObject>>;
34
+ createOrUpdateObject(create: StorageObjectCreate): AsyncGenerator<LaikaResult<StorageObject>>;
35
+ getFolder(key: string): AsyncGenerator<LaikaResult<Folder>>;
36
+ createFolder(folderCreate: FolderCreate): AsyncGenerator<LaikaResult<Folder>>;
37
+ getAtom(key: string): AsyncGenerator<LaikaResult<Atom>>;
38
+ listAtomSummaries(folderKey: string, options: ListAtomsOptions): AsyncGenerator<LaikaResult<readonly AtomSummary[]>>;
39
+ listAtoms(folderKey: string, options: ListAtomsOptions): AsyncGenerator<LaikaResult<readonly Atom[]>>;
40
+ private collectAtoms;
41
+ removeAtoms(keys: readonly string[]): AsyncGenerator<LaikaResult<readonly string[]>>;
42
+ }
43
+ //# sourceMappingURL=github-storage-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-storage-repository.d.ts","sourceRoot":"","sources":["../../src/storage-gh/github-storage-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAErB,KAAK,aAAa,EAElB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,iBAAiB,EACjB,KAAK,yBAAyB,EAC/B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAExF,MAAM,WAAW,8BAA+B,SAAQ,uBAAuB;IAC7E,kBAAkB,EAAE,yBAAyB,CAAC;IAC9C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+DAA+D;IAC/D,YAAY,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAaD;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,iBAAiB;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IAC/D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAW;IAC/C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuB;IACrD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAkC;gBAEpD,OAAO,EAAE,8BAA8B;IAoBnD,OAAO,CAAC,cAAc;YAOR,wBAAwB;YAUxB,SAAS;YAYT,WAAW;IAclB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAwBlE,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IA4BrF,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAmBrF,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAiB7F,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAW3D,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAc7E,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAe9D,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,cAAc,CAAC,WAAW,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC;IAIpH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,cAAc,CAAC,WAAW,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;YAItF,YAAY;IA6CpB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CAAC,WAAW,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;CA2B5F"}
@@ -0,0 +1,277 @@
1
+ import { BadRequestError, EntryAlreadyExistsError, InvalidData, NotFoundError } from 'laikacms/core';
2
+ import { pathCombine, StorageRepository, } from 'laikacms/storage';
3
+ import * as Result from 'effect/Result';
4
+ import * as minimatch from 'minimatch';
5
+ import { GithubDataSource } from './github-datasource.js';
6
+ const DEFAULT_IGNORE_LIST = [
7
+ '**/.keep',
8
+ '**/.DS_Store',
9
+ '**/Thumbs.db',
10
+ '**/desktop.ini',
11
+ '**/.contentbase',
12
+ '**/.laikacms',
13
+ ];
14
+ const failAs = (error) => Result.fail(error);
15
+ /**
16
+ * StorageRepository backed by a GitHub repository. Mirrors the surface and semantics of
17
+ * `laikacms/storage-fs` so that swapping FS for GitHub is purely a wiring change.
18
+ */
19
+ export class GithubStorageRepository extends StorageRepository {
20
+ dataSource;
21
+ serializerRegistry;
22
+ defaultFileExtension;
23
+ availableExtensions;
24
+ excludeFilter;
25
+ commitAuthor;
26
+ constructor(options) {
27
+ super();
28
+ const { serializerRegistry, defaultFileExtension, ignoreList = DEFAULT_IGNORE_LIST, commitAuthor, ...dataSourceOptions } = options;
29
+ this.serializerRegistry = serializerRegistry;
30
+ this.defaultFileExtension = defaultFileExtension;
31
+ this.availableExtensions = Object.keys(serializerRegistry);
32
+ this.commitAuthor = commitAuthor;
33
+ this.dataSource = new GithubDataSource(dataSourceOptions);
34
+ this.excludeFilter = ignoreList
35
+ .map(p => minimatch.makeRe(p, { dot: true, partial: true }))
36
+ .filter((x) => x !== false);
37
+ }
38
+ stripExtension(p) {
39
+ for (const ext of this.availableExtensions) {
40
+ if (p.endsWith(`.${ext}`))
41
+ return p.slice(0, -(ext.length + 1));
42
+ }
43
+ return p;
44
+ }
45
+ async resolvePathWithExtension(key) {
46
+ const base = this.stripExtension(key);
47
+ for (const ext of this.availableExtensions) {
48
+ const candidate = `${base}.${ext}`;
49
+ const meta = await this.dataSource.getFileMeta(candidate);
50
+ if (Result.isSuccess(meta))
51
+ return { path: candidate, extension: ext };
52
+ }
53
+ return null;
54
+ }
55
+ async serialize(ext, content) {
56
+ const cleanExt = ext.startsWith('.') ? ext.slice(1) : ext;
57
+ const serializer = this.serializerRegistry[cleanExt];
58
+ if (!serializer) {
59
+ throw new BadRequestError(`No serializer found for file extension: .${cleanExt}. `
60
+ + `Available formats: ${Object.keys(this.serializerRegistry).join(', ')}`);
61
+ }
62
+ return serializer.serializeDocumentFileContents(content, {});
63
+ }
64
+ async deserialize(ext, content) {
65
+ const cleanExt = ext.startsWith('.') ? ext.slice(1) : ext;
66
+ const serializer = this.serializerRegistry[cleanExt];
67
+ if (!serializer) {
68
+ throw new BadRequestError(`No serializer found for file extension: .${cleanExt}. `
69
+ + `Available formats: ${Object.keys(this.serializerRegistry).join(', ')}`);
70
+ }
71
+ return serializer.deserializeDocumentFileContents(content, {});
72
+ }
73
+ // ===== StorageObjects =====
74
+ async *getObject(key) {
75
+ const resolved = await this.resolvePathWithExtension(key);
76
+ if (!resolved) {
77
+ yield failAs(new NotFoundError(`The file at ${key} does not exist`));
78
+ return;
79
+ }
80
+ const [contentResult, metaResult] = await Promise.all([
81
+ this.dataSource.getFileContents(resolved.path),
82
+ this.dataSource.getFileMeta(resolved.path),
83
+ ]);
84
+ if (Result.isFailure(contentResult)) {
85
+ yield failAs(contentResult.failure);
86
+ return;
87
+ }
88
+ if (Result.isFailure(metaResult)) {
89
+ yield failAs(metaResult.failure);
90
+ return;
91
+ }
92
+ yield Result.succeed({
93
+ type: 'object',
94
+ key: this.stripExtension(resolved.path),
95
+ createdAt: metaResult.success.createdAt.toISOString(),
96
+ updatedAt: metaResult.success.updatedAt.toISOString(),
97
+ content: await this.deserialize(resolved.extension, contentResult.success.content),
98
+ });
99
+ }
100
+ async *createObject(create) {
101
+ if (!create.content) {
102
+ yield Result.fail(new InvalidData('Object content is required for creation'));
103
+ return;
104
+ }
105
+ const existing = await this.resolvePathWithExtension(create.key);
106
+ if (existing) {
107
+ yield Result.fail(new EntryAlreadyExistsError(`An object with key "${create.key}" already exists with extension .${existing.extension}`));
108
+ return;
109
+ }
110
+ const ext = this.defaultFileExtension;
111
+ const serialized = await this.serialize(ext, create.content);
112
+ const path = `${this.stripExtension(create.key)}.${ext}`;
113
+ const writeResult = await this.dataSource.createOrUpdate(path, serialized, {
114
+ commitMessage: `Create ${path}`,
115
+ author: this.commitAuthor,
116
+ });
117
+ if (Result.isFailure(writeResult)) {
118
+ yield failAs(writeResult.failure);
119
+ return;
120
+ }
121
+ yield* this.getObject(create.key);
122
+ }
123
+ async *updateObject(update) {
124
+ const resolved = await this.resolvePathWithExtension(update.key);
125
+ if (!resolved) {
126
+ yield failAs(new NotFoundError(`The file at ${update.key} does not exist`));
127
+ return;
128
+ }
129
+ if (update.content) {
130
+ const serialized = await this.serialize(resolved.extension, update.content);
131
+ const writeResult = await this.dataSource.createOrUpdate(resolved.path, serialized, {
132
+ commitMessage: `Update ${resolved.path}`,
133
+ author: this.commitAuthor,
134
+ });
135
+ if (Result.isFailure(writeResult)) {
136
+ yield failAs(writeResult.failure);
137
+ return;
138
+ }
139
+ }
140
+ yield* this.getObject(update.key);
141
+ }
142
+ async *createOrUpdateObject(create) {
143
+ const existing = await this.resolvePathWithExtension(create.key);
144
+ const ext = existing?.extension ?? this.defaultFileExtension;
145
+ const path = existing?.path ?? `${this.stripExtension(create.key)}.${ext}`;
146
+ const serialized = create.content ? await this.serialize(ext, create.content) : '';
147
+ const writeResult = await this.dataSource.createOrUpdate(path, serialized, {
148
+ commitMessage: `${existing ? 'Update' : 'Create'} ${path}`,
149
+ author: this.commitAuthor,
150
+ });
151
+ if (Result.isFailure(writeResult)) {
152
+ yield failAs(writeResult.failure);
153
+ return;
154
+ }
155
+ yield* this.getObject(create.key);
156
+ }
157
+ // ===== Folders =====
158
+ async *getFolder(key) {
159
+ // Listing the directory both validates existence and gives us a baseline.
160
+ const listing = await this.dataSource.listDirectory(key);
161
+ if (Result.isFailure(listing)) {
162
+ yield failAs(listing.failure);
163
+ return;
164
+ }
165
+ // GitHub doesn't track per-directory timestamps. Approximate with the most recent commit
166
+ // touching the path; if no commits, fall back to epoch.
167
+ const now = new Date(0).toISOString();
168
+ yield Result.succeed({ type: 'folder', key, createdAt: now, updatedAt: now });
169
+ }
170
+ async *createFolder(folderCreate) {
171
+ // GitHub has no concept of empty directories — emulate by writing a `.keep` placeholder.
172
+ const keepPath = pathCombine(folderCreate.key, '.keep');
173
+ const writeResult = await this.dataSource.createOrUpdate(keepPath, '', {
174
+ commitMessage: `Create directory ${folderCreate.key}`,
175
+ author: this.commitAuthor,
176
+ });
177
+ if (Result.isFailure(writeResult)) {
178
+ yield failAs(writeResult.failure);
179
+ return;
180
+ }
181
+ yield* this.getFolder(folderCreate.key);
182
+ }
183
+ // ===== Atoms =====
184
+ async *getAtom(key) {
185
+ try {
186
+ const type = await this.dataSource.pathType(key);
187
+ if (type === 'file')
188
+ yield* this.getObject(key);
189
+ else
190
+ yield* this.getFolder(key);
191
+ }
192
+ catch (e) {
193
+ if (e instanceof NotFoundError) {
194
+ // Treat key as a file path with extension — getObject handles the extension probe.
195
+ yield* this.getObject(key);
196
+ }
197
+ else {
198
+ yield Result.fail(e instanceof Error ? new BadRequestError(e.message) : new BadRequestError(String(e)));
199
+ }
200
+ }
201
+ }
202
+ listAtomSummaries(folderKey, options) {
203
+ return this.collectAtoms(folderKey, options, true);
204
+ }
205
+ listAtoms(folderKey, options) {
206
+ return this.collectAtoms(folderKey, options, false);
207
+ }
208
+ async *collectAtoms(folderKey, _options, summariesOnly) {
209
+ const listing = await this.dataSource.listDirectory(folderKey);
210
+ if (Result.isFailure(listing)) {
211
+ yield failAs(listing.failure);
212
+ return;
213
+ }
214
+ const filtered = listing.success.filter(entry => this.excludeFilter.every(re => !re.test(entry.path)));
215
+ const summaries = filtered.map(entry => {
216
+ let key = entry.path;
217
+ if (entry.type === 'file') {
218
+ for (const ext of this.availableExtensions) {
219
+ if (key.endsWith(`.${ext}`)) {
220
+ key = key.slice(0, -(ext.length + 1));
221
+ break;
222
+ }
223
+ }
224
+ }
225
+ return {
226
+ type: entry.type === 'file' ? 'object-summary' : 'folder-summary',
227
+ key,
228
+ };
229
+ });
230
+ if (summariesOnly) {
231
+ yield Result.succeed(summaries);
232
+ return;
233
+ }
234
+ const atoms = [];
235
+ for (const summary of summaries) {
236
+ if (summary.type === 'object-summary') {
237
+ for await (const r of this.getObject(summary.key)) {
238
+ if (Result.isSuccess(r))
239
+ atoms.push(r.success);
240
+ }
241
+ }
242
+ else {
243
+ for await (const r of this.getFolder(summary.key)) {
244
+ if (Result.isSuccess(r))
245
+ atoms.push(r.success);
246
+ }
247
+ }
248
+ }
249
+ yield Result.succeed(atoms);
250
+ }
251
+ async *removeAtoms(keys) {
252
+ const removed = [];
253
+ for (const key of keys) {
254
+ const resolved = await this.resolvePathWithExtension(key);
255
+ if (!resolved) {
256
+ yield failAs(new NotFoundError(`The file at ${key} does not exist`));
257
+ return;
258
+ }
259
+ const meta = await this.dataSource.getFileMeta(resolved.path);
260
+ if (Result.isFailure(meta)) {
261
+ yield failAs(meta.failure);
262
+ return;
263
+ }
264
+ const deleteResult = await this.dataSource.deleteFile(resolved.path, meta.success.sha, {
265
+ commitMessage: `Delete ${resolved.path}`,
266
+ author: this.commitAuthor,
267
+ });
268
+ if (Result.isFailure(deleteResult)) {
269
+ yield failAs(deleteResult.failure);
270
+ return;
271
+ }
272
+ removed.push(key);
273
+ }
274
+ yield Result.succeed(removed);
275
+ }
276
+ }
277
+ //# sourceMappingURL=github-storage-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-storage-repository.js","sourceRoot":"","sources":["../../src/storage-gh/github-storage-repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { GithubStorageRepository } from './github-storage-repository.js';
2
+ export type { GithubStorageRepositoryOptions } from './github-storage-repository.js';
3
+ export { GithubDataSource } from './github-datasource.js';
4
+ export type { GithubDataSourceOptions } from './github-datasource.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage-gh/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,YAAY,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { GithubStorageRepository } from './github-storage-repository.js';
2
+ export { GithubDataSource } from './github-datasource.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage-gh/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@laikacms/github",
3
+ "version": "0.1.2",
4
+ "description": "GitHub-backed StorageRepository for Laika CMS. Stores content in a GitHub repository; authenticates as a GitHub App via installation tokens.",
5
+ "keywords": [
6
+ "headless-cms",
7
+ "content-management",
8
+ "github",
9
+ "storage",
10
+ "octokit"
11
+ ],
12
+ "author": {
13
+ "name": "Sem Postma",
14
+ "email": "sem@laikacms.com",
15
+ "url": "https://sem-postma.com"
16
+ },
17
+ "homepage": "https://github.com/laikacms/laikacms",
18
+ "bugs": {
19
+ "email": "sem@laikacms.com",
20
+ "url": "https://github.com/laikacms/laikacms/issues"
21
+ },
22
+ "license": "MIT",
23
+ "repository": "https://github.com/laikacms/laikacms",
24
+ "type": "module",
25
+ "sideEffects": false,
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE",
30
+ "CHANGELOG.md"
31
+ ],
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js",
36
+ "default": "./dist/index.js"
37
+ },
38
+ "./storage-gh": {
39
+ "types": "./dist/storage-gh/index.d.ts",
40
+ "import": "./dist/storage-gh/index.js",
41
+ "default": "./dist/storage-gh/index.js"
42
+ }
43
+ },
44
+ "dependencies": {
45
+ "@octokit/auth-app": "^7.1.4",
46
+ "@octokit/rest": "^22.0.1",
47
+ "effect": "4.0.0-beta.40",
48
+ "minimatch": "^10.0.3",
49
+ "laikacms": "0.1.2"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^22.19.15",
53
+ "rimraf": "^6.1.3",
54
+ "typescript": "^6.0.2",
55
+ "vitest": "^4.1.2"
56
+ },
57
+ "engines": {
58
+ "node": "22.x"
59
+ },
60
+ "scripts": {
61
+ "clean": "rimraf dist .turbo",
62
+ "build": "tsc -p tsconfig.json",
63
+ "dev": "tsc -p tsconfig.json --watch",
64
+ "typecheck": "tsc --noEmit -p tsconfig.json",
65
+ "test": "vitest run",
66
+ "test:watch": "vitest"
67
+ }
68
+ }