alinea 1.4.0-preview.1 → 1.4.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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "_id": "2zK16TfpATzOs6qMMNCmcqMSd5w",
2
+ "_id": "2zMfvzyGJk5gAIRxlYhGAkY87JU",
3
3
  "_type": "Page",
4
4
  "_index": "a0",
5
5
  "_seeded": "welcome.json",
package/dist/LICENSES.md CHANGED
@@ -28,34 +28,11 @@ SOFTWARE.
28
28
 
29
29
  ===
30
30
 
31
- # htmlparser2@9.1.0 (MIT)
32
-
33
- Copyright 2010, 2011, Chris Winberry <chris@winberry.net>. All rights reserved.
34
- Permission is hereby granted, free of charge, to any person obtaining a copy
35
- of this software and associated documentation files (the "Software"), to
36
- deal in the Software without restriction, including without limitation the
37
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
38
- sell copies of the Software, and to permit persons to whom the Software is
39
- furnished to do so, subject to the following conditions:
40
-
41
- The above copyright notice and this permission notice shall be included in
42
- all copies or substantial portions of the Software.
43
-
44
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
49
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
50
- IN THE SOFTWARE.
51
-
52
- ===
53
-
54
- # react-query@3.39.3 (MIT)
31
+ # lib0@0.2.88 (MIT)
55
32
 
56
- MIT License
33
+ The MIT License (MIT)
57
34
 
58
- Copyright (c) 2019 Tanner Linsley
35
+ Copyright (c) 2019 Kevin Jahns <kevin.jahns@protonmail.com>.
59
36
 
60
37
  Permission is hereby granted, free of charge, to any person obtaining a copy
61
38
  of this software and associated documentation files (the "Software"), to deal
@@ -104,11 +81,11 @@ SOFTWARE.
104
81
 
105
82
  ===
106
83
 
107
- # lib0@0.2.88 (MIT)
84
+ # react-query@3.39.3 (MIT)
108
85
 
109
- The MIT License (MIT)
86
+ MIT License
110
87
 
111
- Copyright (c) 2019 Kevin Jahns <kevin.jahns@protonmail.com>.
88
+ Copyright (c) 2019 Tanner Linsley
112
89
 
113
90
  Permission is hereby granted, free of charge, to any person obtaining a copy
114
91
  of this software and associated documentation files (the "Software"), to deal
@@ -131,7 +108,30 @@ SOFTWARE.
131
108
 
132
109
  ===
133
110
 
134
- # domutils@3.1.0 (BSD-2-Clause)
111
+ # htmlparser2@9.1.0 (MIT)
112
+
113
+ Copyright 2010, 2011, Chris Winberry <chris@winberry.net>. All rights reserved.
114
+ Permission is hereby granted, free of charge, to any person obtaining a copy
115
+ of this software and associated documentation files (the "Software"), to
116
+ deal in the Software without restriction, including without limitation the
117
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
118
+ sell copies of the Software, and to permit persons to whom the Software is
119
+ furnished to do so, subject to the following conditions:
120
+
121
+ The above copyright notice and this permission notice shall be included in
122
+ all copies or substantial portions of the Software.
123
+
124
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
125
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
126
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
127
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
128
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
129
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
130
+ IN THE SOFTWARE.
131
+
132
+ ===
133
+
134
+ # domhandler@5.0.3 (BSD-2-Clause)
135
135
 
136
136
  Copyright (c) Felix Böhm
137
137
  All rights reserved.
@@ -148,7 +148,7 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
148
148
 
149
149
  ===
150
150
 
151
- # domhandler@5.0.3 (BSD-2-Clause)
151
+ # entities@4.5.0 (BSD-2-Clause)
152
152
 
153
153
  Copyright (c) Felix Böhm
154
154
  All rights reserved.
@@ -165,7 +165,7 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
165
165
 
166
166
  ===
167
167
 
168
- # entities@4.5.0 (BSD-2-Clause)
168
+ # domutils@3.1.0 (BSD-2-Clause)
169
169
 
170
170
  Copyright (c) Felix Böhm
171
171
  All rights reserved.
@@ -180,18 +180,6 @@ THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRE
180
180
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
181
181
 
182
182
 
183
- ===
184
-
185
- # @headless-tree/core@0.0.15 (MIT)
186
-
187
- MIT
188
-
189
- ===
190
-
191
- # @headless-tree/react@0.0.15 (MIT)
192
-
193
- MIT
194
-
195
183
  ===
196
184
 
197
185
  # @popperjs/core@2.11.8 (MIT)
@@ -218,6 +206,33 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
218
206
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
219
207
 
220
208
 
209
+ ===
210
+
211
+ # prop-types@15.8.1 (MIT)
212
+
213
+ MIT License
214
+
215
+ Copyright (c) 2013-present, Facebook, Inc.
216
+
217
+ Permission is hereby granted, free of charge, to any person obtaining a copy
218
+ of this software and associated documentation files (the "Software"), to deal
219
+ in the Software without restriction, including without limitation the rights
220
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
221
+ copies of the Software, and to permit persons to whom the Software is
222
+ furnished to do so, subject to the following conditions:
223
+
224
+ The above copyright notice and this permission notice shall be included in all
225
+ copies or substantial portions of the Software.
226
+
227
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
228
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
229
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
230
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
231
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
232
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
233
+ SOFTWARE.
234
+
235
+
221
236
  ===
222
237
 
223
238
  # @juggle/resize-observer@3.4.0 (Apache-2.0)
@@ -427,29 +442,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
427
442
 
428
443
  ===
429
444
 
430
- # prop-types@15.8.1 (MIT)
445
+ # dom-serializer@2.0.0 (MIT)
431
446
 
432
- MIT License
447
+ License
433
448
 
434
- Copyright (c) 2013-present, Facebook, Inc.
449
+ (The MIT License)
435
450
 
436
- Permission is hereby granted, free of charge, to any person obtaining a copy
437
- of this software and associated documentation files (the "Software"), to deal
438
- in the Software without restriction, including without limitation the rights
439
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
440
- copies of the Software, and to permit persons to whom the Software is
441
- furnished to do so, subject to the following conditions:
451
+ Copyright (c) 2014 The cheeriojs contributors
442
452
 
443
- The above copyright notice and this permission notice shall be included in all
444
- copies or substantial portions of the Software.
453
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
445
454
 
446
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
447
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
448
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
449
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
450
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
451
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
452
- SOFTWARE.
455
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
456
+
457
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
453
458
 
454
459
 
455
460
  ===
@@ -481,20 +486,15 @@ SOFTWARE.
481
486
 
482
487
  ===
483
488
 
484
- # dom-serializer@2.0.0 (MIT)
485
-
486
- License
487
-
488
- (The MIT License)
489
-
490
- Copyright (c) 2014 The cheeriojs contributors
489
+ # @headless-tree/core@0.0.15 (MIT)
491
490
 
492
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
491
+ MIT
493
492
 
494
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
493
+ ===
495
494
 
496
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
495
+ # @headless-tree/react@0.0.15 (MIT)
497
496
 
497
+ MIT
498
498
 
499
499
  ===
500
500
 
@@ -553,7 +553,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
553
553
 
554
554
  ===
555
555
 
556
- # @react-aria/focus@3.19.1 (Apache-2.0)
556
+ # @react-aria/interactions@3.23.0 (Apache-2.0)
557
557
 
558
558
  Apache License
559
559
  Version 2.0, January 2004
@@ -760,7 +760,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
760
760
 
761
761
  ===
762
762
 
763
- # @react-aria/interactions@3.23.0 (Apache-2.0)
763
+ # @react-aria/focus@3.19.1 (Apache-2.0)
764
764
 
765
765
  Apache License
766
766
  Version 2.0, January 2004
@@ -1585,33 +1585,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1585
1585
  See the License for the specific language governing permissions and
1586
1586
  limitations under the License.
1587
1587
 
1588
- ===
1589
-
1590
- # @tanstack/virtual-core@3.10.6 (MIT)
1591
-
1592
- MIT License
1593
-
1594
- Copyright (c) 2021-present Tanner Linsley
1595
-
1596
- Permission is hereby granted, free of charge, to any person obtaining a copy
1597
- of this software and associated documentation files (the "Software"), to deal
1598
- in the Software without restriction, including without limitation the rights
1599
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1600
- copies of the Software, and to permit persons to whom the Software is
1601
- furnished to do so, subject to the following conditions:
1602
-
1603
- The above copyright notice and this permission notice shall be included in all
1604
- copies or substantial portions of the Software.
1605
-
1606
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1607
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1608
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1609
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1610
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1611
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1612
- SOFTWARE.
1613
-
1614
-
1615
1588
  ===
1616
1589
 
1617
1590
  # @react-stately/utils@3.10.5 (Apache-2.0)
@@ -1817,3 +1790,30 @@ SOFTWARE.
1817
1790
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1818
1791
  See the License for the specific language governing permissions and
1819
1792
  limitations under the License.
1793
+
1794
+
1795
+ ===
1796
+
1797
+ # @tanstack/virtual-core@3.10.6 (MIT)
1798
+
1799
+ MIT License
1800
+
1801
+ Copyright (c) 2021-present Tanner Linsley
1802
+
1803
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1804
+ of this software and associated documentation files (the "Software"), to deal
1805
+ in the Software without restriction, including without limitation the rights
1806
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1807
+ copies of the Software, and to permit persons to whom the Software is
1808
+ furnished to do so, subject to the following conditions:
1809
+
1810
+ The above copyright notice and this permission notice shall be included in all
1811
+ copies or substantial portions of the Software.
1812
+
1813
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1814
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1815
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1816
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1817
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1818
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1819
+ SOFTWARE.
@@ -3,7 +3,6 @@ import type { EntryRecord } from 'alinea/core/EntryRecord';
3
3
  import type { CommitRequest } from 'alinea/core/db/CommitRequest';
4
4
  import { GithubSource, type GithubSourceOptions } from 'alinea/core/source/GithubSource';
5
5
  export interface GithubOptions extends GithubSourceOptions {
6
- rootDir: string;
7
6
  author?: {
8
7
  name: string;
9
8
  email: string;
@@ -1,12 +1,14 @@
1
1
  import "../../chunks/chunk-NZLE2WMY.js";
2
2
 
3
3
  // src/backend/api/GithubApi.ts
4
+ import { parseCoAuthoredBy } from "alinea/cli/util/CommitMessage";
4
5
  import { HttpError } from "alinea/core/HttpError";
5
6
  import {
6
7
  GithubSource
7
8
  } from "alinea/core/source/GithubSource";
8
9
  import { ShaMismatchError } from "alinea/core/source/ShaMismatchError";
9
10
  import { base64, btoa } from "alinea/core/util/Encoding";
11
+ import { fileVersions } from "alinea/core/util/EntryFilenames";
10
12
  import { join } from "alinea/core/util/Paths";
11
13
  var GithubApi = class extends GithubSource {
12
14
  #options;
@@ -15,7 +17,7 @@ var GithubApi = class extends GithubSource {
15
17
  this.#options = options;
16
18
  }
17
19
  async write(request) {
18
- const currentCommit = await getLatestCommitOid(this.#options);
20
+ const currentCommit = await this.#getLatestCommitOid();
19
21
  const currentSha = await this.shaAt(currentCommit);
20
22
  if (currentSha !== request.fromSha)
21
23
  throw new ShaMismatchError(currentSha, request.fromSha);
@@ -26,8 +28,7 @@ var GithubApi = class extends GithubSource {
26
28
 
27
29
  Co-authored-by: ${author.name} <${author.email}>`;
28
30
  }
29
- const newCommit = await applyChangesToRepo(
30
- this.#options,
31
+ const newCommit = await this.#applyChangesToRepo(
31
32
  currentCommit,
32
33
  request.changes,
33
34
  commitMessage
@@ -35,79 +36,108 @@ Co-authored-by: ${author.name} <${author.email}>`;
35
36
  return { sha: await this.shaAt(newCommit) };
36
37
  }
37
38
  async revisions(file) {
38
- return getFileCommitHistory(this.#options, file);
39
+ return this.#getFileCommitHistory(file);
39
40
  }
40
41
  async revisionData(file, revisionId) {
41
- const content = await getFileContentAtCommit(
42
- this.#options,
43
- file,
44
- revisionId
45
- );
42
+ const content = await this.#getFileContentAtCommit(file, revisionId);
46
43
  try {
47
44
  return content ? JSON.parse(content) : void 0;
48
45
  } catch (error) {
49
46
  return void 0;
50
47
  }
51
48
  }
52
- };
53
- function graphQL(query, variables, token) {
54
- return fetch("https://api.github.com/graphql", {
55
- method: "POST",
56
- headers: {
57
- Authorization: `Bearer ${token}`,
58
- "Content-Type": "application/json"
59
- },
60
- body: JSON.stringify({ query, variables })
61
- }).then(async (response) => {
62
- if (response.ok) return response.json();
63
- throw new HttpError(response.status, await response.text());
64
- }).then((result) => {
65
- if (Array.isArray(result.errors) && result.errors.length > 0) {
66
- const message = result.errors.map((e) => e.message).join("; ");
67
- console.trace(result.errors);
68
- throw new Error(message);
69
- }
70
- return result;
71
- });
72
- }
73
- async function getFileCommitHistory({ owner, repo, branch, authToken, rootDir }, file) {
74
- const result = await graphQL(
75
- `query GetFileHistory($owner: String!, $repo: String!, $branch: String!, $path: String!) {
76
- repository(owner: $owner, name: $repo) {
77
- ref(qualifiedName: $branch) {
78
- target {
79
- ... on Commit {
80
- history(path: $path, first: 100) {
81
- nodes {
82
- oid
83
- committedDate
84
- message
85
- author {
86
- name
87
- email
88
- }
89
- }
90
- }
49
+ async #graphQL(query, variables, token) {
50
+ return fetch("https://api.github.com/graphql", {
51
+ method: "POST",
52
+ headers: {
53
+ Authorization: `Bearer ${token}`,
54
+ "Content-Type": "application/json"
55
+ },
56
+ body: JSON.stringify({ query, variables })
57
+ }).then(async (response) => {
58
+ if (response.ok) return response.json();
59
+ throw new HttpError(response.status, await response.text());
60
+ }).then((result) => {
61
+ if (Array.isArray(result.errors) && result.errors.length > 0) {
62
+ const message = result.errors.map((e) => e.message).join("; ");
63
+ console.trace(result.errors);
64
+ throw new Error(message);
65
+ }
66
+ return result;
67
+ });
68
+ }
69
+ async #getFileCommitHistory(file) {
70
+ const { owner, repo, branch, authToken, rootDir } = this.#options;
71
+ const seen = /* @__PURE__ */ new Set();
72
+ const queue = fileVersions(file);
73
+ const allRevisions = Array();
74
+ const maxRequests = 3;
75
+ let requestCount = 0;
76
+ while (queue.length) {
77
+ if (requestCount === maxRequests) break;
78
+ requestCount++;
79
+ const versions = [...queue].filter((v) => !seen.has(v));
80
+ for (const v of versions) seen.add(v);
81
+ queue.length = 0;
82
+ const aliasMap = versions.map((v, idx) => ({
83
+ alias: `file${idx}`,
84
+ version: v,
85
+ path: join(rootDir, v)
86
+ }));
87
+ const query = aliasMap.map(
88
+ ({ alias, path }) => `
89
+ ${alias}: history(path: "${path}", first: 100) {
90
+ nodes {
91
+ oid
92
+ committedDate
93
+ message
94
+ author { name email }
95
+ }
96
+ }`
97
+ ).join("");
98
+ const gql = `
99
+ query GetFileHistory($owner: String!, $repo: String!, $branch: String!) {
100
+ repository(owner: $owner, name: $repo) {
101
+ ref(qualifiedName: $branch) {
102
+ target { ... on Commit {${query}} }
91
103
  }
92
104
  }
105
+ }`;
106
+ const result = await this.#graphQL(gql, { owner, repo, branch }, authToken);
107
+ for (const { alias, version, path } of aliasMap) {
108
+ const commits = result.data.repository.ref.target[alias]?.nodes || [];
109
+ if (!commits.length) continue;
110
+ allRevisions.push(
111
+ ...commits.map((commit) => ({
112
+ ref: commit.oid,
113
+ createdAt: new Date(commit.committedDate).getTime(),
114
+ file: version,
115
+ user: parseCoAuthoredBy(commit.message) ?? (commit.author ? { name: commit.author.name, email: commit.author.email } : void 0),
116
+ description: commit.message
117
+ }))
118
+ );
119
+ const earliest = commits[commits.length - 1].oid;
120
+ const res = await fetch(
121
+ `https://api.github.com/repos/${owner}/${repo}/commits/${earliest}`,
122
+ { headers: { Authorization: `Bearer ${authToken}` } }
123
+ );
124
+ if (!res.ok) throw new HttpError(res.status, await res.text());
125
+ const commitData = await res.json();
126
+ const fileEntry = Array.isArray(commitData.files) ? commitData.files.find((f) => f.filename === path) : void 0;
127
+ const prev = fileEntry?.previous_filename;
128
+ if (prev) {
129
+ const prefix = rootDir ? `${rootDir}/` : "";
130
+ const relative = prev.startsWith(prefix) ? prev.slice(prefix.length) : prev;
131
+ queue.push(...fileVersions(relative));
93
132
  }
94
133
  }
95
- }`,
96
- { owner, repo, branch, path: join(rootDir, file) },
97
- authToken
98
- );
99
- const commits = result.data.repository.ref.target.history.nodes;
100
- return commits.map((commit) => ({
101
- ref: commit.oid,
102
- createdAt: new Date(commit.committedDate).getTime(),
103
- file,
104
- user: commit.author ? { name: commit.author.name, email: commit.author.email } : void 0,
105
- description: commit.message
106
- }));
107
- }
108
- async function getFileContentAtCommit({ owner, repo, authToken, rootDir }, file, ref) {
109
- const result = await graphQL(
110
- `query GetFileContent($owner: String!, $repo: String!, $expression: String!) {
134
+ }
135
+ return allRevisions.sort((a, b) => b.createdAt - a.createdAt);
136
+ }
137
+ async #getFileContentAtCommit(file, ref) {
138
+ const { owner, repo, authToken, rootDir } = this.#options;
139
+ const result = await this.#graphQL(
140
+ `query GetFileContent($owner: String!, $repo: String!, $expression: String!) {
111
141
  repository(owner: $owner, name: $repo) {
112
142
  object(expression: $expression) {
113
143
  ... on Blob {
@@ -116,97 +146,94 @@ async function getFileContentAtCommit({ owner, repo, authToken, rootDir }, file,
116
146
  }
117
147
  }
118
148
  }`,
119
- { owner, repo, expression: `${ref}:${join(rootDir, file)}` },
120
- authToken
121
- );
122
- return result.data.repository.object?.text;
123
- }
124
- async function applyChangesToRepo(options, expectedHeadOid, changes, commitMessage) {
125
- const { additions, deletions } = await processChanges(options, changes);
126
- const { owner, repo, branch, authToken } = options;
127
- return graphQL(
128
- `mutation CreateCommitOnBranch($input: CreateCommitOnBranchInput!) {
149
+ { owner, repo, expression: `${ref}:${join(rootDir, file)}` },
150
+ authToken
151
+ );
152
+ return result.data.repository.object?.text;
153
+ }
154
+ async #applyChangesToRepo(expectedHeadOid, changes, commitMessage) {
155
+ const { additions, deletions } = await this.#processChanges(changes);
156
+ const { owner, repo, branch, authToken } = this.#options;
157
+ return this.#graphQL(
158
+ `mutation CreateCommitOnBranch($input: CreateCommitOnBranchInput!) {
129
159
  createCommitOnBranch(input: $input) {
130
160
  commit {
131
161
  oid
132
162
  }
133
163
  }
134
164
  }`,
135
- {
136
- input: {
137
- branch: {
138
- repositoryNameWithOwner: `${owner}/${repo}`,
139
- branchName: branch
140
- },
141
- message: { headline: commitMessage },
142
- fileChanges: { additions, deletions },
143
- expectedHeadOid
144
- }
145
- },
146
- authToken
147
- ).then((result) => {
148
- const commitId = result.data.createCommitOnBranch.commit.oid;
149
- return commitId;
150
- }).catch((error) => {
151
- if (error instanceof Error) {
152
- const mismatchMessage = /is at ([a-z0-9]+) but expected ([a-z0-9]+)/;
153
- const match = error.message.match(mismatchMessage);
154
- if (match) {
155
- const [_, actual, expected] = match;
156
- throw new ShaMismatchError(actual, expected);
157
- }
158
- const expectedMessage = /Expected branch to point to "([a-z0-9]+)"/;
159
- const expectedMatch = error.message.match(expectedMessage);
160
- if (expectedMatch) {
161
- const actualSha = expectedMatch[1];
162
- throw new ShaMismatchError(actualSha, expectedHeadOid);
163
- }
164
- }
165
- throw error;
166
- });
167
- }
168
- async function processChanges({ rootDir, contentDir }, changes) {
169
- const additions = Array();
170
- const deletions = Array();
171
- for (const change of changes) {
172
- switch (change.op) {
173
- case "addContent": {
174
- additions.push({
175
- path: join(contentDir, change.path),
176
- contents: btoa(change.contents)
177
- });
178
- break;
179
- }
180
- case "uploadFile": {
181
- const file = join(rootDir, change.location);
182
- additions.push({
183
- path: file,
184
- contents: await fetchUploadedContent(change.url)
185
- });
186
- break;
187
- }
188
- case "deleteContent": {
189
- const file = join(contentDir, change.path);
190
- deletions.push({ path: file });
191
- break;
165
+ {
166
+ input: {
167
+ branch: {
168
+ repositoryNameWithOwner: `${owner}/${repo}`,
169
+ branchName: branch
170
+ },
171
+ message: { headline: commitMessage },
172
+ fileChanges: { additions, deletions },
173
+ expectedHeadOid
174
+ }
175
+ },
176
+ authToken
177
+ ).then((result) => {
178
+ const commitId = result.data.createCommitOnBranch.commit.oid;
179
+ return commitId;
180
+ }).catch((error) => {
181
+ if (error instanceof Error) {
182
+ const mismatchMessage = /is at ([a-z0-9]+) but expected ([a-z0-9]+)/;
183
+ const match = error.message.match(mismatchMessage);
184
+ if (match) {
185
+ const [_, actual, expected] = match;
186
+ throw new ShaMismatchError(actual, expected);
187
+ }
188
+ const expectedMessage = /Expected branch to point to "([a-z0-9]+)"/;
189
+ const expectedMatch = error.message.match(expectedMessage);
190
+ if (expectedMatch) {
191
+ const actualSha = expectedMatch[1];
192
+ throw new ShaMismatchError(actualSha, expectedHeadOid);
193
+ }
192
194
  }
193
- case "removeFile": {
194
- const file = join(rootDir, change.location);
195
- deletions.push({ path: file });
196
- break;
195
+ throw error;
196
+ });
197
+ }
198
+ async #processChanges(changes) {
199
+ const { rootDir } = this.#options;
200
+ const additions = Array();
201
+ const deletions = Array();
202
+ for (const change of changes) {
203
+ switch (change.op) {
204
+ case "addContent": {
205
+ additions.push({
206
+ path: join(this.contentLocation, change.path),
207
+ contents: btoa(change.contents)
208
+ });
209
+ break;
210
+ }
211
+ case "uploadFile": {
212
+ const file = join(rootDir, change.location);
213
+ additions.push({
214
+ path: file,
215
+ contents: await this.#fetchUploadedContent(change.url)
216
+ });
217
+ break;
218
+ }
219
+ case "deleteContent": {
220
+ const file = join(this.contentLocation, change.path);
221
+ deletions.push({ path: file });
222
+ break;
223
+ }
224
+ case "removeFile": {
225
+ const file = join(rootDir, change.location);
226
+ deletions.push({ path: file });
227
+ break;
228
+ }
197
229
  }
198
230
  }
231
+ return { additions, deletions };
199
232
  }
200
- return { additions, deletions };
201
- }
202
- async function getLatestCommitOid({
203
- owner,
204
- repo,
205
- branch,
206
- authToken
207
- }) {
208
- return graphQL(
209
- `query GetLatestCommit($owner: String!, $repo: String!, $branch: String!) {
233
+ async #getLatestCommitOid() {
234
+ const { owner, repo, branch, authToken } = this.#options;
235
+ return this.#graphQL(
236
+ `query GetLatestCommit($owner: String!, $repo: String!, $branch: String!) {
210
237
  repository(owner: $owner, name: $repo) {
211
238
  ref(qualifiedName: $branch) {
212
239
  target {
@@ -215,14 +242,15 @@ async function getLatestCommitOid({
215
242
  }
216
243
  }
217
244
  }`,
218
- { owner, repo, branch },
219
- authToken
220
- ).then((result) => result.data.repository.ref.target.oid);
221
- }
222
- async function fetchUploadedContent(url) {
223
- const response = await fetch(url);
224
- return base64.stringify(new Uint8Array(await response.arrayBuffer()));
225
- }
245
+ { owner, repo, branch },
246
+ authToken
247
+ ).then((result) => result.data.repository.ref.target.oid);
248
+ }
249
+ async #fetchUploadedContent(url) {
250
+ const response = await fetch(url);
251
+ return base64.stringify(new Uint8Array(await response.arrayBuffer()));
252
+ }
253
+ };
226
254
  export {
227
255
  GithubApi
228
256
  };
@@ -2,7 +2,7 @@
2
2
  var package_default = {
3
3
  bin: "./dist/cli.js",
4
4
  name: "alinea",
5
- version: "1.4.0-preview.1",
5
+ version: "1.4.0",
6
6
  description: "Headless git-based CMS",
7
7
  repository: {
8
8
  type: "git",
package/dist/cli/Serve.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "../chunks/chunk-LDLSN5JK.js";
3
+ } from "../chunks/chunk-OO7VJEA4.js";
4
4
  import "../chunks/chunk-NZLE2WMY.js";
5
5
 
6
6
  // src/cli/Serve.ts
package/dist/cli/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "../chunks/chunk-LDLSN5JK.js";
3
+ } from "../chunks/chunk-OO7VJEA4.js";
4
4
  import "../chunks/chunk-NZLE2WMY.js";
5
5
 
6
6
  // node_modules/mri/lib/index.mjs
@@ -3,6 +3,8 @@ import "../../chunks/chunk-NZLE2WMY.js";
3
3
  // src/cli/serve/GitHistory.ts
4
4
  import { JsonLoader } from "alinea/backend/loader/JsonLoader";
5
5
  import { execGit } from "alinea/backend/util/ExecGit";
6
+ import { fileVersions } from "alinea/core/util/EntryFilenames";
7
+ import { parseCoAuthoredBy } from "../util/CommitMessage.js";
6
8
  var encoder = new TextEncoder();
7
9
  var GitHistory = class {
8
10
  constructor(config, rootDir) {
@@ -10,34 +12,34 @@ var GitHistory = class {
10
12
  this.rootDir = rootDir;
11
13
  }
12
14
  async revisions(file) {
13
- const output = await execGit(this.rootDir, [
14
- "log",
15
- "--follow",
16
- "--name-status",
17
- "--pretty=format:%H%n%at%n%s%n%ae%n%an%n%f",
18
- "--",
19
- file
20
- ]);
21
- const revisions = output.split("\n\n").filter((entry) => {
22
- return entry.includes("\n");
23
- }).map((entry) => {
24
- const [hash, timestamp, message, email, name, changedFile, ...rest] = entry.split("\n");
25
- const fileLocation = rest.length ? rest[rest.length - 1].split(" ").pop().trim() : file;
26
- const user = { name, email };
27
- if (message?.includes("Co-authored-by")) {
28
- const coAuthors = message.split("Co-authored-by:").slice(1).map((line) => line.split("<").pop().split(">")[0]);
29
- user.email = coAuthors[0] || email;
30
- user.name = coAuthors[1] || name;
31
- }
32
- return {
33
- ref: hash,
34
- createdAt: Number.parseInt(timestamp) * 1e3,
35
- description: message,
36
- file: fileLocation,
37
- user
38
- };
39
- });
40
- return revisions;
15
+ const versions = fileVersions(file);
16
+ const results = Array();
17
+ for (const versioned of versions) {
18
+ const output = await execGit(this.rootDir, [
19
+ "log",
20
+ "--follow",
21
+ "--name-status",
22
+ "--pretty=format:%H%n%at%n%s%n%ae%n%an%n%f",
23
+ "--",
24
+ versioned
25
+ ]);
26
+ const revisions = output.split("\n\n").filter((entry) => {
27
+ return entry.includes("\n");
28
+ }).map((entry) => {
29
+ const [ref, timestamp, message, email, name, changedFile, ...rest] = entry.split("\n");
30
+ const fileLocation = rest.length ? rest[rest.length - 1].split(" ").pop().trim() : versioned;
31
+ const user = parseCoAuthoredBy(message) ?? { name, email };
32
+ return {
33
+ ref,
34
+ createdAt: Number.parseInt(timestamp) * 1e3,
35
+ description: message,
36
+ file: fileLocation,
37
+ user
38
+ };
39
+ });
40
+ results.push(...revisions);
41
+ }
42
+ return results;
41
43
  }
42
44
  async revisionData(file, ref) {
43
45
  const { config } = this;
@@ -0,0 +1,4 @@
1
+ export declare function parseCoAuthoredBy(commitMessage: string): {
2
+ email: string;
3
+ name: string;
4
+ } | undefined;
@@ -0,0 +1,16 @@
1
+ import "../../chunks/chunk-NZLE2WMY.js";
2
+
3
+ // src/cli/util/CommitMessage.ts
4
+ function parseCoAuthoredBy(commitMessage) {
5
+ const regex = /Co-authored-by:\s*(.+?)\s*<(\S+@\S+\.\S+)>/;
6
+ const match = commitMessage.match(regex);
7
+ if (match && match.length >= 3) {
8
+ const name = match[1].trim();
9
+ const email = match[2].trim();
10
+ return { name, email };
11
+ }
12
+ return void 0;
13
+ }
14
+ export {
15
+ parseCoAuthoredBy
16
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "../chunks/chunk-LDLSN5JK.js";
3
+ } from "../chunks/chunk-OO7VJEA4.js";
4
4
  import {
5
5
  PLazy
6
6
  } from "../chunks/chunk-IKINPSS5.js";
@@ -6,11 +6,13 @@ export interface GithubSourceOptions {
6
6
  owner: string;
7
7
  repo: string;
8
8
  branch: string;
9
+ rootDir: string;
9
10
  contentDir: string;
10
11
  }
11
12
  export declare class GithubSource implements Source {
12
13
  #private;
13
14
  constructor(options: GithubSourceOptions);
15
+ protected get contentLocation(): string;
14
16
  getTree(): Promise<ReadonlyTree>;
15
17
  shaAt(ref: string): Promise<string>;
16
18
  getTreeIfDifferent(sha: string): Promise<ReadonlyTree | undefined>;
@@ -14,6 +14,12 @@ var GithubSource = class {
14
14
  constructor(options) {
15
15
  this.#options = options;
16
16
  }
17
+ get contentLocation() {
18
+ const { contentDir, rootDir } = this.#options;
19
+ if (contentDir.startsWith("/")) return contentDir.slice(1);
20
+ if (rootDir.endsWith("/")) return rootDir + contentDir;
21
+ return `${rootDir}/${contentDir}`;
22
+ }
17
23
  async getTree() {
18
24
  const current = this.#current;
19
25
  const newTree = await this.getTreeIfDifferent(current.sha);
@@ -21,8 +27,8 @@ var GithubSource = class {
21
27
  return current;
22
28
  }
23
29
  async shaAt(ref) {
24
- const { contentDir, owner, repo, authToken } = this.#options;
25
- const parentDir = contentDir.split("/").slice(0, -1).join("/");
30
+ const { owner, repo, authToken } = this.#options;
31
+ const parentDir = this.contentLocation.split("/").slice(0, -1).join("/");
26
32
  const parentInfo = await fetch(
27
33
  `https://api.github.com/repos/${owner}/${repo}/contents/${parentDir}?ref=${ref}`,
28
34
  { headers: { Authorization: `Bearer ${authToken}` } }
@@ -30,7 +36,7 @@ var GithubSource = class {
30
36
  assert(parentInfo.ok, `Failed to get parent: ${parentInfo.statusText}`);
31
37
  const parents = await parentInfo.json();
32
38
  assert(Array.isArray(parents));
33
- const parent = parents.find((entry) => entry.path === contentDir);
39
+ const parent = parents.find((entry) => entry.path === this.contentLocation);
34
40
  if (!parent) return ReadonlyTree.EMPTY.sha;
35
41
  assert(typeof parent.sha === "string");
36
42
  return parent.sha;
@@ -29,3 +29,4 @@ export declare function entryFile(config: Config, entry: EntryRow): string;
29
29
  export declare function entryUrl(type: Type, meta: EntryUrlMeta): string;
30
30
  export declare function pathSuffix(path: string, conflictingPaths: Array<string>): number | undefined;
31
31
  export declare function applySuffix(path: string, suffix: number): string;
32
+ export declare function fileVersions(file: string): string[];
@@ -1,6 +1,7 @@
1
1
  import "../../chunks/chunk-NZLE2WMY.js";
2
2
 
3
3
  // src/core/util/EntryFilenames.ts
4
+ import * as paths from "alinea/core/util/Paths";
4
5
  import { entryStatuses } from "../Entry.js";
5
6
  import { ALT_STATUS } from "../EntryRow.js";
6
7
  import { getRoot, getType } from "../Internal.js";
@@ -74,6 +75,16 @@ function pathSuffix(path, conflictingPaths) {
74
75
  function applySuffix(path, suffix) {
75
76
  return `${path}-${suffix}`;
76
77
  }
78
+ function fileVersions(file) {
79
+ const dir = paths.dirname(file);
80
+ const base = paths.basename(file, ".json");
81
+ const [name] = entryInfo(base);
82
+ return [
83
+ `${dir}/${name}.json`,
84
+ `${dir}/${name}.draft.json`,
85
+ `${dir}/${name}.archived.json`
86
+ ];
87
+ }
77
88
  export {
78
89
  applySuffix,
79
90
  entryChildrenDir,
@@ -82,6 +93,7 @@ export {
82
93
  entryFilepath,
83
94
  entryInfo,
84
95
  entryUrl,
96
+ fileVersions,
85
97
  pathSuffix,
86
98
  workspaceMediaDir
87
99
  };
@@ -1,9 +1,9 @@
1
- import "../../chunks/chunk-WDCPVJJC.js";
2
1
  import {
3
2
  useAtomValue,
4
3
  useSetAtom,
5
4
  useStore
6
5
  } from "../../chunks/chunk-TOJF2G3X.js";
6
+ import "../../chunks/chunk-WDCPVJJC.js";
7
7
  import {
8
8
  atom
9
9
  } from "../../chunks/chunk-WJ67RR7S.js";
@@ -1,9 +1,9 @@
1
- import {
2
- atomFamily
3
- } from "../../chunks/chunk-WDCPVJJC.js";
4
1
  import {
5
2
  useAtomValue
6
3
  } from "../../chunks/chunk-TOJF2G3X.js";
4
+ import {
5
+ atomFamily
6
+ } from "../../chunks/chunk-WDCPVJJC.js";
7
7
  import {
8
8
  atom
9
9
  } from "../../chunks/chunk-WJ67RR7S.js";
@@ -1,9 +1,9 @@
1
- import {
2
- atomFamily
3
- } from "../../chunks/chunk-WDCPVJJC.js";
4
1
  import {
5
2
  require_dataloader
6
3
  } from "../../chunks/chunk-3TNMMA3W.js";
4
+ import {
5
+ atomFamily
6
+ } from "../../chunks/chunk-WDCPVJJC.js";
7
7
  import {
8
8
  atom
9
9
  } from "../../chunks/chunk-WJ67RR7S.js";
@@ -1,11 +1,11 @@
1
- import {
2
- atomFamily
3
- } from "../../chunks/chunk-WDCPVJJC.js";
4
1
  import {
5
2
  useAtom,
6
3
  useAtomValue,
7
4
  useSetAtom
8
5
  } from "../../chunks/chunk-TOJF2G3X.js";
6
+ import {
7
+ atomFamily
8
+ } from "../../chunks/chunk-WDCPVJJC.js";
9
9
  import {
10
10
  atom
11
11
  } from "../../chunks/chunk-WJ67RR7S.js";
@@ -1,9 +1,9 @@
1
- import {
2
- atomWithStorage
3
- } from "../../chunks/chunk-WDCPVJJC.js";
4
1
  import {
5
2
  useAtom
6
3
  } from "../../chunks/chunk-TOJF2G3X.js";
4
+ import {
5
+ atomWithStorage
6
+ } from "../../chunks/chunk-WDCPVJJC.js";
7
7
  import {
8
8
  atom
9
9
  } from "../../chunks/chunk-WJ67RR7S.js";
@@ -1,9 +1,9 @@
1
- import {
2
- unwrap
3
- } from "../../../chunks/chunk-WDCPVJJC.js";
4
1
  import {
5
2
  useAtomValue
6
3
  } from "../../../chunks/chunk-TOJF2G3X.js";
4
+ import {
5
+ unwrap
6
+ } from "../../../chunks/chunk-WDCPVJJC.js";
7
7
  import "../../../chunks/chunk-WJ67RR7S.js";
8
8
  import "../../../chunks/chunk-NZLE2WMY.js";
9
9
 
@@ -18,12 +18,12 @@ import {
18
18
  import {
19
19
  dist_default
20
20
  } from "../../chunks/chunk-A5O3N2GS.js";
21
- import {
22
- atomWithStorage
23
- } from "../../chunks/chunk-WDCPVJJC.js";
24
21
  import {
25
22
  useAtom
26
23
  } from "../../chunks/chunk-TOJF2G3X.js";
24
+ import {
25
+ atomWithStorage
26
+ } from "../../chunks/chunk-WDCPVJJC.js";
27
27
  import "../../chunks/chunk-WJ67RR7S.js";
28
28
  import "../../chunks/chunk-NZLE2WMY.js";
29
29
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bin": "./dist/cli.js",
3
3
  "name": "alinea",
4
- "version": "1.4.0-preview.1",
4
+ "version": "1.4.0",
5
5
  "description": "Headless git-based CMS",
6
6
  "repository": {
7
7
  "type": "git",