@wp-typia/project-tools 0.20.1 → 0.21.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.
Files changed (48) hide show
  1. package/dist/runtime/cli-add-block.js +1 -1
  2. package/dist/runtime/cli-add-shared.d.ts +73 -5
  3. package/dist/runtime/cli-add-shared.js +58 -11
  4. package/dist/runtime/cli-add-workspace-ability.js +11 -57
  5. package/dist/runtime/cli-add-workspace-admin-view.d.ts +23 -0
  6. package/dist/runtime/cli-add-workspace-admin-view.js +872 -0
  7. package/dist/runtime/cli-add-workspace-ai-anchors.js +2 -5
  8. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +0 -4
  9. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +7 -17
  10. package/dist/runtime/cli-add-workspace-ai.js +4 -6
  11. package/dist/runtime/cli-add-workspace-assets.d.ts +13 -5
  12. package/dist/runtime/cli-add-workspace-assets.js +290 -106
  13. package/dist/runtime/cli-add-workspace-rest-anchors.js +2 -5
  14. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +0 -1
  15. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +7 -14
  16. package/dist/runtime/cli-add-workspace-rest.js +4 -6
  17. package/dist/runtime/cli-add-workspace.d.ts +58 -1
  18. package/dist/runtime/cli-add-workspace.js +588 -18
  19. package/dist/runtime/cli-add.d.ts +1 -1
  20. package/dist/runtime/cli-add.js +1 -1
  21. package/dist/runtime/cli-core.d.ts +8 -5
  22. package/dist/runtime/cli-core.js +7 -4
  23. package/dist/runtime/cli-diagnostics.d.ts +84 -1
  24. package/dist/runtime/cli-diagnostics.js +90 -3
  25. package/dist/runtime/cli-doctor-workspace.js +552 -13
  26. package/dist/runtime/cli-doctor.d.ts +4 -2
  27. package/dist/runtime/cli-doctor.js +2 -1
  28. package/dist/runtime/cli-help.js +19 -9
  29. package/dist/runtime/cli-init.d.ts +67 -3
  30. package/dist/runtime/cli-init.js +603 -64
  31. package/dist/runtime/cli-validation.js +4 -3
  32. package/dist/runtime/index.d.ts +9 -4
  33. package/dist/runtime/index.js +7 -3
  34. package/dist/runtime/package-json-types.d.ts +12 -0
  35. package/dist/runtime/package-json-types.js +1 -0
  36. package/dist/runtime/package-versions.d.ts +17 -2
  37. package/dist/runtime/package-versions.js +46 -1
  38. package/dist/runtime/php-utils.d.ts +16 -0
  39. package/dist/runtime/php-utils.js +59 -0
  40. package/dist/runtime/scaffold-answer-resolution.js +35 -10
  41. package/dist/runtime/scaffold-apply-utils.d.ts +2 -3
  42. package/dist/runtime/scaffold-apply-utils.js +3 -43
  43. package/dist/runtime/template-source-cache.d.ts +112 -0
  44. package/dist/runtime/template-source-cache.js +434 -0
  45. package/dist/runtime/template-source-seeds.js +333 -53
  46. package/dist/runtime/workspace-inventory.d.ts +43 -2
  47. package/dist/runtime/workspace-inventory.js +138 -5
  48. package/package.json +2 -2
@@ -6,9 +6,73 @@ 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 { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, PROJECT_TOOLS_PACKAGE_ROOT, } from './template-registry.js';
9
+ import { findReusableExternalTemplateSourceCache, isExternalTemplateCacheEnabled, resolveExternalTemplateSourceCache, } from './template-source-cache.js';
10
+ import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
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';
14
+ const USER_FACING_TEMPLATE_IDS = [
15
+ ...TEMPLATE_IDS,
16
+ OFFICIAL_WORKSPACE_TEMPLATE_ALIAS,
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
+ }
31
+ function getUnknownNpmTemplateMessage(templateId) {
32
+ return [
33
+ `Unknown template "${templateId}". Expected one of: ${USER_FACING_TEMPLATE_IDS.join(', ')}.`,
34
+ 'Run `wp-typia templates list` to inspect available templates.',
35
+ 'If you meant an npm template package, verify the package name and configured npm registry.',
36
+ ].join(' ');
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
+ }
12
76
  function selectRegistryVersion(metadata, locator) {
13
77
  const distTags = isPlainObject(metadata['dist-tags'])
14
78
  ? metadata['dist-tags']
@@ -49,6 +113,9 @@ async function fetchNpmTemplateSource(locator) {
49
113
  label: metadataLabel,
50
114
  });
51
115
  if (!metadataResponse.ok) {
116
+ if (metadataResponse.status === 404) {
117
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, getUnknownNpmTemplateMessage(locator.raw));
118
+ }
52
119
  throw new Error(`Failed to fetch npm template metadata for ${locator.raw}: ${metadataResponse.status}`);
53
120
  }
54
121
  const metadata = await readJsonResponseWithLimit(metadataResponse, {
@@ -65,27 +132,43 @@ async function fetchNpmTemplateSource(locator) {
65
132
  if (typeof tarballUrl !== 'string' || tarballUrl.length === 0) {
66
133
  throw new Error(`npm template metadata is missing tarball URL for ${locator.raw}@${resolvedVersion}.`);
67
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
+ }
68
168
  const { path: tempRoot, cleanup } = await createManagedTempRoot('wp-typia-template-source-');
69
169
  try {
70
- const tarballResponse = await fetchWithExternalTemplateTimeout(tarballUrl, {
71
- label: `downloading npm template tarball for ${locator.raw}@${resolvedVersion}`,
72
- });
73
- if (!tarballResponse.ok) {
74
- throw new Error(`Failed to download npm template tarball for ${locator.raw}: ${tarballResponse.status}`);
75
- }
76
- const tarballPath = path.join(tempRoot, 'template.tgz');
77
170
  const unpackDir = path.join(tempRoot, 'source');
78
- await fsp.mkdir(unpackDir, { recursive: true });
79
- await fsp.writeFile(tarballPath, await readBufferResponseWithLimit(tarballResponse, {
80
- label: `npm template tarball for ${locator.raw}@${resolvedVersion}`,
81
- maxBytes: getExternalTemplateTarballMaxBytes(),
82
- }));
83
- await extractTarball({
84
- cwd: unpackDir,
85
- file: tarballPath,
86
- strip: 1,
87
- });
88
- await assertNoSymlinks(unpackDir);
171
+ await downloadNpmTemplateTarball(locator, resolvedVersion, tarballUrl, unpackDir);
89
172
  return {
90
173
  blockDir: unpackDir,
91
174
  cleanup,
@@ -186,47 +269,244 @@ export async function assertNoSymlinks(sourceDir) {
186
269
  await assertNoSymlinks(path.join(sourceDir, entry));
187
270
  }
188
271
  }
189
- async function resolveGitHubTemplateSource(locator) {
190
- const { path: remoteRoot, cleanup } = await createManagedTempRoot('wp-typia-template-source-');
191
- const checkoutDir = path.join(remoteRoot, 'source');
192
- try {
193
- const args = ['clone', '--depth', '1'];
194
- if (locator.ref) {
195
- args.push('--branch', locator.ref);
196
- }
197
- args.push(`https://github.com/${locator.owner}/${locator.repo}.git`, checkoutDir);
198
- const cloneTimeoutMs = getExternalTemplateTimeoutMs();
199
- 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, {
200
281
  stdio: 'ignore',
201
- timeout: cloneTimeoutMs,
282
+ timeout: timeoutMs,
202
283
  });
203
- if (cloneResult.error) {
204
- const errorCode = typeof cloneResult.error === 'object' &&
205
- cloneResult.error !== null &&
206
- 'code' in cloneResult.error
207
- ? String(cloneResult.error.code)
208
- : '';
209
- if (errorCode === 'ETIMEDOUT') {
210
- throw createExternalTemplateTimeoutError(`cloning GitHub template ${locator.owner}/${locator.repo}`, cloneTimeoutMs);
211
- }
212
- 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);
213
292
  }
214
- if (cloneResult.signal === 'SIGTERM' ||
215
- cloneResult.signal === 'SIGKILL') {
216
- throw createExternalTemplateTimeoutError(`cloning GitHub template ${locator.owner}/${locator.repo}`, cloneTimeoutMs);
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;
217
369
  }
218
- if (cloneResult.status !== 0) {
219
- throw new Error(`Failed to clone GitHub template source ${locator.owner}/${locator.repo}.`);
370
+ const peeledTagRevision = revisions.find((entry) => entry.resolvedRef === `refs/tags/${ref}^{}`);
371
+ if (peeledTagRevision) {
372
+ return peeledTagRevision.revision;
220
373
  }
221
- const sourceDir = path.resolve(checkoutDir, locator.sourcePath);
222
- const relativeSourceDir = path.relative(checkoutDir, sourceDir);
223
- if (relativeSourceDir.startsWith('..') ||
224
- path.isAbsolute(relativeSourceDir)) {
225
- 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;
226
377
  }
227
- if (!fs.existsSync(sourceDir)) {
228
- throw new Error(`GitHub template path does not exist: ${locator.sourcePath}`);
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;
229
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;
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);
230
510
  await assertNoSymlinks(sourceDir);
231
511
  return {
232
512
  blockDir: sourceDir,
@@ -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,20 @@ 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`.
100
+ */
101
+ export interface WorkspaceAdminViewInventoryEntry {
102
+ file: string;
103
+ phpFile: string;
104
+ slug: string;
105
+ source?: string;
106
+ }
79
107
  /**
80
108
  * Editor-plugin entry parsed from `scripts/block-config.ts`.
81
109
  *
@@ -89,14 +117,20 @@ export interface WorkspaceEditorPluginInventoryEntry {
89
117
  slot: string;
90
118
  }
91
119
  export interface WorkspaceInventory {
120
+ adminViews: WorkspaceAdminViewInventoryEntry[];
92
121
  bindingSources: WorkspaceBindingSourceInventoryEntry[];
93
122
  blockConfigPath: string;
94
123
  blocks: WorkspaceBlockInventoryEntry[];
124
+ blockStyles: WorkspaceBlockStyleInventoryEntry[];
125
+ blockTransforms: WorkspaceBlockTransformInventoryEntry[];
95
126
  abilities: WorkspaceAbilityInventoryEntry[];
96
127
  aiFeatures: WorkspaceAiFeatureInventoryEntry[];
97
128
  hasAbilitiesSection: boolean;
129
+ hasAdminViewsSection: boolean;
98
130
  hasBindingSourcesSection: boolean;
99
131
  hasAiFeaturesSection: boolean;
132
+ hasBlockStylesSection: boolean;
133
+ hasBlockTransformsSection: boolean;
100
134
  hasEditorPluginsSection: boolean;
101
135
  hasPatternsSection: boolean;
102
136
  hasRestResourcesSection: boolean;
@@ -109,11 +143,14 @@ export interface WorkspaceInventory {
109
143
  }
110
144
  export declare const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
111
145
  export declare const VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
146
+ export declare const BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
147
+ export declare const BLOCK_TRANSFORM_CONFIG_ENTRY_MARKER = "\t// wp-typia add transform entries";
112
148
  export declare const PATTERN_CONFIG_ENTRY_MARKER = "\t// wp-typia add pattern entries";
113
149
  export declare const BINDING_SOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add binding-source entries";
114
150
  export declare const REST_RESOURCE_CONFIG_ENTRY_MARKER = "\t// wp-typia add rest-resource entries";
115
151
  export declare const ABILITY_CONFIG_ENTRY_MARKER = "\t// wp-typia add ability entries";
116
152
  export declare const AI_FEATURE_CONFIG_ENTRY_MARKER = "\t// wp-typia add ai-feature entries";
153
+ export declare const ADMIN_VIEW_CONFIG_ENTRY_MARKER = "\t// wp-typia add admin-view entries";
117
154
  /**
118
155
  * Marker used to append generated editor-plugin entries into `EDITOR_PLUGINS`.
119
156
  */
@@ -152,7 +189,8 @@ export declare function getWorkspaceBlockSelectOptions(projectDir: string): Arra
152
189
  * Update `scripts/block-config.ts` source text with additional inventory entries.
153
190
  *
154
191
  * Missing inventory sections for variations, patterns, binding sources, REST
155
- * resources, workflow abilities, AI features, and editor plugins are created
192
+ * resources, workflow abilities, AI features, editor plugins, block styles, and
193
+ * block transforms are created
156
194
  * automatically before new entries are appended at their marker comments.
157
195
  * When provided, `transformSource` runs before any entries are inserted.
158
196
  *
@@ -160,10 +198,13 @@ export declare function getWorkspaceBlockSelectOptions(projectDir: string): Arra
160
198
  * @param options Entry lists plus an optional source transformer.
161
199
  * @returns Updated source text with all requested inventory entries appended.
162
200
  */
163
- export declare function updateWorkspaceInventorySource(source: string, { blockEntries, bindingSourceEntries, abilityEntries, aiFeatureEntries, editorPluginEntries, patternEntries, restResourceEntries, variationEntries, transformSource, }?: {
201
+ export declare function updateWorkspaceInventorySource(source: string, { blockEntries, blockStyleEntries, blockTransformEntries, bindingSourceEntries, abilityEntries, adminViewEntries, aiFeatureEntries, editorPluginEntries, patternEntries, restResourceEntries, variationEntries, transformSource, }?: {
164
202
  abilityEntries?: string[];
203
+ adminViewEntries?: string[];
165
204
  aiFeatureEntries?: string[];
166
205
  blockEntries?: string[];
206
+ blockStyleEntries?: string[];
207
+ blockTransformEntries?: string[];
167
208
  bindingSourceEntries?: string[];
168
209
  editorPluginEntries?: string[];
169
210
  patternEntries?: string[];