@wp-typia/project-tools 0.22.7 → 0.22.9

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 (63) hide show
  1. package/dist/runtime/block-targets.d.ts +40 -0
  2. package/dist/runtime/block-targets.js +71 -0
  3. package/dist/runtime/built-in-block-artifact-types.js +2 -1
  4. package/dist/runtime/built-in-block-attribute-specs.js +2 -1
  5. package/dist/runtime/built-in-block-code-artifacts.js +2 -0
  6. package/dist/runtime/built-in-block-non-ts-family-artifacts.js +12 -9
  7. package/dist/runtime/built-in-block-non-ts-render-utils.js +2 -0
  8. package/dist/runtime/cli-add-block-config.js +2 -1
  9. package/dist/runtime/cli-add-block.js +16 -4
  10. package/dist/runtime/cli-add-types.d.ts +8 -0
  11. package/dist/runtime/cli-add-types.js +11 -0
  12. package/dist/runtime/cli-add-workspace-ability-scaffold.js +21 -15
  13. package/dist/runtime/cli-add-workspace-ability.js +2 -2
  14. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +17 -13
  15. package/dist/runtime/cli-add-workspace-admin-view.js +2 -2
  16. package/dist/runtime/cli-add-workspace-ai.js +2 -2
  17. package/dist/runtime/cli-add-workspace-assets.js +42 -48
  18. package/dist/runtime/cli-add-workspace-rest.js +2 -2
  19. package/dist/runtime/cli-add-workspace.js +6 -38
  20. package/dist/runtime/cli-add.d.ts +3 -2
  21. package/dist/runtime/cli-add.js +2 -2
  22. package/dist/runtime/cli-core.d.ts +4 -2
  23. package/dist/runtime/cli-core.js +3 -2
  24. package/dist/runtime/cli-diagnostics.js +6 -0
  25. package/dist/runtime/cli-doctor-workspace.js +2 -0
  26. package/dist/runtime/cli-scaffold.js +5 -4
  27. package/dist/runtime/create-template-validation.d.ts +10 -0
  28. package/dist/runtime/create-template-validation.js +95 -0
  29. package/dist/runtime/id-suggestions.d.ts +21 -0
  30. package/dist/runtime/id-suggestions.js +48 -0
  31. package/dist/runtime/index.d.ts +5 -2
  32. package/dist/runtime/index.js +3 -1
  33. package/dist/runtime/migration-maintenance-verify.js +2 -0
  34. package/dist/runtime/package-versions.js +15 -2
  35. package/dist/runtime/php-utils.js +66 -0
  36. package/dist/runtime/scaffold-answer-resolution.js +5 -108
  37. package/dist/runtime/scaffold-apply-utils.js +3 -2
  38. package/dist/runtime/scaffold-identifiers.js +4 -3
  39. package/dist/runtime/scaffold-template-assertions.d.ts +6 -0
  40. package/dist/runtime/scaffold-template-assertions.js +33 -0
  41. package/dist/runtime/scaffold-template-variable-groups.d.ts +2 -0
  42. package/dist/runtime/scaffold-template-variable-groups.js +7 -0
  43. package/dist/runtime/scaffold.js +3 -3
  44. package/dist/runtime/string-case.d.ts +2 -4
  45. package/dist/runtime/string-case.js +13 -4
  46. package/dist/runtime/template-builtins.js +11 -8
  47. package/dist/runtime/template-layers.js +2 -2
  48. package/dist/runtime/template-source-cache-policy.d.ts +53 -0
  49. package/dist/runtime/template-source-cache-policy.js +135 -0
  50. package/dist/runtime/template-source-cache.d.ts +1 -45
  51. package/dist/runtime/template-source-cache.js +9 -152
  52. package/dist/runtime/template-source-external.d.ts +3 -0
  53. package/dist/runtime/template-source-external.js +5 -2
  54. package/dist/runtime/template-source-remote.d.ts +6 -0
  55. package/dist/runtime/template-source-remote.js +8 -2
  56. package/dist/runtime/template-source-seeds.d.ts +13 -0
  57. package/dist/runtime/template-source-seeds.js +36 -8
  58. package/dist/runtime/template-source.js +2 -2
  59. package/dist/runtime/ts-property-names.d.ts +11 -0
  60. package/dist/runtime/ts-property-names.js +16 -0
  61. package/dist/runtime/workspace-inventory.d.ts +33 -7
  62. package/dist/runtime/workspace-inventory.js +94 -41
  63. package/package.json +11 -1
@@ -0,0 +1,135 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ /**
4
+ * Environment variable that disables external template cache reads and writes.
5
+ *
6
+ * Set to `0`, `false`, `no`, or `off` to bypass the cache.
7
+ */
8
+ export const EXTERNAL_TEMPLATE_CACHE_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE';
9
+ /**
10
+ * Environment variable that overrides the external template cache root.
11
+ */
12
+ export const EXTERNAL_TEMPLATE_CACHE_DIR_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR';
13
+ /**
14
+ * Environment variable that enables TTL-based external template cache pruning.
15
+ *
16
+ * Unset, empty, zero, negative, and non-numeric values keep pruning disabled.
17
+ */
18
+ export const EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_TTL_DAYS';
19
+ /**
20
+ * Environment variable that overrides how often TTL pruning may scan the cache.
21
+ *
22
+ * Unset values use the default interval. Zero, negative, and non-numeric values
23
+ * disable scan throttling.
24
+ */
25
+ export const EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS';
26
+ /**
27
+ * Milliseconds in one TTL day.
28
+ */
29
+ const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
30
+ /**
31
+ * Default minimum interval between full external template cache prune scans.
32
+ */
33
+ const DEFAULT_CACHE_PRUNE_INTERVAL_MS = 60 * 60 * 1000;
34
+ /**
35
+ * Normalized environment values that disable the cache.
36
+ */
37
+ const DISABLED_CACHE_VALUES = new Set(['0', 'false', 'no', 'off']);
38
+ /**
39
+ * Checks whether remote external template source caching is enabled.
40
+ *
41
+ * Caching is enabled by default. Set `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE` to
42
+ * `0`, `false`, `no`, or `off` to force uncached resolution.
43
+ *
44
+ * @param env Environment object to inspect, defaulting to `process.env`.
45
+ * @returns Whether external template source cache reads and writes are enabled.
46
+ */
47
+ export function isExternalTemplateCacheEnabled(env = process.env) {
48
+ const rawValue = env[EXTERNAL_TEMPLATE_CACHE_ENV];
49
+ if (rawValue === undefined) {
50
+ return true;
51
+ }
52
+ return !DISABLED_CACHE_VALUES.has(rawValue.trim().toLowerCase());
53
+ }
54
+ /**
55
+ * Resolves the external template source cache root directory.
56
+ *
57
+ * `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR` overrides the location. Without an
58
+ * override, wp-typia uses a per-user `wp-typia-template-source-cache-*`
59
+ * directory inside the operating system temp directory.
60
+ *
61
+ * @param env Environment object to inspect, defaulting to `process.env`.
62
+ * @returns Absolute cache root directory path.
63
+ */
64
+ export function getExternalTemplateCacheRoot(env = process.env) {
65
+ const configuredCacheDir = env[EXTERNAL_TEMPLATE_CACHE_DIR_ENV]?.trim();
66
+ if (configuredCacheDir) {
67
+ return path.resolve(configuredCacheDir);
68
+ }
69
+ return path.join(os.tmpdir(), `wp-typia-template-source-cache-${getCurrentUserCacheSegment()}`);
70
+ }
71
+ function parseExternalTemplateCacheTtlDays(value) {
72
+ if (typeof value !== 'string' && typeof value !== 'number') {
73
+ return null;
74
+ }
75
+ const ttlDays = typeof value === 'number' ? value : Number(value.trim());
76
+ if (!Number.isFinite(ttlDays) || ttlDays <= 0) {
77
+ return null;
78
+ }
79
+ return ttlDays;
80
+ }
81
+ export function resolveExternalTemplateCacheTtlMs(options = {}) {
82
+ const env = options.env ?? process.env;
83
+ const ttlDays = options.ttlDays === undefined
84
+ ? parseExternalTemplateCacheTtlDays(env[EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV])
85
+ : parseExternalTemplateCacheTtlDays(options.ttlDays);
86
+ if (ttlDays === null) {
87
+ return null;
88
+ }
89
+ const ttlMs = ttlDays * MILLISECONDS_PER_DAY;
90
+ return Number.isFinite(ttlMs) ? ttlMs : null;
91
+ }
92
+ function parseExternalTemplateCachePruneIntervalMs(value) {
93
+ if (typeof value !== 'string' && typeof value !== 'number') {
94
+ return null;
95
+ }
96
+ const intervalMs = typeof value === 'number' ? value : Number(value.trim());
97
+ if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
98
+ return null;
99
+ }
100
+ return intervalMs;
101
+ }
102
+ export function resolveExternalTemplateCachePruneIntervalMs(options = {}) {
103
+ if (options.pruneIntervalMs !== undefined) {
104
+ return parseExternalTemplateCachePruneIntervalMs(options.pruneIntervalMs);
105
+ }
106
+ const env = options.env ?? process.env;
107
+ const envValue = env[EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV];
108
+ if (envValue === undefined) {
109
+ return DEFAULT_CACHE_PRUNE_INTERVAL_MS;
110
+ }
111
+ return parseExternalTemplateCachePruneIntervalMs(envValue);
112
+ }
113
+ export function getExternalTemplateCacheNowMs(now) {
114
+ const nowMs = now instanceof Date
115
+ ? now.getTime()
116
+ : typeof now === 'number'
117
+ ? now
118
+ : Date.now();
119
+ return Number.isFinite(nowMs) ? nowMs : Date.now();
120
+ }
121
+ function getCurrentUserCacheSegment() {
122
+ if (typeof process.getuid === 'function') {
123
+ return String(process.getuid());
124
+ }
125
+ try {
126
+ const safeUsername = os
127
+ .userInfo()
128
+ .username.trim()
129
+ .replace(/[^A-Za-z0-9._-]+/gu, '-');
130
+ return safeUsername.length > 0 ? safeUsername : 'user';
131
+ }
132
+ catch {
133
+ return 'user';
134
+ }
135
+ }
@@ -1,26 +1,4 @@
1
- /**
2
- * Environment variable that disables external template cache reads and writes.
3
- *
4
- * Set to `0`, `false`, `no`, or `off` to bypass the cache.
5
- */
6
- export declare const EXTERNAL_TEMPLATE_CACHE_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE";
7
- /**
8
- * Environment variable that overrides the external template cache root.
9
- */
10
- export declare const EXTERNAL_TEMPLATE_CACHE_DIR_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR";
11
- /**
12
- * Environment variable that enables TTL-based external template cache pruning.
13
- *
14
- * Unset, empty, zero, negative, and non-numeric values keep pruning disabled.
15
- */
16
- export declare const EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_TTL_DAYS";
17
- /**
18
- * Environment variable that overrides how often TTL pruning may scan the cache.
19
- *
20
- * Unset values use the default interval. Zero, negative, and non-numeric values
21
- * disable scan throttling.
22
- */
23
- export declare const EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS";
1
+ export { EXTERNAL_TEMPLATE_CACHE_DIR_ENV, EXTERNAL_TEMPLATE_CACHE_ENV, EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV, EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV, getExternalTemplateCacheRoot, isExternalTemplateCacheEnabled, } from './template-source-cache-policy.js';
24
2
  /**
25
3
  * Serializable metadata recorded in the cache marker for diagnostics.
26
4
  */
@@ -125,27 +103,6 @@ export interface ExternalTemplateCachePruneResult {
125
103
  */
126
104
  ttlMs: number | null;
127
105
  }
128
- /**
129
- * Checks whether remote external template source caching is enabled.
130
- *
131
- * Caching is enabled by default. Set `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE` to
132
- * `0`, `false`, `no`, or `off` to force uncached resolution.
133
- *
134
- * @param env Environment object to inspect, defaulting to `process.env`.
135
- * @returns Whether external template source cache reads and writes are enabled.
136
- */
137
- export declare function isExternalTemplateCacheEnabled(env?: NodeJS.ProcessEnv): boolean;
138
- /**
139
- * Resolves the external template source cache root directory.
140
- *
141
- * `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR` overrides the location. Without an
142
- * override, wp-typia uses a per-user `wp-typia-template-source-cache-*`
143
- * directory inside the operating system temp directory.
144
- *
145
- * @param env Environment object to inspect, defaulting to `process.env`.
146
- * @returns Absolute cache root directory path.
147
- */
148
- export declare function getExternalTemplateCacheRoot(env?: NodeJS.ProcessEnv): string;
149
106
  /**
150
107
  * Creates a deterministic cache key from source identity and integrity parts.
151
108
  *
@@ -187,4 +144,3 @@ export declare function findReusableExternalTemplateSourceCache(descriptor: Exte
187
144
  * @returns Cache resolution details, or `null` when caching is disabled.
188
145
  */
189
146
  export declare function resolveExternalTemplateSourceCache(descriptor: ExternalTemplateCacheDescriptor, populateSourceDir: (sourceDir: string) => Promise<void>): Promise<ExternalTemplateCacheResolution | null>;
190
- export {};
@@ -1,31 +1,9 @@
1
- import { createHash } from 'node:crypto';
2
- import fs from 'node:fs';
1
+ import { createHash, randomUUID } from 'node:crypto';
3
2
  import { promises as fsp } from 'node:fs';
4
- import os from 'node:os';
5
3
  import path from 'node:path';
6
- /**
7
- * Environment variable that disables external template cache reads and writes.
8
- *
9
- * Set to `0`, `false`, `no`, or `off` to bypass the cache.
10
- */
11
- export const EXTERNAL_TEMPLATE_CACHE_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE';
12
- /**
13
- * Environment variable that overrides the external template cache root.
14
- */
15
- export const EXTERNAL_TEMPLATE_CACHE_DIR_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR';
16
- /**
17
- * Environment variable that enables TTL-based external template cache pruning.
18
- *
19
- * Unset, empty, zero, negative, and non-numeric values keep pruning disabled.
20
- */
21
- export const EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_TTL_DAYS';
22
- /**
23
- * Environment variable that overrides how often TTL pruning may scan the cache.
24
- *
25
- * Unset values use the default interval. Zero, negative, and non-numeric values
26
- * disable scan throttling.
27
- */
28
- export const EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV = 'WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS';
4
+ import { getNodeErrorCode, pathExists } from './fs-async.js';
5
+ import { getExternalTemplateCacheNowMs, getExternalTemplateCacheRoot, isExternalTemplateCacheEnabled, resolveExternalTemplateCachePruneIntervalMs, resolveExternalTemplateCacheTtlMs, } from './template-source-cache-policy.js';
6
+ export { EXTERNAL_TEMPLATE_CACHE_DIR_ENV, EXTERNAL_TEMPLATE_CACHE_ENV, EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV, EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV, getExternalTemplateCacheRoot, isExternalTemplateCacheEnabled, } from './template-source-cache-policy.js';
29
7
  /**
30
8
  * Marker file written after a cache entry is fully populated.
31
9
  */
@@ -34,14 +12,6 @@ const CACHE_MARKER_FILE = 'wp-typia-template-cache.json';
34
12
  * Marker file written after a full TTL prune scan completes.
35
13
  */
36
14
  const CACHE_PRUNE_MARKER_FILE = 'wp-typia-template-cache-prune.json';
37
- /**
38
- * Milliseconds in one TTL day.
39
- */
40
- const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
41
- /**
42
- * Default minimum interval between full external template cache prune scans.
43
- */
44
- const DEFAULT_CACHE_PRUNE_INTERVAL_MS = 60 * 60 * 1000;
45
15
  /**
46
16
  * Private directory mode used for cache roots and entries on POSIX platforms.
47
17
  */
@@ -50,10 +20,6 @@ const PRIVATE_CACHE_DIRECTORY_MODE = 0o700;
50
20
  * Marker value used when URL-like metadata cannot be safely normalized.
51
21
  */
52
22
  const REDACTED_CACHE_METADATA_VALUE = '[redacted]';
53
- /**
54
- * Normalized environment values that disable the cache.
55
- */
56
- const DISABLED_CACHE_VALUES = new Set(['0', 'false', 'no', 'off']);
57
23
  /**
58
24
  * Filesystem errors that mean another writer published the same cache entry.
59
25
  */
@@ -80,80 +46,10 @@ const SAFE_CACHE_NAMESPACE_SEGMENT = /^[A-Za-z0-9_.-]+$/u;
80
46
  * Cache entries are deterministic SHA-256 digest directory names.
81
47
  */
82
48
  const SAFE_CACHE_ENTRY_SEGMENT = /^[a-f0-9]{64}$/u;
83
- /**
84
- * Checks whether remote external template source caching is enabled.
85
- *
86
- * Caching is enabled by default. Set `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE` to
87
- * `0`, `false`, `no`, or `off` to force uncached resolution.
88
- *
89
- * @param env Environment object to inspect, defaulting to `process.env`.
90
- * @returns Whether external template source cache reads and writes are enabled.
91
- */
92
- export function isExternalTemplateCacheEnabled(env = process.env) {
93
- const rawValue = env[EXTERNAL_TEMPLATE_CACHE_ENV];
94
- if (rawValue === undefined) {
95
- return true;
96
- }
97
- return !DISABLED_CACHE_VALUES.has(rawValue.trim().toLowerCase());
98
- }
99
- /**
100
- * Resolves the external template source cache root directory.
101
- *
102
- * `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR` overrides the location. Without an
103
- * override, wp-typia uses a per-user `wp-typia-template-source-cache-*`
104
- * directory inside the operating system temp directory.
105
- *
106
- * @param env Environment object to inspect, defaulting to `process.env`.
107
- * @returns Absolute cache root directory path.
108
- */
109
- export function getExternalTemplateCacheRoot(env = process.env) {
110
- const configuredCacheDir = env[EXTERNAL_TEMPLATE_CACHE_DIR_ENV]?.trim();
111
- if (configuredCacheDir) {
112
- return path.resolve(configuredCacheDir);
113
- }
114
- return path.join(os.tmpdir(), `wp-typia-template-source-cache-${getCurrentUserCacheSegment()}`);
115
- }
116
- function parseExternalTemplateCacheTtlDays(value) {
117
- if (typeof value !== 'string' && typeof value !== 'number') {
118
- return null;
119
- }
120
- const ttlDays = typeof value === 'number' ? value : Number(value.trim());
121
- if (!Number.isFinite(ttlDays) || ttlDays <= 0) {
122
- return null;
123
- }
124
- return ttlDays;
125
- }
126
- function resolveExternalTemplateCacheTtlMs(options = {}) {
127
- const env = options.env ?? process.env;
128
- const ttlDays = options.ttlDays === undefined
129
- ? parseExternalTemplateCacheTtlDays(env[EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV])
130
- : parseExternalTemplateCacheTtlDays(options.ttlDays);
131
- if (ttlDays === null) {
132
- return null;
133
- }
134
- const ttlMs = ttlDays * MILLISECONDS_PER_DAY;
135
- return Number.isFinite(ttlMs) ? ttlMs : null;
136
- }
137
- function parseExternalTemplateCachePruneIntervalMs(value) {
138
- if (typeof value !== 'string' && typeof value !== 'number') {
139
- return null;
140
- }
141
- const intervalMs = typeof value === 'number' ? value : Number(value.trim());
142
- if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
143
- return null;
144
- }
145
- return intervalMs;
146
- }
147
- function resolveExternalTemplateCachePruneIntervalMs(options = {}) {
148
- if (options.pruneIntervalMs !== undefined) {
149
- return parseExternalTemplateCachePruneIntervalMs(options.pruneIntervalMs);
150
- }
151
- const env = options.env ?? process.env;
152
- const envValue = env[EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV];
153
- if (envValue === undefined) {
154
- return DEFAULT_CACHE_PRUNE_INTERVAL_MS;
155
- }
156
- return parseExternalTemplateCachePruneIntervalMs(envValue);
49
+ function createTemporaryCacheEntryDirName(cacheKey) {
50
+ // Crypto randomness keeps concurrent cache populators from colliding on the
51
+ // staging directory before the final atomic rename publishes the cache entry.
52
+ return `.tmp-${cacheKey}-${process.pid}-${Date.now()}-${randomUUID()}`;
157
53
  }
158
54
  /**
159
55
  * Creates a deterministic cache key from source identity and integrity parts.
@@ -166,15 +62,6 @@ export function createExternalTemplateCacheKey(keyParts) {
166
62
  .update(JSON.stringify(keyParts))
167
63
  .digest('hex');
168
64
  }
169
- async function pathExists(filePath) {
170
- try {
171
- await fsp.access(filePath, fs.constants.F_OK);
172
- return true;
173
- }
174
- catch {
175
- return false;
176
- }
177
- }
178
65
  async function isDirectoryPath(directory) {
179
66
  try {
180
67
  const stats = await fsp.lstat(directory);
@@ -184,11 +71,6 @@ async function isDirectoryPath(directory) {
184
71
  return false;
185
72
  }
186
73
  }
187
- function getNodeErrorCode(error) {
188
- return typeof error === 'object' && error !== null && 'code' in error
189
- ? String(error.code)
190
- : '';
191
- }
192
74
  async function removeTemporaryCacheEntry(entryDir) {
193
75
  try {
194
76
  await fsp.rm(entryDir, { force: true, recursive: true });
@@ -197,21 +79,6 @@ async function removeTemporaryCacheEntry(entryDir) {
197
79
  // Cache cleanup is best-effort; the caller can still continue uncached.
198
80
  }
199
81
  }
200
- function getCurrentUserCacheSegment() {
201
- if (typeof process.getuid === 'function') {
202
- return String(process.getuid());
203
- }
204
- try {
205
- const safeUsername = os
206
- .userInfo()
207
- .username.trim()
208
- .replace(/[^A-Za-z0-9._-]+/gu, '-');
209
- return safeUsername.length > 0 ? safeUsername : 'user';
210
- }
211
- catch {
212
- return 'user';
213
- }
214
- }
215
82
  function getCurrentUid() {
216
83
  return typeof process.getuid === 'function' ? process.getuid() : null;
217
84
  }
@@ -360,14 +227,6 @@ async function readCacheEntryMarker(markerPath) {
360
227
  function cacheMetadataMatches(actual, expected) {
361
228
  return Object.entries(expected).every(([key, value]) => actual[key] === value);
362
229
  }
363
- function getExternalTemplateCacheNowMs(now) {
364
- const nowMs = now instanceof Date
365
- ? now.getTime()
366
- : typeof now === 'number'
367
- ? now
368
- : Date.now();
369
- return Number.isFinite(nowMs) ? nowMs : Date.now();
370
- }
371
230
  function isCacheEntryFreshForTtl(createdAtMs, nowMs, ttlMs) {
372
231
  return ttlMs === null || createdAtMs >= nowMs - ttlMs;
373
232
  }
@@ -673,9 +532,7 @@ export async function resolveExternalTemplateSourceCache(descriptor, populateSou
673
532
  if (existingMarker) {
674
533
  await removeCacheEntryWithinRoot(cacheRoot, entryDir);
675
534
  }
676
- const temporaryEntryDir = path.join(namespaceDir, `.tmp-${cacheKey}-${process.pid}-${Date.now()}-${Math.random()
677
- .toString(16)
678
- .slice(2)}`);
535
+ const temporaryEntryDir = path.join(namespaceDir, createTemporaryCacheEntryDirName(cacheKey));
679
536
  const temporarySourceDir = path.join(temporaryEntryDir, 'source');
680
537
  let populateFailed = false;
681
538
  try {
@@ -7,6 +7,9 @@ export declare const EXTERNAL_TEMPLATE_TRUST_WARNING = "External template config
7
7
  /**
8
8
  * Search a source directory for the first supported external template entry.
9
9
  *
10
+ * @deprecated Use `findExternalTemplateEntry()` from async template-source
11
+ * paths. This synchronous helper remains only for compatibility callers.
12
+ *
10
13
  * @param sourceDir Directory that may contain an external template config entry.
11
14
  * @returns The first matching entry path, or null when no supported entry exists.
12
15
  */
@@ -33,6 +33,9 @@ function resolveSourceSubpath(sourceDir, relativePath) {
33
33
  /**
34
34
  * Search a source directory for the first supported external template entry.
35
35
  *
36
+ * @deprecated Use `findExternalTemplateEntry()` from async template-source
37
+ * paths. This synchronous helper remains only for compatibility callers.
38
+ *
36
39
  * @param sourceDir Directory that may contain an external template config entry.
37
40
  * @returns The first matching entry path, or null when no supported entry exists.
38
41
  */
@@ -205,11 +208,11 @@ export async function renderCreateBlockExternalTemplate(sourceDir, context, requ
205
208
  const view = await buildExternalTemplateView(context, config, selectedVariant, variantConfig);
206
209
  const blockTemplateDir = resolveSourceSubpath(sourceDir, templatePath);
207
210
  await copyRenderedDirectory(blockTemplateDir, blockDir, view, {
208
- filter: (sourcePath, _destinationPath, entry) => {
211
+ filter: async (sourcePath, _destinationPath, entry) => {
209
212
  const mustacheVariantPath = path.join(path.dirname(sourcePath), `${entry.name}.mustache`);
210
213
  return !(entry.isFile() &&
211
214
  (entry.name === 'package.json' || entry.name === 'README.md') &&
212
- fs.existsSync(mustacheVariantPath));
215
+ (await pathExists(mustacheVariantPath)));
213
216
  },
214
217
  });
215
218
  const assetsPath = typeof variantConfig.assetsPath === 'string'
@@ -2,6 +2,9 @@ import type { ResolvedTemplateSource, SeedSource, TemplateVariableContext } from
2
2
  /**
3
3
  * Read a remote block source and return its default block category.
4
4
  *
5
+ * @deprecated Use `getDefaultCategoryAsync()` from async template-source paths.
6
+ * This synchronous helper remains only for compatibility callers.
7
+ *
5
8
  * @param sourceDir Block source directory that may contain a block.json file.
6
9
  * @returns The declared block category, or "widgets" when detection fails.
7
10
  */
@@ -16,6 +19,9 @@ export declare function getDefaultCategoryAsync(sourceDir: string): Promise<stri
16
19
  /**
17
20
  * Read `wpTypia.projectType` from a rendered or source template package
18
21
  * manifest and return it when present.
22
+ *
23
+ * @deprecated Use `getTemplateProjectTypeAsync()` from async template-source
24
+ * paths. This synchronous helper remains only for compatibility callers.
19
25
  */
20
26
  export declare function getTemplateProjectType(sourceDir: string): string | null;
21
27
  /**
@@ -59,6 +59,9 @@ async function readRemoteBlockJsonAsync(blockDir) {
59
59
  /**
60
60
  * Read a remote block source and return its default block category.
61
61
  *
62
+ * @deprecated Use `getDefaultCategoryAsync()` from async template-source paths.
63
+ * This synchronous helper remains only for compatibility callers.
64
+ *
62
65
  * @param sourceDir Block source directory that may contain a block.json file.
63
66
  * @returns The declared block category, or "widgets" when detection fails.
64
67
  */
@@ -139,6 +142,9 @@ async function readTemplatePackageJsonAsync(sourceDir) {
139
142
  /**
140
143
  * Read `wpTypia.projectType` from a rendered or source template package
141
144
  * manifest and return it when present.
145
+ *
146
+ * @deprecated Use `getTemplateProjectTypeAsync()` from async template-source
147
+ * paths. This synchronous helper remains only for compatibility callers.
142
148
  */
143
149
  export function getTemplateProjectType(sourceDir) {
144
150
  const packageJsonEntry = readTemplatePackageJson(sourceDir);
@@ -185,11 +191,11 @@ export async function normalizeWpTypiaTemplateSeed(seed) {
185
191
  const normalizedDir = path.join(tempRoot, 'template');
186
192
  try {
187
193
  await copyRawDirectory(seed.blockDir, normalizedDir, {
188
- filter: (sourcePath, _targetPath, entry) => {
194
+ filter: async (sourcePath, _targetPath, entry) => {
189
195
  const mustacheVariantPath = path.join(path.dirname(sourcePath), `${entry.name}.mustache`);
190
196
  return !(entry.isFile() &&
191
197
  (entry.name === 'package.json' || entry.name === 'README.md') &&
192
- fs.existsSync(mustacheVariantPath));
198
+ (await pathExists(mustacheVariantPath)));
193
199
  },
194
200
  });
195
201
  if (seed.assetsDir && (await pathExists(seed.assetsDir))) {
@@ -1,5 +1,18 @@
1
1
  import type { RemoteTemplateLocator, SeedSource } from './template-source-contracts.js';
2
+ /**
3
+ * Synchronously identify the bundled workspace template seed.
4
+ *
5
+ * This remains sync-only for compatibility callers. Async template resolution
6
+ * paths should use `isOfficialWorkspaceTemplateSeedAsync()`.
7
+ */
2
8
  export declare function isOfficialWorkspaceTemplateSeed(seed: SeedSource): boolean;
9
+ /**
10
+ * Asynchronously identify the bundled workspace template seed.
11
+ *
12
+ * @param seed Seed source to inspect.
13
+ * @returns Whether the seed resolves to the official workspace template package.
14
+ */
15
+ export declare function isOfficialWorkspaceTemplateSeedAsync(seed: SeedSource): Promise<boolean>;
3
16
  export declare function assertNoSymlinks(sourceDir: string): Promise<void>;
4
17
  /**
5
18
  * Resolves a template locator into a local seed source directory.
@@ -8,6 +8,7 @@ import { x as extractTarball } from 'tar';
8
8
  import { createExternalTemplateTimeoutError, fetchWithExternalTemplateTimeout, getExternalTemplateMetadataMaxBytes, getExternalTemplateTarballMaxBytes, getExternalTemplateTimeoutMs, readBufferResponseWithLimit, readJsonResponseWithLimit, } from './external-template-guards.js';
9
9
  import { findReusableExternalTemplateSourceCache, isExternalTemplateCacheEnabled, resolveExternalTemplateSourceCache, } from './template-source-cache.js';
10
10
  import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
11
+ import { pathExists } from './fs-async.js';
11
12
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, PROJECT_TOOLS_PACKAGE_ROOT, TEMPLATE_IDS, } from './template-registry.js';
12
13
  import { isPlainObject } from './object-utils.js';
13
14
  import { createManagedTempRoot } from './temp-roots.js';
@@ -184,7 +185,9 @@ async function fetchNpmTemplateSource(locator) {
184
185
  * Resolve a locally installed npm template package from the caller workspace.
185
186
  *
186
187
  * Bare package ids are preferred here so monorepo and offline workflows can
187
- * use an already-installed template without forcing a registry fetch.
188
+ * use an already-installed template without forcing a registry fetch. This
189
+ * path intentionally keeps Node's synchronous `require.resolve` semantics
190
+ * because the module resolver itself has no async equivalent.
188
191
  */
189
192
  function resolveInstalledNpmTemplateSource(locator, cwd) {
190
193
  if (locator.rawSpec !== '' && locator.rawSpec !== '*') {
@@ -244,6 +247,12 @@ function resolveInstalledNpmTemplateSource(locator, cwd) {
244
247
  throw error;
245
248
  }
246
249
  }
250
+ /**
251
+ * Synchronously identify the bundled workspace template seed.
252
+ *
253
+ * This remains sync-only for compatibility callers. Async template resolution
254
+ * paths should use `isOfficialWorkspaceTemplateSeedAsync()`.
255
+ */
247
256
  export function isOfficialWorkspaceTemplateSeed(seed) {
248
257
  const packageJsonPath = path.join(seed.rootDir, 'package.json');
249
258
  if (!fs.existsSync(packageJsonPath)) {
@@ -257,6 +266,25 @@ export function isOfficialWorkspaceTemplateSeed(seed) {
257
266
  return false;
258
267
  }
259
268
  }
269
+ /**
270
+ * Asynchronously identify the bundled workspace template seed.
271
+ *
272
+ * @param seed Seed source to inspect.
273
+ * @returns Whether the seed resolves to the official workspace template package.
274
+ */
275
+ export async function isOfficialWorkspaceTemplateSeedAsync(seed) {
276
+ const packageJsonPath = path.join(seed.rootDir, 'package.json');
277
+ if (!(await pathExists(packageJsonPath))) {
278
+ return false;
279
+ }
280
+ try {
281
+ const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, 'utf8'));
282
+ return packageJson.name === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE;
283
+ }
284
+ catch {
285
+ return false;
286
+ }
287
+ }
260
288
  export async function assertNoSymlinks(sourceDir) {
261
289
  const stats = await fsp.lstat(sourceDir);
262
290
  if (stats.isSymbolicLink()) {
@@ -300,13 +328,13 @@ function runGitTemplateCommand(args, label, options = {}) {
300
328
  function getGitHubTemplateRepositoryUrl(locator) {
301
329
  return `https://github.com/${locator.owner}/${locator.repo}.git`;
302
330
  }
303
- function resolveGitHubTemplateDirectory(checkoutDir, locator) {
331
+ async function resolveGitHubTemplateDirectory(checkoutDir, locator) {
304
332
  const sourceDir = path.resolve(checkoutDir, locator.sourcePath);
305
333
  const relativeSourceDir = path.relative(checkoutDir, sourceDir);
306
334
  if (relativeSourceDir.startsWith('..') || path.isAbsolute(relativeSourceDir)) {
307
335
  throw new Error('GitHub template path must stay within the cloned repository.');
308
336
  }
309
- if (!fs.existsSync(sourceDir)) {
337
+ if (!(await pathExists(sourceDir))) {
310
338
  throw new Error(`GitHub template path does not exist: ${locator.sourcePath}`);
311
339
  }
312
340
  return sourceDir;
@@ -438,7 +466,7 @@ async function reuseGitHubTemplateCacheByMetadata(locator) {
438
466
  if (!cachedSource) {
439
467
  return null;
440
468
  }
441
- const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
469
+ const sourceDir = await resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
442
470
  await assertNoSymlinks(sourceDir);
443
471
  return {
444
472
  blockDir: sourceDir,
@@ -482,12 +510,12 @@ async function resolveGitHubTemplateSource(locator) {
482
510
  namespace: 'github',
483
511
  }, async (checkoutDir) => {
484
512
  cloneGitHubTemplateSource(locator, checkoutDir);
485
- const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
513
+ const sourceDir = await resolveGitHubTemplateDirectory(checkoutDir, locator);
486
514
  await assertNoSymlinks(sourceDir);
487
515
  pinGitHubTemplateCacheRevision(locator, checkoutDir, resolvedCacheRevision);
488
516
  });
489
517
  if (cachedSource) {
490
- const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
518
+ const sourceDir = await resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
491
519
  await assertNoSymlinks(sourceDir);
492
520
  return {
493
521
  blockDir: sourceDir,
@@ -506,7 +534,7 @@ async function resolveGitHubTemplateSource(locator) {
506
534
  const checkoutDir = path.join(remoteRoot, 'source');
507
535
  try {
508
536
  cloneGitHubTemplateSource(locator, checkoutDir);
509
- const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
537
+ const sourceDir = await resolveGitHubTemplateDirectory(checkoutDir, locator);
510
538
  await assertNoSymlinks(sourceDir);
511
539
  return {
512
540
  blockDir: sourceDir,
@@ -530,7 +558,7 @@ async function resolveGitHubTemplateSource(locator) {
530
558
  export async function resolveTemplateSeed(locator, cwd) {
531
559
  if (locator.kind === 'path') {
532
560
  const sourceDir = path.resolve(cwd, locator.templatePath);
533
- if (!fs.existsSync(sourceDir)) {
561
+ if (!(await pathExists(sourceDir))) {
534
562
  throw new Error(`Template path does not exist: ${sourceDir}`);
535
563
  }
536
564
  await assertNoSymlinks(sourceDir);
@@ -2,7 +2,7 @@ import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from './tem
2
2
  import { resolveBuiltInTemplateSource } from './template-builtins.js';
3
3
  import { parseTemplateLocator, } from './template-source-locators.js';
4
4
  import { detectTemplateSourceFormat, getTemplateProjectTypeAsync, getDefaultCategoryAsync, getTemplateVariableContext, normalizeCreateBlockSubset, normalizeWpTypiaTemplateSeed, renderCreateBlockExternalTemplate, } from './template-source-normalization.js';
5
- import { isOfficialWorkspaceTemplateSeed, resolveTemplateSeed, } from './template-source-seeds.js';
5
+ import { isOfficialWorkspaceTemplateSeedAsync, resolveTemplateSeed, } from './template-source-seeds.js';
6
6
  import { assertBuiltInTemplateVariantAllowed, } from './cli-validation.js';
7
7
  import { getScaffoldTemplateVariableGroups } from './scaffold-template-variable-groups.js';
8
8
  export { parseGitHubTemplateLocator, parseNpmTemplateLocator, parseTemplateLocator, } from './template-source-locators.js';
@@ -27,7 +27,7 @@ export async function resolveTemplateSource(templateId, cwd, variables, variant)
27
27
  const context = getTemplateVariableContext(variables);
28
28
  const seed = await resolveTemplateSeed(locator, cwd);
29
29
  const isOfficialWorkspaceTemplate = templateId === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE ||
30
- isOfficialWorkspaceTemplateSeed(seed);
30
+ (await isOfficialWorkspaceTemplateSeedAsync(seed));
31
31
  let normalizedSeed = null;
32
32
  try {
33
33
  const format = await detectTemplateSourceFormat(seed.blockDir);
@@ -0,0 +1,11 @@
1
+ import ts from "typescript";
2
+ /**
3
+ * Extract the literal text for TypeScript property names this runtime supports.
4
+ *
5
+ * Computed property names are intentionally not resolved because their runtime
6
+ * values are not statically knowable from the syntax node alone.
7
+ *
8
+ * @param name TypeScript property name node.
9
+ * @returns Identifier, string-literal, or numeric-literal text; otherwise `null`.
10
+ */
11
+ export declare function getPropertyNameText(name: ts.PropertyName): string | null;