@wp-typia/project-tools 0.20.2 → 0.22.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/dist/runtime/ai-feature-capability.js +2 -33
- package/dist/runtime/built-in-block-artifact-types.js +11 -0
- package/dist/runtime/built-in-block-code-artifacts.js +5 -1
- package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +4 -3
- package/dist/runtime/built-in-block-code-templates/interactivity.js +259 -100
- package/dist/runtime/built-in-block-code-templates.d.ts +1 -1
- package/dist/runtime/built-in-block-code-templates.js +1 -1
- package/dist/runtime/cli-add-shared.d.ts +74 -5
- package/dist/runtime/cli-add-shared.js +61 -11
- package/dist/runtime/cli-add-workspace-ability.js +14 -61
- package/dist/runtime/cli-add-workspace-admin-view.d.ts +25 -0
- package/dist/runtime/cli-add-workspace-admin-view.js +1401 -0
- package/dist/runtime/cli-add-workspace-ai-anchors.js +2 -5
- package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +0 -4
- package/dist/runtime/cli-add-workspace-ai-source-emitters.js +7 -17
- package/dist/runtime/cli-add-workspace-ai.js +4 -6
- package/dist/runtime/cli-add-workspace-assets.d.ts +13 -5
- package/dist/runtime/cli-add-workspace-assets.js +290 -106
- package/dist/runtime/cli-add-workspace-rest-anchors.js +2 -5
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +0 -1
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +7 -14
- package/dist/runtime/cli-add-workspace-rest.js +4 -6
- package/dist/runtime/cli-add-workspace.d.ts +58 -1
- package/dist/runtime/cli-add-workspace.js +588 -18
- package/dist/runtime/cli-add.d.ts +1 -1
- package/dist/runtime/cli-add.js +1 -1
- package/dist/runtime/cli-core.d.ts +8 -5
- package/dist/runtime/cli-core.js +7 -4
- package/dist/runtime/cli-diagnostics.d.ts +83 -1
- package/dist/runtime/cli-diagnostics.js +85 -2
- package/dist/runtime/cli-doctor-workspace.js +553 -13
- package/dist/runtime/cli-doctor.d.ts +4 -2
- package/dist/runtime/cli-doctor.js +2 -1
- package/dist/runtime/cli-help.js +22 -9
- package/dist/runtime/cli-init.d.ts +67 -3
- package/dist/runtime/cli-init.js +603 -64
- package/dist/runtime/cli-validation.js +4 -3
- package/dist/runtime/external-layer-selection.d.ts +8 -2
- package/dist/runtime/external-layer-selection.js +3 -4
- package/dist/runtime/index.d.ts +9 -4
- package/dist/runtime/index.js +7 -3
- package/dist/runtime/package-json-types.d.ts +12 -0
- package/dist/runtime/package-json-types.js +1 -0
- package/dist/runtime/package-versions.d.ts +30 -2
- package/dist/runtime/package-versions.js +59 -1
- package/dist/runtime/php-utils.d.ts +16 -0
- package/dist/runtime/php-utils.js +59 -0
- package/dist/runtime/scaffold-answer-resolution.js +7 -6
- package/dist/runtime/scaffold-apply-utils.d.ts +2 -3
- package/dist/runtime/scaffold-apply-utils.js +3 -43
- package/dist/runtime/scaffold-compatibility.d.ts +2 -2
- package/dist/runtime/scaffold-compatibility.js +22 -48
- package/dist/runtime/template-source-cache.d.ts +112 -0
- package/dist/runtime/template-source-cache.js +434 -0
- package/dist/runtime/template-source-seeds.js +319 -53
- package/dist/runtime/version-floor.d.ts +26 -0
- package/dist/runtime/version-floor.js +56 -0
- package/dist/runtime/workspace-inventory.d.ts +44 -2
- package/dist/runtime/workspace-inventory.js +138 -5
- package/package.json +4 -3
|
@@ -6,6 +6,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
import semver from 'semver';
|
|
7
7
|
import { x as extractTarball } from 'tar';
|
|
8
8
|
import { createExternalTemplateTimeoutError, fetchWithExternalTemplateTimeout, getExternalTemplateMetadataMaxBytes, getExternalTemplateTarballMaxBytes, getExternalTemplateTimeoutMs, readBufferResponseWithLimit, readJsonResponseWithLimit, } from './external-template-guards.js';
|
|
9
|
+
import { findReusableExternalTemplateSourceCache, isExternalTemplateCacheEnabled, resolveExternalTemplateSourceCache, } from './template-source-cache.js';
|
|
10
|
+
import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
|
|
9
11
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, PROJECT_TOOLS_PACKAGE_ROOT, TEMPLATE_IDS, } from './template-registry.js';
|
|
10
12
|
import { isPlainObject } from './object-utils.js';
|
|
11
13
|
import { createManagedTempRoot } from './temp-roots.js';
|
|
@@ -13,6 +15,19 @@ const USER_FACING_TEMPLATE_IDS = [
|
|
|
13
15
|
...TEMPLATE_IDS,
|
|
14
16
|
OFFICIAL_WORKSPACE_TEMPLATE_ALIAS,
|
|
15
17
|
];
|
|
18
|
+
const GITHUB_TEMPLATE_CACHE_REVISION_RACE_CODE = 'github-template-cache-revision-race';
|
|
19
|
+
function createGitHubTemplateCacheRevisionRaceError(message) {
|
|
20
|
+
const error = new Error(message);
|
|
21
|
+
error.code = GITHUB_TEMPLATE_CACHE_REVISION_RACE_CODE;
|
|
22
|
+
return error;
|
|
23
|
+
}
|
|
24
|
+
function isGitHubTemplateCacheRevisionRaceError(error) {
|
|
25
|
+
return (typeof error === 'object' &&
|
|
26
|
+
error !== null &&
|
|
27
|
+
'code' in error &&
|
|
28
|
+
error.code ===
|
|
29
|
+
GITHUB_TEMPLATE_CACHE_REVISION_RACE_CODE);
|
|
30
|
+
}
|
|
16
31
|
function getUnknownNpmTemplateMessage(templateId) {
|
|
17
32
|
return [
|
|
18
33
|
`Unknown template "${templateId}". Expected one of: ${USER_FACING_TEMPLATE_IDS.join(', ')}.`,
|
|
@@ -20,6 +35,44 @@ function getUnknownNpmTemplateMessage(templateId) {
|
|
|
20
35
|
'If you meant an npm template package, verify the package name and configured npm registry.',
|
|
21
36
|
].join(' ');
|
|
22
37
|
}
|
|
38
|
+
function readOptionalDistString(dist, key) {
|
|
39
|
+
const value = dist[key];
|
|
40
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
41
|
+
}
|
|
42
|
+
function normalizeNpmRegistryCacheKey(registryBase) {
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(registryBase);
|
|
45
|
+
url.username = '';
|
|
46
|
+
url.password = '';
|
|
47
|
+
url.search = '';
|
|
48
|
+
url.hash = '';
|
|
49
|
+
return url.toString().replace(/\/$/u, '');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return registryBase;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir) {
|
|
56
|
+
const tarballResponse = await fetchWithExternalTemplateTimeout(tarballUrl, {
|
|
57
|
+
label: `downloading npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
58
|
+
});
|
|
59
|
+
if (!tarballResponse.ok) {
|
|
60
|
+
throw new Error(`Failed to download npm template tarball for ${locator.raw}: ${tarballResponse.status}`);
|
|
61
|
+
}
|
|
62
|
+
const tarballPath = path.join(path.dirname(unpackDir), 'template.tgz');
|
|
63
|
+
await fsp.mkdir(unpackDir, { recursive: true });
|
|
64
|
+
await fsp.writeFile(tarballPath, await readBufferResponseWithLimit(tarballResponse, {
|
|
65
|
+
label: `npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
66
|
+
maxBytes: getExternalTemplateTarballMaxBytes(),
|
|
67
|
+
}));
|
|
68
|
+
await extractTarball({
|
|
69
|
+
cwd: unpackDir,
|
|
70
|
+
file: tarballPath,
|
|
71
|
+
strip: 1,
|
|
72
|
+
});
|
|
73
|
+
await fsp.rm(tarballPath, { force: true });
|
|
74
|
+
await assertNoSymlinks(unpackDir);
|
|
75
|
+
}
|
|
23
76
|
function selectRegistryVersion(metadata, locator) {
|
|
24
77
|
const distTags = isPlainObject(metadata['dist-tags'])
|
|
25
78
|
? metadata['dist-tags']
|
|
@@ -61,7 +114,7 @@ async function fetchNpmTemplateSource(locator) {
|
|
|
61
114
|
});
|
|
62
115
|
if (!metadataResponse.ok) {
|
|
63
116
|
if (metadataResponse.status === 404) {
|
|
64
|
-
throw
|
|
117
|
+
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, getUnknownNpmTemplateMessage(locator.raw));
|
|
65
118
|
}
|
|
66
119
|
throw new Error(`Failed to fetch npm template metadata for ${locator.raw}: ${metadataResponse.status}`);
|
|
67
120
|
}
|
|
@@ -79,27 +132,43 @@ async function fetchNpmTemplateSource(locator) {
|
|
|
79
132
|
if (typeof tarballUrl !== 'string' || tarballUrl.length === 0) {
|
|
80
133
|
throw new Error(`npm template metadata is missing tarball URL for ${locator.raw}@${resolvedVersion}.`);
|
|
81
134
|
}
|
|
135
|
+
const tarballIntegrity = readOptionalDistString(versionMetadata.dist, 'integrity');
|
|
136
|
+
const tarballShasum = readOptionalDistString(versionMetadata.dist, 'shasum');
|
|
137
|
+
if (tarballIntegrity || tarballShasum) {
|
|
138
|
+
const registryCacheKey = normalizeNpmRegistryCacheKey(registryBase);
|
|
139
|
+
const cachedSource = await resolveExternalTemplateSourceCache({
|
|
140
|
+
keyParts: [
|
|
141
|
+
'npm',
|
|
142
|
+
registryCacheKey,
|
|
143
|
+
locator.name,
|
|
144
|
+
locator.raw,
|
|
145
|
+
resolvedVersion,
|
|
146
|
+
tarballIntegrity ?? '',
|
|
147
|
+
tarballShasum ?? '',
|
|
148
|
+
],
|
|
149
|
+
metadata: {
|
|
150
|
+
integrity: tarballIntegrity,
|
|
151
|
+
package: locator.name,
|
|
152
|
+
raw: locator.raw,
|
|
153
|
+
registry: registryBase,
|
|
154
|
+
shasum: tarballShasum,
|
|
155
|
+
tarball: tarballUrl,
|
|
156
|
+
version: resolvedVersion,
|
|
157
|
+
},
|
|
158
|
+
namespace: 'npm',
|
|
159
|
+
}, (unpackDir) => downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir));
|
|
160
|
+
if (cachedSource) {
|
|
161
|
+
await assertNoSymlinks(cachedSource.sourceDir);
|
|
162
|
+
return {
|
|
163
|
+
blockDir: cachedSource.sourceDir,
|
|
164
|
+
rootDir: cachedSource.sourceDir,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
82
168
|
const { path: tempRoot, cleanup } = await createManagedTempRoot('wp-typia-template-source-');
|
|
83
169
|
try {
|
|
84
|
-
const tarballResponse = await fetchWithExternalTemplateTimeout(tarballUrl, {
|
|
85
|
-
label: `downloading npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
86
|
-
});
|
|
87
|
-
if (!tarballResponse.ok) {
|
|
88
|
-
throw new Error(`Failed to download npm template tarball for ${locator.raw}: ${tarballResponse.status}`);
|
|
89
|
-
}
|
|
90
|
-
const tarballPath = path.join(tempRoot, 'template.tgz');
|
|
91
170
|
const unpackDir = path.join(tempRoot, 'source');
|
|
92
|
-
await
|
|
93
|
-
await fsp.writeFile(tarballPath, await readBufferResponseWithLimit(tarballResponse, {
|
|
94
|
-
label: `npm template tarball for ${locator.raw}@${resolvedVersion}`,
|
|
95
|
-
maxBytes: getExternalTemplateTarballMaxBytes(),
|
|
96
|
-
}));
|
|
97
|
-
await extractTarball({
|
|
98
|
-
cwd: unpackDir,
|
|
99
|
-
file: tarballPath,
|
|
100
|
-
strip: 1,
|
|
101
|
-
});
|
|
102
|
-
await assertNoSymlinks(unpackDir);
|
|
171
|
+
await downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir);
|
|
103
172
|
return {
|
|
104
173
|
blockDir: unpackDir,
|
|
105
174
|
cleanup,
|
|
@@ -200,47 +269,244 @@ export async function assertNoSymlinks(sourceDir) {
|
|
|
200
269
|
await assertNoSymlinks(path.join(sourceDir, entry));
|
|
201
270
|
}
|
|
202
271
|
}
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const cloneTimeoutMs = getExternalTemplateTimeoutMs();
|
|
213
|
-
const cloneResult = spawnSync('git', args, {
|
|
272
|
+
function runGitTemplateCommand(args, label, options = {}) {
|
|
273
|
+
const timeoutMs = getExternalTemplateTimeoutMs();
|
|
274
|
+
const result = options.captureOutput
|
|
275
|
+
? spawnSync('git', args, {
|
|
276
|
+
encoding: 'utf8',
|
|
277
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
278
|
+
timeout: timeoutMs,
|
|
279
|
+
})
|
|
280
|
+
: spawnSync('git', args, {
|
|
214
281
|
stdio: 'ignore',
|
|
215
|
-
timeout:
|
|
282
|
+
timeout: timeoutMs,
|
|
216
283
|
});
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
throw cloneResult.error;
|
|
284
|
+
if (result.error) {
|
|
285
|
+
const errorCode = typeof result.error === 'object' &&
|
|
286
|
+
result.error !== null &&
|
|
287
|
+
'code' in result.error
|
|
288
|
+
? String(result.error.code)
|
|
289
|
+
: '';
|
|
290
|
+
if (errorCode === 'ETIMEDOUT') {
|
|
291
|
+
throw createExternalTemplateTimeoutError(label, timeoutMs);
|
|
227
292
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
293
|
+
throw result.error;
|
|
294
|
+
}
|
|
295
|
+
if (result.signal === 'SIGTERM' || result.signal === 'SIGKILL') {
|
|
296
|
+
throw createExternalTemplateTimeoutError(label, timeoutMs);
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
function getGitHubTemplateRepositoryUrl(locator) {
|
|
301
|
+
return `https://github.com/${locator.owner}/${locator.repo}.git`;
|
|
302
|
+
}
|
|
303
|
+
function resolveGitHubTemplateDirectory(checkoutDir, locator) {
|
|
304
|
+
const sourceDir = path.resolve(checkoutDir, locator.sourcePath);
|
|
305
|
+
const relativeSourceDir = path.relative(checkoutDir, sourceDir);
|
|
306
|
+
if (relativeSourceDir.startsWith('..') || path.isAbsolute(relativeSourceDir)) {
|
|
307
|
+
throw new Error('GitHub template path must stay within the cloned repository.');
|
|
308
|
+
}
|
|
309
|
+
if (!fs.existsSync(sourceDir)) {
|
|
310
|
+
throw new Error(`GitHub template path does not exist: ${locator.sourcePath}`);
|
|
311
|
+
}
|
|
312
|
+
return sourceDir;
|
|
313
|
+
}
|
|
314
|
+
function cloneGitHubTemplateSource(locator, checkoutDir) {
|
|
315
|
+
const args = ['clone', '--depth', '1'];
|
|
316
|
+
if (locator.ref) {
|
|
317
|
+
args.push('--branch', locator.ref);
|
|
318
|
+
}
|
|
319
|
+
args.push(getGitHubTemplateRepositoryUrl(locator), checkoutDir);
|
|
320
|
+
const cloneResult = runGitTemplateCommand(args, `cloning GitHub template ${locator.owner}/${locator.repo}`);
|
|
321
|
+
if (cloneResult.status !== 0) {
|
|
322
|
+
throw new Error(`Failed to clone GitHub template source ${locator.owner}/${locator.repo}.`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function readGitHubTemplateHeadRevision(checkoutDir) {
|
|
326
|
+
const result = runGitTemplateCommand(['-C', checkoutDir, 'rev-parse', 'HEAD'], 'reading GitHub template checkout revision', { captureOutput: true });
|
|
327
|
+
if (result.status !== 0 || typeof result.stdout !== 'string') {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const revision = result.stdout.trim().split(/\s+/u)[0];
|
|
331
|
+
return /^[0-9a-f]{40}$/iu.test(revision) ? revision.toLowerCase() : null;
|
|
332
|
+
}
|
|
333
|
+
function pinGitHubTemplateCacheRevision(locator, checkoutDir, cacheRevision) {
|
|
334
|
+
const normalizedCacheRevision = cacheRevision.toLowerCase();
|
|
335
|
+
if (readGitHubTemplateHeadRevision(checkoutDir) === normalizedCacheRevision) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const fetchResult = runGitTemplateCommand(['-C', checkoutDir, 'fetch', '--depth', '1', 'origin', cacheRevision], `fetching GitHub template revision ${locator.owner}/${locator.repo}`);
|
|
339
|
+
if (fetchResult.status !== 0) {
|
|
340
|
+
throw createGitHubTemplateCacheRevisionRaceError(`Failed to fetch GitHub template revision ${cacheRevision} for ${locator.owner}/${locator.repo}.`);
|
|
341
|
+
}
|
|
342
|
+
const checkoutResult = runGitTemplateCommand(['-C', checkoutDir, 'checkout', '--detach', cacheRevision], `checking out GitHub template revision ${locator.owner}/${locator.repo}`);
|
|
343
|
+
if (checkoutResult.status !== 0) {
|
|
344
|
+
throw createGitHubTemplateCacheRevisionRaceError(`Failed to check out GitHub template revision ${cacheRevision} for ${locator.owner}/${locator.repo}.`);
|
|
345
|
+
}
|
|
346
|
+
if (readGitHubTemplateHeadRevision(checkoutDir) !== normalizedCacheRevision) {
|
|
347
|
+
throw createGitHubTemplateCacheRevisionRaceError(`GitHub template checkout did not match resolved revision ${cacheRevision} for ${locator.owner}/${locator.repo}.`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function getGitHubTemplateRevisionPatterns(locator) {
|
|
351
|
+
const ref = locator.ref ?? 'HEAD';
|
|
352
|
+
if (!locator.ref) {
|
|
353
|
+
return [ref];
|
|
354
|
+
}
|
|
355
|
+
if (ref.startsWith('refs/')) {
|
|
356
|
+
return [ref, `${ref}^{}`];
|
|
357
|
+
}
|
|
358
|
+
return [ref, `refs/heads/${ref}`, `refs/tags/${ref}`, `refs/tags/${ref}^{}`];
|
|
359
|
+
}
|
|
360
|
+
function pickGitHubTemplateCacheRevision(locator, revisions) {
|
|
361
|
+
const ref = locator.ref ?? 'HEAD';
|
|
362
|
+
if (!locator.ref) {
|
|
363
|
+
return revisions[0]?.revision ?? null;
|
|
364
|
+
}
|
|
365
|
+
if (!ref.startsWith('refs/')) {
|
|
366
|
+
const branchRevision = revisions.find((entry) => entry.resolvedRef === `refs/heads/${ref}`);
|
|
367
|
+
if (branchRevision) {
|
|
368
|
+
return branchRevision.revision;
|
|
231
369
|
}
|
|
232
|
-
|
|
233
|
-
|
|
370
|
+
const peeledTagRevision = revisions.find((entry) => entry.resolvedRef === `refs/tags/${ref}^{}`);
|
|
371
|
+
if (peeledTagRevision) {
|
|
372
|
+
return peeledTagRevision.revision;
|
|
234
373
|
}
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
path.isAbsolute(relativeSourceDir)) {
|
|
239
|
-
throw new Error('GitHub template path must stay within the cloned repository.');
|
|
374
|
+
const tagRevision = revisions.find((entry) => entry.resolvedRef === `refs/tags/${ref}`);
|
|
375
|
+
if (tagRevision) {
|
|
376
|
+
return tagRevision.revision;
|
|
240
377
|
}
|
|
241
|
-
|
|
242
|
-
|
|
378
|
+
}
|
|
379
|
+
if (ref.startsWith('refs/tags/')) {
|
|
380
|
+
const peeledRevision = revisions.find((entry) => entry.resolvedRef === `${ref}^{}`);
|
|
381
|
+
if (peeledRevision) {
|
|
382
|
+
return peeledRevision.revision;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const exactRevision = revisions.find((entry) => entry.resolvedRef === ref);
|
|
386
|
+
return (exactRevision ?? revisions[0])?.revision ?? null;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Resolves a GitHub template ref to a stable revision for cache keying.
|
|
390
|
+
*
|
|
391
|
+
* @param locator GitHub template locator containing owner, repo, and optional ref.
|
|
392
|
+
* @returns Revision lookup result, including whether `git ls-remote` was unavailable.
|
|
393
|
+
*/
|
|
394
|
+
function resolveGitHubTemplateCacheRevision(locator) {
|
|
395
|
+
const result = runGitTemplateCommand([
|
|
396
|
+
'ls-remote',
|
|
397
|
+
getGitHubTemplateRepositoryUrl(locator),
|
|
398
|
+
...getGitHubTemplateRevisionPatterns(locator),
|
|
399
|
+
], `checking GitHub template revision ${locator.owner}/${locator.repo}`, { captureOutput: true });
|
|
400
|
+
if (result.status !== 0 || typeof result.stdout !== 'string') {
|
|
401
|
+
return {
|
|
402
|
+
lookupUnavailable: true,
|
|
403
|
+
revision: null,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const revisions = result.stdout
|
|
407
|
+
.split('\n')
|
|
408
|
+
.map((line) => {
|
|
409
|
+
const [revision, resolvedRef] = line.trim().split(/\s+/u);
|
|
410
|
+
if (!/^[0-9a-f]{40}$/iu.test(revision) || !resolvedRef) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
resolvedRef,
|
|
415
|
+
revision: revision.toLowerCase(),
|
|
416
|
+
};
|
|
417
|
+
})
|
|
418
|
+
.filter((entry) => entry !== null);
|
|
419
|
+
return {
|
|
420
|
+
lookupUnavailable: false,
|
|
421
|
+
revision: pickGitHubTemplateCacheRevision(locator, revisions),
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function getGitHubTemplateCacheMetadata(locator, revision) {
|
|
425
|
+
return {
|
|
426
|
+
...(revision ? { revision } : {}),
|
|
427
|
+
owner: locator.owner,
|
|
428
|
+
ref: locator.ref,
|
|
429
|
+
repo: locator.repo,
|
|
430
|
+
sourcePath: locator.sourcePath,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
async function reuseGitHubTemplateCacheByMetadata(locator) {
|
|
434
|
+
const cachedSource = await findReusableExternalTemplateSourceCache({
|
|
435
|
+
metadata: getGitHubTemplateCacheMetadata(locator),
|
|
436
|
+
namespace: 'github',
|
|
437
|
+
});
|
|
438
|
+
if (!cachedSource) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
442
|
+
await assertNoSymlinks(sourceDir);
|
|
443
|
+
return {
|
|
444
|
+
blockDir: sourceDir,
|
|
445
|
+
rootDir: sourceDir,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
async function resolveGitHubTemplateSource(locator) {
|
|
449
|
+
const cacheEnabled = isExternalTemplateCacheEnabled();
|
|
450
|
+
let cacheRevisionLookupUnavailable = false;
|
|
451
|
+
let cacheRevision = null;
|
|
452
|
+
if (cacheEnabled) {
|
|
453
|
+
try {
|
|
454
|
+
const resolvedRevision = resolveGitHubTemplateCacheRevision(locator);
|
|
455
|
+
cacheRevision = resolvedRevision.revision;
|
|
456
|
+
cacheRevisionLookupUnavailable = resolvedRevision.lookupUnavailable;
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
cacheRevision = null;
|
|
460
|
+
cacheRevisionLookupUnavailable = true;
|
|
243
461
|
}
|
|
462
|
+
}
|
|
463
|
+
if (cacheEnabled && !cacheRevision && cacheRevisionLookupUnavailable) {
|
|
464
|
+
const cachedSource = await reuseGitHubTemplateCacheByMetadata(locator);
|
|
465
|
+
if (cachedSource) {
|
|
466
|
+
return cachedSource;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (cacheRevision) {
|
|
470
|
+
const resolvedCacheRevision = cacheRevision;
|
|
471
|
+
try {
|
|
472
|
+
const cachedSource = await resolveExternalTemplateSourceCache({
|
|
473
|
+
keyParts: [
|
|
474
|
+
'github',
|
|
475
|
+
locator.owner,
|
|
476
|
+
locator.repo,
|
|
477
|
+
locator.sourcePath,
|
|
478
|
+
locator.ref ?? '',
|
|
479
|
+
resolvedCacheRevision,
|
|
480
|
+
],
|
|
481
|
+
metadata: getGitHubTemplateCacheMetadata(locator, resolvedCacheRevision),
|
|
482
|
+
namespace: 'github',
|
|
483
|
+
}, async (checkoutDir) => {
|
|
484
|
+
cloneGitHubTemplateSource(locator, checkoutDir);
|
|
485
|
+
const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
486
|
+
await assertNoSymlinks(sourceDir);
|
|
487
|
+
pinGitHubTemplateCacheRevision(locator, checkoutDir, resolvedCacheRevision);
|
|
488
|
+
});
|
|
489
|
+
if (cachedSource) {
|
|
490
|
+
const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
491
|
+
await assertNoSymlinks(sourceDir);
|
|
492
|
+
return {
|
|
493
|
+
blockDir: sourceDir,
|
|
494
|
+
rootDir: sourceDir,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
if (!isGitHubTemplateCacheRevisionRaceError(error)) {
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
// Fall back to the existing uncached clone path if revision pinning races.
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const { path: remoteRoot, cleanup } = await createManagedTempRoot('wp-typia-template-source-');
|
|
506
|
+
const checkoutDir = path.join(remoteRoot, 'source');
|
|
507
|
+
try {
|
|
508
|
+
cloneGitHubTemplateSource(locator, checkoutDir);
|
|
509
|
+
const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
244
510
|
await assertNoSymlinks(sourceDir);
|
|
245
511
|
return {
|
|
246
512
|
blockDir: sourceDir,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a dotted version floor into numeric parts for stable comparisons.
|
|
3
|
+
*
|
|
4
|
+
* @param value Human-authored version floor such as `7.0` or `8.1.2`.
|
|
5
|
+
* @returns Numeric version segments suitable for ordered comparison.
|
|
6
|
+
* @throws When any segment contains non-digit characters.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseVersionFloorParts(value: string): number[];
|
|
9
|
+
/**
|
|
10
|
+
* Compare two dotted version floors.
|
|
11
|
+
*
|
|
12
|
+
* Missing trailing segments are treated as zero so `7.0` equals `7.0.0`.
|
|
13
|
+
*
|
|
14
|
+
* @param left Left-hand version floor.
|
|
15
|
+
* @param right Right-hand version floor.
|
|
16
|
+
* @returns `1` when left is higher, `-1` when right is higher, or `0` when equal.
|
|
17
|
+
*/
|
|
18
|
+
export declare function compareVersionFloors(left: string, right: string): number;
|
|
19
|
+
/**
|
|
20
|
+
* Pick the higher version floor while tolerating undefined values.
|
|
21
|
+
*
|
|
22
|
+
* @param current Current resolved floor, when present.
|
|
23
|
+
* @param candidate Candidate floor to compare against the current floor.
|
|
24
|
+
* @returns The higher defined floor, or undefined when neither value exists.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pickHigherVersionFloor(current: string | undefined, candidate: string | undefined): string | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a dotted version floor into numeric parts for stable comparisons.
|
|
3
|
+
*
|
|
4
|
+
* @param value Human-authored version floor such as `7.0` or `8.1.2`.
|
|
5
|
+
* @returns Numeric version segments suitable for ordered comparison.
|
|
6
|
+
* @throws When any segment contains non-digit characters.
|
|
7
|
+
*/
|
|
8
|
+
export function parseVersionFloorParts(value) {
|
|
9
|
+
return value.split('.').map((part, index) => {
|
|
10
|
+
if (!/^\d+$/u.test(part)) {
|
|
11
|
+
throw new Error(`parseVersionFloorParts received an invalid version floor "${value}" at segment ${index + 1}.`);
|
|
12
|
+
}
|
|
13
|
+
return Number.parseInt(part, 10);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compare two dotted version floors.
|
|
18
|
+
*
|
|
19
|
+
* Missing trailing segments are treated as zero so `7.0` equals `7.0.0`.
|
|
20
|
+
*
|
|
21
|
+
* @param left Left-hand version floor.
|
|
22
|
+
* @param right Right-hand version floor.
|
|
23
|
+
* @returns `1` when left is higher, `-1` when right is higher, or `0` when equal.
|
|
24
|
+
*/
|
|
25
|
+
export function compareVersionFloors(left, right) {
|
|
26
|
+
const leftParts = parseVersionFloorParts(left);
|
|
27
|
+
const rightParts = parseVersionFloorParts(right);
|
|
28
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
29
|
+
for (let index = 0; index < length; index += 1) {
|
|
30
|
+
const leftValue = leftParts[index] ?? 0;
|
|
31
|
+
const rightValue = rightParts[index] ?? 0;
|
|
32
|
+
if (leftValue > rightValue) {
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
if (leftValue < rightValue) {
|
|
36
|
+
return -1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Pick the higher version floor while tolerating undefined values.
|
|
43
|
+
*
|
|
44
|
+
* @param current Current resolved floor, when present.
|
|
45
|
+
* @param candidate Candidate floor to compare against the current floor.
|
|
46
|
+
* @returns The higher defined floor, or undefined when neither value exists.
|
|
47
|
+
*/
|
|
48
|
+
export function pickHigherVersionFloor(current, candidate) {
|
|
49
|
+
if (!candidate) {
|
|
50
|
+
return current;
|
|
51
|
+
}
|
|
52
|
+
if (!current) {
|
|
53
|
+
return candidate;
|
|
54
|
+
}
|
|
55
|
+
return compareVersionFloors(current, candidate) >= 0 ? current : candidate;
|
|
56
|
+
}
|
|
@@ -10,11 +10,25 @@ export interface WorkspaceVariationInventoryEntry {
|
|
|
10
10
|
file: string;
|
|
11
11
|
slug: string;
|
|
12
12
|
}
|
|
13
|
+
export interface WorkspaceBlockStyleInventoryEntry {
|
|
14
|
+
block: string;
|
|
15
|
+
file: string;
|
|
16
|
+
slug: string;
|
|
17
|
+
}
|
|
18
|
+
export interface WorkspaceBlockTransformInventoryEntry {
|
|
19
|
+
block: string;
|
|
20
|
+
file: string;
|
|
21
|
+
from: string;
|
|
22
|
+
slug: string;
|
|
23
|
+
to: string;
|
|
24
|
+
}
|
|
13
25
|
export interface WorkspacePatternInventoryEntry {
|
|
14
26
|
file: string;
|
|
15
27
|
slug: string;
|
|
16
28
|
}
|
|
17
29
|
export interface WorkspaceBindingSourceInventoryEntry {
|
|
30
|
+
attribute?: string;
|
|
31
|
+
block?: string;
|
|
18
32
|
editorFile: string;
|
|
19
33
|
serverFile: string;
|
|
20
34
|
slug: string;
|
|
@@ -76,6 +90,21 @@ export interface WorkspaceAiFeatureInventoryEntry {
|
|
|
76
90
|
typesFile: string;
|
|
77
91
|
validatorsFile: string;
|
|
78
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* DataViews admin-screen entry parsed from `scripts/block-config.ts`.
|
|
95
|
+
*
|
|
96
|
+
* @property file Relative path to the generated admin view shared entry file.
|
|
97
|
+
* @property phpFile Relative path to the generated WordPress admin page glue.
|
|
98
|
+
* @property slug Normalized admin view slug.
|
|
99
|
+
* @property source Optional source locator such as `rest-resource:products` or
|
|
100
|
+
* `core-data:postType/post`.
|
|
101
|
+
*/
|
|
102
|
+
export interface WorkspaceAdminViewInventoryEntry {
|
|
103
|
+
file: string;
|
|
104
|
+
phpFile: string;
|
|
105
|
+
slug: string;
|
|
106
|
+
source?: string;
|
|
107
|
+
}
|
|
79
108
|
/**
|
|
80
109
|
* Editor-plugin entry parsed from `scripts/block-config.ts`.
|
|
81
110
|
*
|
|
@@ -89,14 +118,20 @@ export interface WorkspaceEditorPluginInventoryEntry {
|
|
|
89
118
|
slot: string;
|
|
90
119
|
}
|
|
91
120
|
export interface WorkspaceInventory {
|
|
121
|
+
adminViews: WorkspaceAdminViewInventoryEntry[];
|
|
92
122
|
bindingSources: WorkspaceBindingSourceInventoryEntry[];
|
|
93
123
|
blockConfigPath: string;
|
|
94
124
|
blocks: WorkspaceBlockInventoryEntry[];
|
|
125
|
+
blockStyles: WorkspaceBlockStyleInventoryEntry[];
|
|
126
|
+
blockTransforms: WorkspaceBlockTransformInventoryEntry[];
|
|
95
127
|
abilities: WorkspaceAbilityInventoryEntry[];
|
|
96
128
|
aiFeatures: WorkspaceAiFeatureInventoryEntry[];
|
|
97
129
|
hasAbilitiesSection: boolean;
|
|
130
|
+
hasAdminViewsSection: boolean;
|
|
98
131
|
hasBindingSourcesSection: boolean;
|
|
99
132
|
hasAiFeaturesSection: boolean;
|
|
133
|
+
hasBlockStylesSection: boolean;
|
|
134
|
+
hasBlockTransformsSection: boolean;
|
|
100
135
|
hasEditorPluginsSection: boolean;
|
|
101
136
|
hasPatternsSection: boolean;
|
|
102
137
|
hasRestResourcesSection: boolean;
|
|
@@ -109,11 +144,14 @@ export interface WorkspaceInventory {
|
|
|
109
144
|
}
|
|
110
145
|
export declare const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
|
|
111
146
|
export declare const VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
|
|
147
|
+
export declare const BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
|
|
148
|
+
export declare const BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER = "\t// wp-typia add transform entries";
|
|
112
149
|
export declare const PATTERN_CONFIG_ENTRY_MARKER = "\t// wp-typia add pattern entries";
|
|
113
150
|
export declare const BINDING_SOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add binding-source entries";
|
|
114
151
|
export declare const REST_RESOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add rest-resource entries";
|
|
115
152
|
export declare const ABILITY_CONFIG_ENTRY_MARKER = "\t// wp-typia add ability entries";
|
|
116
153
|
export declare const AI_FEATURE_CONFIG_ENTRY_MARKER = "\t// wp-typia add ai-feature entries";
|
|
154
|
+
export declare const ADMIN_VIEW_CONFIG_ENTRY_MARKER = "\t// wp-typia add admin-view entries";
|
|
117
155
|
/**
|
|
118
156
|
* Marker used to append generated editor-plugin entries into `EDITOR_PLUGINS`.
|
|
119
157
|
*/
|
|
@@ -152,7 +190,8 @@ export declare function getWorkspaceBlockSelectOptions(projectDir: string): Arra
|
|
|
152
190
|
* Update `scripts/block-config.ts` source text with additional inventory entries.
|
|
153
191
|
*
|
|
154
192
|
* Missing inventory sections for variations, patterns, binding sources, REST
|
|
155
|
-
* resources, workflow abilities, AI features,
|
|
193
|
+
* resources, workflow abilities, AI features, editor plugins, block styles, and
|
|
194
|
+
* block transforms are created
|
|
156
195
|
* automatically before new entries are appended at their marker comments.
|
|
157
196
|
* When provided, `transformSource` runs before any entries are inserted.
|
|
158
197
|
*
|
|
@@ -160,10 +199,13 @@ export declare function getWorkspaceBlockSelectOptions(projectDir: string): Arra
|
|
|
160
199
|
* @param options Entry lists plus an optional source transformer.
|
|
161
200
|
* @returns Updated source text with all requested inventory entries appended.
|
|
162
201
|
*/
|
|
163
|
-
export declare function updateWorkspaceInventorySource(source: string, { blockEntries, bindingSourceEntries, abilityEntries, aiFeatureEntries, editorPluginEntries, patternEntries, restResourceEntries, variationEntries, transformSource, }?: {
|
|
202
|
+
export declare function updateWorkspaceInventorySource(source: string, { blockEntries, blockStyleEntries, blockTransformEntries, bindingSourceEntries, abilityEntries, adminViewEntries, aiFeatureEntries, editorPluginEntries, patternEntries, restResourceEntries, variationEntries, transformSource, }?: {
|
|
164
203
|
abilityEntries?: string[];
|
|
204
|
+
adminViewEntries?: string[];
|
|
165
205
|
aiFeatureEntries?: string[];
|
|
166
206
|
blockEntries?: string[];
|
|
207
|
+
blockStyleEntries?: string[];
|
|
208
|
+
blockTransformEntries?: string[];
|
|
167
209
|
bindingSourceEntries?: string[];
|
|
168
210
|
editorPluginEntries?: string[];
|
|
169
211
|
patternEntries?: string[];
|