alinea 1.4.0-preview.0 → 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": "2zEEsGEasvfbFJSwme8hbd6n5z2",
2
+ "_id": "2zMfvzyGJk5gAIRxlYhGAkY87JU",
3
3
  "_type": "Page",
4
4
  "_index": "a0",
5
5
  "_seeded": "welcome.json",
package/dist/LICENSES.md CHANGED
@@ -81,29 +81,6 @@ SOFTWARE.
81
81
 
82
82
  ===
83
83
 
84
- # htmlparser2@9.1.0 (MIT)
85
-
86
- Copyright 2010, 2011, Chris Winberry <chris@winberry.net>. All rights reserved.
87
- Permission is hereby granted, free of charge, to any person obtaining a copy
88
- of this software and associated documentation files (the "Software"), to
89
- deal in the Software without restriction, including without limitation the
90
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
91
- sell copies of the Software, and to permit persons to whom the Software is
92
- furnished to do so, subject to the following conditions:
93
-
94
- The above copyright notice and this permission notice shall be included in
95
- all copies or substantial portions of the Software.
96
-
97
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
98
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
99
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
100
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
101
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
102
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
103
- IN THE SOFTWARE.
104
-
105
- ===
106
-
107
84
  # react-query@3.39.3 (MIT)
108
85
 
109
86
  MIT License
@@ -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.
@@ -440,6 +440,23 @@ SOFTWARE.
440
440
  limitations under the License.
441
441
 
442
442
 
443
+ ===
444
+
445
+ # dom-serializer@2.0.0 (MIT)
446
+
447
+ License
448
+
449
+ (The MIT License)
450
+
451
+ Copyright (c) 2014 The cheeriojs contributors
452
+
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:
454
+
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.
458
+
459
+
443
460
  ===
444
461
 
445
462
  # react-is@16.13.1 (MIT)
@@ -467,23 +484,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
467
484
  SOFTWARE.
468
485
 
469
486
 
470
- ===
471
-
472
- # dom-serializer@2.0.0 (MIT)
473
-
474
- License
475
-
476
- (The MIT License)
477
-
478
- Copyright (c) 2014 The cheeriojs contributors
479
-
480
- 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:
481
-
482
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
483
-
484
- 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.
485
-
486
-
487
487
  ===
488
488
 
489
489
  # @headless-tree/core@0.0.15 (MIT)
@@ -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.0",
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-LIA6NPBB.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-LIA6NPBB.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-LIA6NPBB.js";
3
+ } from "../chunks/chunk-OO7VJEA4.js";
4
4
  import {
5
5
  PLazy
6
6
  } from "../chunks/chunk-IKINPSS5.js";
@@ -273,20 +273,23 @@ var EntryTransaction = class {
273
273
  this.#tx.remove(entry.filePath);
274
274
  const record = createRecord({ ...entry, path }, "published");
275
275
  const contents = new TextEncoder().encode(JSON.stringify(record, null, 2));
276
- this.#tx.add(`${childrenDir}.json`, contents);
277
276
  if (pathChange) {
278
277
  this.#tx.remove(`${entry.parentDir}/${entry.path}.json`);
279
278
  this.#tx.rename(entry.childrenDir, childrenDir);
280
279
  }
280
+ this.#tx.add(`${childrenDir}.json`, contents);
281
281
  this.#messages.push(this.#reportOp("publish", entry.title));
282
282
  return this;
283
283
  }
284
284
  unpublish({ id, locale }) {
285
285
  const index = this.#index;
286
- const entry = index.findFirst((entry2) => {
287
- return entry2.id === id && entry2.locale === locale && entry2.status === "published";
288
- });
286
+ const versions = index.byId.get(id)?.locales.get(locale);
287
+ const entry = versions?.main;
289
288
  assert(entry, `Entry not found: ${id}`);
289
+ for (const version of versions.values()) {
290
+ if (version.main) continue;
291
+ this.#tx.remove(version.filePath);
292
+ }
290
293
  this.#checks.push([entry.filePath, entry.fileHash]);
291
294
  this.#tx.rename(entry.filePath, `${entry.childrenDir}.draft.json`);
292
295
  this.#messages.push(this.#reportOp("unpublish", entry.title));
@@ -294,10 +297,13 @@ var EntryTransaction = class {
294
297
  }
295
298
  archive({ id, locale }) {
296
299
  const index = this.#index;
297
- const entry = index.findFirst((entry2) => {
298
- return entry2.id === id && entry2.locale === locale && entry2.status === "published";
299
- });
300
+ const versions = index.byId.get(id)?.locales.get(locale);
301
+ const entry = versions?.main;
300
302
  assert(entry, `Entry not found: ${id}`);
303
+ for (const version of versions.values()) {
304
+ if (version.main) continue;
305
+ this.#tx.remove(version.filePath);
306
+ }
301
307
  this.#checks.push([entry.filePath, entry.fileHash]);
302
308
  this.#tx.rename(entry.filePath, `${entry.childrenDir}.archived.json`);
303
309
  this.#messages.push(this.#reportOp("archive", entry.title));
@@ -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;
@@ -85,7 +85,8 @@ var TreeBase = class _TreeBase {
85
85
  }
86
86
  equals(other) {
87
87
  if (other instanceof ReadonlyTree) return this.sha === other.sha;
88
- if (this.sha === other.sha) return true;
88
+ const canCompare = this.sha && other.sha;
89
+ if (canCompare && this.sha === other.sha) return true;
89
90
  if (this.nodes.size !== other.nodes.size) return false;
90
91
  for (const [name, entry] of this.nodes) {
91
92
  const otherEntry = other.nodes.get(name);
@@ -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
  };
@@ -38,7 +38,7 @@ export declare const entryRoute: Route<{
38
38
  unPublish: import("jotai").WritableAtom<null, [], Promise<void>> & {
39
39
  init: null;
40
40
  };
41
- archivePublished: import("jotai").WritableAtom<null, [], Promise<void>> & {
41
+ archive: import("jotai").WritableAtom<null, [], Promise<void>> & {
42
42
  init: null;
43
43
  };
44
44
  publishArchived: import("jotai").WritableAtom<null, [], Promise<void>> & {
@@ -50,7 +50,7 @@ export declare const entryRoute: Route<{
50
50
  deleteMediaLibrary: import("jotai").WritableAtom<null, [], Promise<void>> & {
51
51
  init: null;
52
52
  };
53
- deleteArchived: import("jotai").WritableAtom<null, [], Promise<void>> & {
53
+ deleteEntry: import("jotai").WritableAtom<null, [], Promise<void>> & {
54
54
  init: null;
55
55
  };
56
56
  saveTranslation: import("jotai").WritableAtom<null, [locale: string], Promise<void>> & {
@@ -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";
@@ -28,7 +28,7 @@ export declare enum EntryTransition {
28
28
  ArchivePublished = 7,
29
29
  PublishArchived = 8,
30
30
  DeleteFile = 9,
31
- DeleteArchived = 10
31
+ DeleteEntry = 10
32
32
  }
33
33
  export declare const entryEditorAtoms: import("jotai/vanilla/utils/atomFamily.js").AtomFamily<EntryEditorParams, import("jotai").Atom<Promise<{
34
34
  transition: import("jotai").PrimitiveAtom<EntryTransition | undefined> & {
@@ -68,7 +68,7 @@ export declare const entryEditorAtoms: import("jotai/vanilla/utils/atomFamily.js
68
68
  unPublish: import("jotai").WritableAtom<null, [], Promise<void>> & {
69
69
  init: null;
70
70
  };
71
- archivePublished: import("jotai").WritableAtom<null, [], Promise<void>> & {
71
+ archive: import("jotai").WritableAtom<null, [], Promise<void>> & {
72
72
  init: null;
73
73
  };
74
74
  publishArchived: import("jotai").WritableAtom<null, [], Promise<void>> & {
@@ -80,7 +80,7 @@ export declare const entryEditorAtoms: import("jotai/vanilla/utils/atomFamily.js
80
80
  deleteMediaLibrary: import("jotai").WritableAtom<null, [], Promise<void>> & {
81
81
  init: null;
82
82
  };
83
- deleteArchived: import("jotai").WritableAtom<null, [], Promise<void>> & {
83
+ deleteEntry: import("jotai").WritableAtom<null, [], Promise<void>> & {
84
84
  init: null;
85
85
  };
86
86
  saveTranslation: import("jotai").WritableAtom<null, [locale: string], Promise<void>> & {
@@ -190,7 +190,7 @@ export declare function createEntryEditor(entryData: EntryData): {
190
190
  unPublish: import("jotai").WritableAtom<null, [], Promise<void>> & {
191
191
  init: null;
192
192
  };
193
- archivePublished: import("jotai").WritableAtom<null, [], Promise<void>> & {
193
+ archive: import("jotai").WritableAtom<null, [], Promise<void>> & {
194
194
  init: null;
195
195
  };
196
196
  publishArchived: import("jotai").WritableAtom<null, [], Promise<void>> & {
@@ -202,7 +202,7 @@ export declare function createEntryEditor(entryData: EntryData): {
202
202
  deleteMediaLibrary: import("jotai").WritableAtom<null, [], Promise<void>> & {
203
203
  init: null;
204
204
  };
205
- deleteArchived: import("jotai").WritableAtom<null, [], Promise<void>> & {
205
+ deleteEntry: import("jotai").WritableAtom<null, [], Promise<void>> & {
206
206
  init: null;
207
207
  };
208
208
  saveTranslation: import("jotai").WritableAtom<null, [locale: string], Promise<void>> & {
@@ -51,7 +51,7 @@ var EntryTransition = /* @__PURE__ */ ((EntryTransition2) => {
51
51
  EntryTransition2[EntryTransition2["ArchivePublished"] = 7] = "ArchivePublished";
52
52
  EntryTransition2[EntryTransition2["PublishArchived"] = 8] = "PublishArchived";
53
53
  EntryTransition2[EntryTransition2["DeleteFile"] = 9] = "DeleteFile";
54
- EntryTransition2[EntryTransition2["DeleteArchived"] = 10] = "DeleteArchived";
54
+ EntryTransition2[EntryTransition2["DeleteEntry"] = 10] = "DeleteEntry";
55
55
  return EntryTransition2;
56
56
  })(EntryTransition || {});
57
57
  var entryTransitionAtoms = atomFamily((id) => {
@@ -396,7 +396,7 @@ function createEntryEditor(entryData) {
396
396
  errorMessage: "Could not complete unpublish action, please try again later"
397
397
  });
398
398
  });
399
- const archivePublished = atom(null, async (get, set) => {
399
+ const archive = atom(null, async (get, set) => {
400
400
  const db = get(dbAtom);
401
401
  return set(action, {
402
402
  transition: 7 /* ArchivePublished */,
@@ -426,7 +426,7 @@ function createEntryEditor(entryData) {
426
426
  if (!result) return;
427
427
  const db = get(dbAtom);
428
428
  return set(action, {
429
- transition: 10 /* DeleteArchived */,
429
+ transition: 10 /* DeleteEntry */,
430
430
  result: db.remove(activeVersion.id),
431
431
  errorMessage: "Could not complete delete action, please try again later"
432
432
  });
@@ -441,10 +441,10 @@ function createEntryEditor(entryData) {
441
441
  errorMessage: "Could not complete delete action, please try again later"
442
442
  });
443
443
  });
444
- const deleteArchived = atom(null, async (get, set) => {
444
+ const deleteEntry = atom(null, async (get, set) => {
445
445
  const db = get(dbAtom);
446
446
  return set(action, {
447
- transition: 10 /* DeleteArchived */,
447
+ transition: 10 /* DeleteEntry */,
448
448
  result: db.remove(activeVersion.id),
449
449
  errorMessage: "Could not complete delete action, please try again later"
450
450
  });
@@ -581,11 +581,11 @@ function createEntryEditor(entryData) {
581
581
  publishDraft,
582
582
  discardDraft,
583
583
  unPublish,
584
- archivePublished,
584
+ archive,
585
585
  publishArchived,
586
586
  deleteFile,
587
587
  deleteMediaLibrary,
588
- deleteArchived,
588
+ deleteEntry,
589
589
  saveTranslation,
590
590
  discardEdits,
591
591
  showHistory,
@@ -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";
@@ -113,7 +113,7 @@ var transitions = {
113
113
  [EntryTransition.ArchivePublished]: "Archiving",
114
114
  [EntryTransition.PublishArchived]: "Publishing",
115
115
  [EntryTransition.DeleteFile]: "Deleting",
116
- [EntryTransition.DeleteArchived]: "Deleting"
116
+ [EntryTransition.DeleteEntry]: "Deleting"
117
117
  };
118
118
  var variantIcon = {
119
119
  draft: IcRoundEdit,
@@ -144,9 +144,9 @@ function EntryHeader({ editor, editable = true }) {
144
144
  const restoreRevision = useSetAtom(editor.restoreRevision);
145
145
  const discardDraft = useSetAtom(editor.discardDraft);
146
146
  const unPublish = useSetAtom(editor.unPublish);
147
- const archivePublished = useSetAtom(editor.archivePublished);
147
+ const archive = useSetAtom(editor.archive);
148
148
  const publishArchived = useSetAtom(editor.publishArchived);
149
- const deleteArchived = useSetAtom(editor.deleteArchived);
149
+ const deleteEntry = useSetAtom(editor.deleteEntry);
150
150
  const deleteFile = useSetAtom(editor.deleteFile);
151
151
  const deleteMediaLibrary = useSetAtom(editor.deleteMediaLibrary);
152
152
  const queryClient = useQueryClient();
@@ -191,6 +191,7 @@ function EntryHeader({ editor, editable = true }) {
191
191
  };
192
192
  input.click();
193
193
  }
194
+ const isParentUnpublished = editor.parents.some((p) => p.status === "draft");
194
195
  const options = variant === "draft" ? /* @__PURE__ */ jsx(
195
196
  DropdownMenu.Item,
196
197
  {
@@ -231,14 +232,7 @@ function EntryHeader({ editor, editable = true }) {
231
232
  children: "Unpublish"
232
233
  }
233
234
  ),
234
- /* @__PURE__ */ jsx(
235
- DropdownMenu.Item,
236
- {
237
- className: styles.root.action(),
238
- onClick: archivePublished,
239
- children: "Archive"
240
- }
241
- )
235
+ /* @__PURE__ */ jsx(DropdownMenu.Item, { className: styles.root.action(), onClick: archive, children: "Archive" })
242
236
  ] }) : variant === "archived" ? /* @__PURE__ */ jsxs(Fragment, { children: [
243
237
  canPublish && /* @__PURE__ */ jsx(
244
238
  DropdownMenu.Item,
@@ -252,11 +246,18 @@ function EntryHeader({ editor, editable = true }) {
252
246
  DropdownMenu.Item,
253
247
  {
254
248
  className: styles.root.action(),
255
- onClick: deleteArchived,
249
+ onClick: deleteEntry,
256
250
  children: "Delete"
257
251
  }
258
252
  )
259
- ] }) : null;
253
+ ] }) : variant === "unpublished" ? isParentUnpublished ? /* @__PURE__ */ jsx(
254
+ DropdownMenu.Item,
255
+ {
256
+ className: styles.root.action(),
257
+ onClick: deleteEntry,
258
+ children: "Delete"
259
+ }
260
+ ) : /* @__PURE__ */ jsx(DropdownMenu.Item, { className: styles.root.action(), onClick: archive, children: "Archive" }) : null;
260
261
  const inTransition = currentTransition !== void 0;
261
262
  return /* @__PURE__ */ jsxs(Fragment, { children: [
262
263
  isReplacing && /* @__PURE__ */ jsx(FileUploader, {}),
@@ -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.0",
4
+ "version": "1.4.0",
5
5
  "description": "Headless git-based CMS",
6
6
  "repository": {
7
7
  "type": "git",