@laikacms/github 0.1.2 → 2.0.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/CHANGELOG.md +129 -0
- package/README.md +162 -18
- package/dist/storage-gh/github-datasource.d.ts +12 -4
- package/dist/storage-gh/github-datasource.d.ts.map +1 -1
- package/dist/storage-gh/github-datasource.js +22 -4
- package/dist/storage-gh/github-datasource.js.map +1 -1
- package/dist/storage-gh/github-storage-repository.d.ts +21 -19
- package/dist/storage-gh/github-storage-repository.d.ts.map +1 -1
- package/dist/storage-gh/github-storage-repository.js +219 -188
- package/dist/storage-gh/github-storage-repository.js.map +1 -1
- package/dist/storage-gh/index.d.ts +3 -2
- package/dist/storage-gh/index.d.ts.map +1 -1
- package/dist/storage-gh/index.js +2 -1
- package/dist/storage-gh/index.js.map +1 -1
- package/dist/storage-gh/testing/index.d.ts +3 -0
- package/dist/storage-gh/testing/index.d.ts.map +1 -0
- package/dist/storage-gh/testing/index.js +142 -0
- package/dist/storage-gh/testing/index.js.map +1 -0
- package/package.json +15 -9
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { GithubStorageRepository } from '../github-storage-repository.js';
|
|
2
|
+
function textToBase64(text) {
|
|
3
|
+
const bytes = new TextEncoder().encode(text);
|
|
4
|
+
let binary = '';
|
|
5
|
+
for (let i = 0; i < bytes.length; i++)
|
|
6
|
+
binary += String.fromCharCode(bytes[i]);
|
|
7
|
+
return btoa(binary);
|
|
8
|
+
}
|
|
9
|
+
const createMockOctokit = () => {
|
|
10
|
+
const files = new Map();
|
|
11
|
+
let shaCounter = 0;
|
|
12
|
+
const newSha = () => `sha${(++shaCounter).toString().padStart(40, '0')}`;
|
|
13
|
+
const createdAt = new Date('2026-01-01');
|
|
14
|
+
const getContentData = (path) => {
|
|
15
|
+
const exactFile = files.get(path);
|
|
16
|
+
if (exactFile) {
|
|
17
|
+
return {
|
|
18
|
+
type: 'file',
|
|
19
|
+
content: textToBase64(exactFile.content),
|
|
20
|
+
encoding: 'base64',
|
|
21
|
+
sha: exactFile.sha,
|
|
22
|
+
path,
|
|
23
|
+
name: path.split('/').pop() ?? path,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// Build a directory listing that includes both direct file children and
|
|
27
|
+
// immediate subdirectory entries (needed for depth>1 traversal).
|
|
28
|
+
const childPrefix = path === '' ? '' : `${path}/`;
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const entries = [];
|
|
31
|
+
for (const f of files.values()) {
|
|
32
|
+
if (path !== '' && !f.path.startsWith(childPrefix))
|
|
33
|
+
continue;
|
|
34
|
+
const rest = path === '' ? f.path : f.path.slice(childPrefix.length);
|
|
35
|
+
const slash = rest.indexOf('/');
|
|
36
|
+
if (slash === -1) {
|
|
37
|
+
// Direct file child
|
|
38
|
+
if (!seen.has(f.path)) {
|
|
39
|
+
seen.add(f.path);
|
|
40
|
+
entries.push({ type: 'file', sha: f.sha, path: f.path, name: f.path.split('/').pop() ?? f.path });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Nested under a subdirectory — emit the immediate subdirectory entry
|
|
45
|
+
const childName = rest.slice(0, slash);
|
|
46
|
+
const childPath = path === '' ? childName : `${path}/${childName}`;
|
|
47
|
+
if (!seen.has(childPath)) {
|
|
48
|
+
seen.add(childPath);
|
|
49
|
+
entries.push({ type: 'dir', sha: f.sha, path: childPath, name: childName });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (entries.length > 0)
|
|
54
|
+
return entries;
|
|
55
|
+
// Simulate 404
|
|
56
|
+
const err = new Error('Not Found');
|
|
57
|
+
err.status = 404;
|
|
58
|
+
throw err;
|
|
59
|
+
};
|
|
60
|
+
const octokit = {
|
|
61
|
+
repos: {
|
|
62
|
+
getContent: async ({ path }) => {
|
|
63
|
+
const data = getContentData(path);
|
|
64
|
+
return { data };
|
|
65
|
+
},
|
|
66
|
+
createOrUpdateFileContents: async ({ path, content, sha: expectedSha, }) => {
|
|
67
|
+
const existing = files.get(path);
|
|
68
|
+
if (existing && expectedSha && existing.sha !== expectedSha) {
|
|
69
|
+
const err = new Error('Conflict');
|
|
70
|
+
err.status = 409;
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
const sha = newSha();
|
|
74
|
+
const decoded = (() => {
|
|
75
|
+
const b64 = content.replace(/\s+/g, '');
|
|
76
|
+
const binary = atob(b64);
|
|
77
|
+
const bytes = new Uint8Array(binary.length);
|
|
78
|
+
for (let i = 0; i < binary.length; i++)
|
|
79
|
+
bytes[i] = binary.charCodeAt(i);
|
|
80
|
+
return new TextDecoder().decode(bytes);
|
|
81
|
+
})();
|
|
82
|
+
files.set(path, { content: decoded, sha, path });
|
|
83
|
+
return { data: { content: { sha, path } } };
|
|
84
|
+
},
|
|
85
|
+
deleteFile: async ({ path, sha, }) => {
|
|
86
|
+
const existing = files.get(path);
|
|
87
|
+
if (!existing) {
|
|
88
|
+
const err = new Error('Not Found');
|
|
89
|
+
err.status = 404;
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
if (existing.sha !== sha) {
|
|
93
|
+
const err = new Error('Conflict');
|
|
94
|
+
err.status = 409;
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
files.delete(path);
|
|
98
|
+
return {};
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
rest: {
|
|
102
|
+
repos: {
|
|
103
|
+
listCommits: async (_params) => {
|
|
104
|
+
// Return a single fake commit for all paths.
|
|
105
|
+
const commit = {
|
|
106
|
+
commit: {
|
|
107
|
+
author: { date: createdAt.toISOString() },
|
|
108
|
+
committer: { date: createdAt.toISOString() },
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
data: [commit],
|
|
113
|
+
headers: {},
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
return { files, octokit };
|
|
120
|
+
};
|
|
121
|
+
const makeSerializerRegistry = () => ({
|
|
122
|
+
json: {
|
|
123
|
+
format: { mediaType: 'application/json' },
|
|
124
|
+
serializeDocumentFileContents: async (content) => JSON.stringify(content),
|
|
125
|
+
deserializeDocumentFileContents: async (raw) => JSON.parse(raw),
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
export const githubContractCase = {
|
|
129
|
+
name: 'GithubStorageRepository',
|
|
130
|
+
async makeRepo() {
|
|
131
|
+
const { octokit } = createMockOctokit();
|
|
132
|
+
return new GithubStorageRepository({
|
|
133
|
+
owner: 'test-owner',
|
|
134
|
+
repo: 'test-repo',
|
|
135
|
+
branch: 'main',
|
|
136
|
+
octokit: octokit,
|
|
137
|
+
serializerRegistry: makeSerializerRegistry(),
|
|
138
|
+
defaultFileExtension: 'json',
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/storage-gh/testing/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laikacms/github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
4
7
|
"description": "GitHub-backed StorageRepository for Laika CMS. Stores content in a GitHub repository; authenticates as a GitHub App via installation tokens.",
|
|
5
8
|
"keywords": [
|
|
6
9
|
"headless-cms",
|
|
@@ -20,7 +23,10 @@
|
|
|
20
23
|
"url": "https://github.com/laikacms/laikacms/issues"
|
|
21
24
|
},
|
|
22
25
|
"license": "MIT",
|
|
23
|
-
"repository":
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/laikacms/laikacms"
|
|
29
|
+
},
|
|
24
30
|
"type": "module",
|
|
25
31
|
"sideEffects": false,
|
|
26
32
|
"files": [
|
|
@@ -44,18 +50,18 @@
|
|
|
44
50
|
"dependencies": {
|
|
45
51
|
"@octokit/auth-app": "^7.1.4",
|
|
46
52
|
"@octokit/rest": "^22.0.1",
|
|
47
|
-
"effect": "4.0.0-beta.
|
|
48
|
-
"minimatch": "^10.
|
|
49
|
-
"laikacms": "0.
|
|
53
|
+
"effect": "4.0.0-beta.104",
|
|
54
|
+
"minimatch": "^10.2.5",
|
|
55
|
+
"laikacms": "6.0.0"
|
|
50
56
|
},
|
|
51
57
|
"devDependencies": {
|
|
52
|
-
"@types/node": "^
|
|
58
|
+
"@types/node": "^24.13.3",
|
|
53
59
|
"rimraf": "^6.1.3",
|
|
54
|
-
"typescript": "^6.0.
|
|
55
|
-
"vitest": "^4.1.
|
|
60
|
+
"typescript": "^6.0.3",
|
|
61
|
+
"vitest": "^4.1.10"
|
|
56
62
|
},
|
|
57
63
|
"engines": {
|
|
58
|
-
"node": "
|
|
64
|
+
"node": "24.x"
|
|
59
65
|
},
|
|
60
66
|
"scripts": {
|
|
61
67
|
"clean": "rimraf dist .turbo",
|