create-shape-app 0.1.3 → 0.1.4

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.
@@ -23,10 +23,11 @@ export async function fetchTemplateRelease(options = {}) {
23
23
  const endpoint = templateRef
24
24
  ? `https://api.github.com/repos/${owner}/${repo}/releases/tags/${encodeURIComponent(templateRef)}`
25
25
  : `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
26
+ const headers = buildHeaders(githubToken);
26
27
  let response;
27
28
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
28
29
  response = await fetchImpl(endpoint, {
29
- headers: buildHeaders(githubToken),
30
+ headers,
30
31
  });
31
32
  if (response.ok) {
32
33
  break;
@@ -40,6 +41,37 @@ export async function fetchTemplateRelease(options = {}) {
40
41
  throw new Error('Failed to resolve template release: no response received.');
41
42
  }
42
43
  if (!response.ok) {
44
+ if (response.status === 404) {
45
+ if (templateRef) {
46
+ const resolvedTag = await resolveTemplateRefTag({
47
+ owner,
48
+ repo,
49
+ templateRef,
50
+ githubToken,
51
+ fetchImpl,
52
+ });
53
+ if (resolvedTag) {
54
+ return {
55
+ tag: resolvedTag,
56
+ tarballUrl: buildTagTarballUrl(owner, repo, resolvedTag),
57
+ };
58
+ }
59
+ }
60
+ else {
61
+ const latestTag = await resolveLatestSupportedTag({
62
+ owner,
63
+ repo,
64
+ githubToken,
65
+ fetchImpl,
66
+ });
67
+ if (latestTag) {
68
+ return {
69
+ tag: latestTag,
70
+ tarballUrl: buildTagTarballUrl(owner, repo, latestTag),
71
+ };
72
+ }
73
+ }
74
+ }
43
75
  throw new Error(await buildReleaseLookupError(response, templateRef));
44
76
  }
45
77
  const payload = (await response.json());
@@ -54,6 +86,9 @@ export async function fetchTemplateRelease(options = {}) {
54
86
  tarballUrl,
55
87
  };
56
88
  }
89
+ function buildTagTarballUrl(owner, repo, tag) {
90
+ return `https://api.github.com/repos/${owner}/${repo}/tarball/${encodeURIComponent(tag)}`;
91
+ }
57
92
  function buildHeaders(githubToken) {
58
93
  const headers = {
59
94
  Accept: 'application/vnd.github+json',
@@ -84,6 +119,50 @@ async function buildReleaseLookupError(response, templateRef) {
84
119
  }
85
120
  return `Failed to resolve ${refLabel}: HTTP ${response.status}.`;
86
121
  }
122
+ async function resolveLatestSupportedTag(options) {
123
+ const tags = await fetchTagNames(options);
124
+ return tags.find((tag) => isSupportedTag(tag));
125
+ }
126
+ async function resolveTemplateRefTag(options) {
127
+ const tags = await fetchTagNames(options);
128
+ const exactMatch = tags.find((tag) => tag === options.templateRef && isSupportedTag(tag));
129
+ if (exactMatch) {
130
+ return exactMatch;
131
+ }
132
+ const normalizedTemplateRef = normalizeTag(options.templateRef);
133
+ return tags.find((tag) => normalizeTag(tag) === normalizedTemplateRef && isSupportedTag(tag));
134
+ }
135
+ async function fetchTagNames(options) {
136
+ const response = await options.fetchImpl(`https://api.github.com/repos/${options.owner}/${options.repo}/tags?per_page=100`, {
137
+ headers: buildHeaders(options.githubToken),
138
+ });
139
+ if (!response.ok) {
140
+ return [];
141
+ }
142
+ const payload = (await response.json());
143
+ if (!Array.isArray(payload)) {
144
+ return [];
145
+ }
146
+ return payload.flatMap((item) => {
147
+ if (!item || typeof item !== 'object') {
148
+ return [];
149
+ }
150
+ const name = item.name;
151
+ return typeof name === 'string' && name.trim() ? [name] : [];
152
+ });
153
+ }
154
+ function isSupportedTag(tag) {
155
+ try {
156
+ assertTagIsSupported(tag);
157
+ return true;
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ function normalizeTag(tag) {
164
+ return tag.startsWith('v') ? tag.slice(1) : tag;
165
+ }
87
166
  async function readApiMessage(response) {
88
167
  try {
89
168
  const payload = (await response.clone().json());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-shape-app",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Scaffold Shape Builder Kit projects from pinned release tags.",
5
5
  "license": "MIT",
6
6
  "repository": {