@wp-typia/project-tools 0.22.9 → 0.23.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 (90) hide show
  1. package/dist/runtime/ai-artifacts.js +3 -4
  2. package/dist/runtime/ai-feature-artifacts.js +2 -4
  3. package/dist/runtime/cli-add-collision.d.ts +25 -0
  4. package/dist/runtime/cli-add-collision.js +76 -0
  5. package/dist/runtime/cli-add-filesystem.js +2 -15
  6. package/dist/runtime/cli-add-help.js +11 -2
  7. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  8. package/dist/runtime/cli-add-kind-ids.js +3 -0
  9. package/dist/runtime/cli-add-types.d.ts +117 -0
  10. package/dist/runtime/cli-add-types.js +29 -1
  11. package/dist/runtime/cli-add-validation.d.ts +90 -1
  12. package/dist/runtime/cli-add-validation.js +304 -1
  13. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +74 -19
  14. package/dist/runtime/cli-add-workspace-admin-view-source.js +11 -2
  15. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +20 -2
  16. package/dist/runtime/cli-add-workspace-admin-view-templates.js +359 -3
  17. package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +21 -0
  18. package/dist/runtime/cli-add-workspace-admin-view-types.js +22 -0
  19. package/dist/runtime/cli-add-workspace-ai-anchors.js +121 -31
  20. package/dist/runtime/cli-add-workspace-contract-source-emitters.d.ts +15 -0
  21. package/dist/runtime/cli-add-workspace-contract-source-emitters.js +42 -0
  22. package/dist/runtime/cli-add-workspace-contract.d.ts +15 -0
  23. package/dist/runtime/cli-add-workspace-contract.js +65 -0
  24. package/dist/runtime/cli-add-workspace-integration-env.d.ts +24 -0
  25. package/dist/runtime/cli-add-workspace-integration-env.js +391 -0
  26. package/dist/runtime/cli-add-workspace-post-meta-anchors.d.ts +23 -0
  27. package/dist/runtime/cli-add-workspace-post-meta-anchors.js +244 -0
  28. package/dist/runtime/cli-add-workspace-post-meta-source-emitters.d.ts +63 -0
  29. package/dist/runtime/cli-add-workspace-post-meta-source-emitters.js +179 -0
  30. package/dist/runtime/cli-add-workspace-post-meta.d.ts +15 -0
  31. package/dist/runtime/cli-add-workspace-post-meta.js +107 -0
  32. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +1 -0
  33. package/dist/runtime/cli-add-workspace-rest-anchors.js +285 -21
  34. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +90 -2
  35. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +302 -29
  36. package/dist/runtime/cli-add-workspace-rest.d.ts +15 -2
  37. package/dist/runtime/cli-add-workspace-rest.js +329 -21
  38. package/dist/runtime/cli-add-workspace.d.ts +15 -0
  39. package/dist/runtime/cli-add-workspace.js +15 -0
  40. package/dist/runtime/cli-add.d.ts +1 -1
  41. package/dist/runtime/cli-add.js +1 -1
  42. package/dist/runtime/cli-core.d.ts +2 -1
  43. package/dist/runtime/cli-core.js +2 -1
  44. package/dist/runtime/cli-doctor-environment.js +1 -3
  45. package/dist/runtime/cli-doctor-workspace-features.js +128 -10
  46. package/dist/runtime/cli-doctor-workspace-package.d.ts +25 -3
  47. package/dist/runtime/cli-doctor-workspace-package.js +35 -13
  48. package/dist/runtime/cli-doctor-workspace-shared.d.ts +2 -0
  49. package/dist/runtime/cli-doctor-workspace-shared.js +5 -0
  50. package/dist/runtime/cli-doctor-workspace.d.ts +1 -1
  51. package/dist/runtime/cli-doctor-workspace.js +16 -8
  52. package/dist/runtime/cli-doctor.js +1 -1
  53. package/dist/runtime/cli-help.js +7 -0
  54. package/dist/runtime/cli-init-templates.js +11 -1
  55. package/dist/runtime/contract-artifacts.d.ts +14 -0
  56. package/dist/runtime/contract-artifacts.js +15 -0
  57. package/dist/runtime/fs-async.d.ts +7 -0
  58. package/dist/runtime/fs-async.js +11 -2
  59. package/dist/runtime/index.d.ts +1 -1
  60. package/dist/runtime/index.js +1 -1
  61. package/dist/runtime/migration-maintenance-verify.js +3 -0
  62. package/dist/runtime/migration-render-generated.js +4 -0
  63. package/dist/runtime/package-versions.js +3 -7
  64. package/dist/runtime/rest-resource-artifacts.d.ts +57 -1
  65. package/dist/runtime/rest-resource-artifacts.js +97 -1
  66. package/dist/runtime/scaffold-repository-reference.js +3 -7
  67. package/dist/runtime/template-render.d.ts +1 -1
  68. package/dist/runtime/template-render.js +1 -1
  69. package/dist/runtime/template-source-cache-markers.d.ts +37 -0
  70. package/dist/runtime/template-source-cache-markers.js +125 -0
  71. package/dist/runtime/template-source-cache.d.ts +1 -4
  72. package/dist/runtime/template-source-cache.js +16 -122
  73. package/dist/runtime/template-source-external.d.ts +4 -2
  74. package/dist/runtime/template-source-external.js +4 -2
  75. package/dist/runtime/template-source-remote.d.ts +8 -4
  76. package/dist/runtime/template-source-remote.js +8 -4
  77. package/dist/runtime/typia-llm.js +3 -4
  78. package/dist/runtime/workspace-inventory-mutations.d.ts +24 -0
  79. package/dist/runtime/workspace-inventory-mutations.js +181 -0
  80. package/dist/runtime/workspace-inventory-parser.d.ts +53 -0
  81. package/dist/runtime/workspace-inventory-parser.js +632 -0
  82. package/dist/runtime/workspace-inventory-read.d.ts +51 -0
  83. package/dist/runtime/workspace-inventory-read.js +98 -0
  84. package/dist/runtime/workspace-inventory-templates.d.ts +50 -0
  85. package/dist/runtime/workspace-inventory-templates.js +268 -0
  86. package/dist/runtime/workspace-inventory-types.d.ts +220 -0
  87. package/dist/runtime/workspace-inventory-types.js +1 -0
  88. package/dist/runtime/workspace-inventory.d.ts +5 -252
  89. package/dist/runtime/workspace-inventory.js +4 -928
  90. package/package.json +2 -2
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import path from 'node:path';
4
+ import { getOptionalNodeErrorCode } from './fs-async.js';
4
5
  import { PROJECT_TOOLS_PACKAGE_ROOT } from './template-registry.js';
5
6
  const require = createRequire(import.meta.url);
6
7
  const DEFAULT_VERSION_RANGE = '^0.0.0';
@@ -21,11 +22,6 @@ export const DEFAULT_WORDPRESS_DATA_VERSION = '^9.28.0';
21
22
  export const DEFAULT_WORDPRESS_DATAVIEWS_VERSION = '^14.1.0';
22
23
  export const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = '^0.1.1';
23
24
  let cachedPackageVersions = null;
24
- function getErrorCode(error) {
25
- return typeof error === 'object' && error !== null && 'code' in error
26
- ? String(error.code)
27
- : undefined;
28
- }
29
25
  function normalizeVersionRange(value) {
30
26
  const trimmed = value?.trim();
31
27
  if (!trimmed) {
@@ -82,7 +78,7 @@ function resolvePackageManifestLocation(packageJsonPath) {
82
78
  };
83
79
  }
84
80
  catch (error) {
85
- if (getErrorCode(error) === 'ENOENT') {
81
+ if (getOptionalNodeErrorCode(error) === 'ENOENT') {
86
82
  return {
87
83
  cacheKey: `missing-file:${packageJsonPath}`,
88
84
  packageJsonPath: null,
@@ -114,7 +110,7 @@ function resolveInstalledPackageManifestLocation(packageName) {
114
110
  return resolvePackageManifestLocation(require.resolve(`${packageName}/package.json`));
115
111
  }
116
112
  catch (error) {
117
- if (getErrorCode(error) === 'MODULE_NOT_FOUND') {
113
+ if (getOptionalNodeErrorCode(error) === 'MODULE_NOT_FOUND') {
118
114
  return {
119
115
  cacheKey: `missing-module:${packageName}`,
120
116
  packageJsonPath: null,
@@ -1,7 +1,8 @@
1
- import type { RestResourceMethodId } from "./cli-add-shared.js";
1
+ import type { ManualRestContractAuthId, ManualRestContractHttpMethodId, RestResourceMethodId } from "./cli-add-shared.js";
2
2
  interface RestResourceTemplateVariablesLike {
3
3
  namespace: string;
4
4
  pascalCase: string;
5
+ routePattern?: string;
5
6
  slugKebabCase: string;
6
7
  title: string;
7
8
  }
@@ -14,6 +15,26 @@ interface SyncRestResourceArtifactsOptions {
14
15
  validatorsFile: string;
15
16
  variables: RestResourceTemplateVariablesLike;
16
17
  }
18
+ interface ManualRestContractTemplateVariablesLike {
19
+ auth: ManualRestContractAuthId;
20
+ bodyTypeName?: string;
21
+ method: ManualRestContractHttpMethodId;
22
+ namespace: string;
23
+ pascalCase: string;
24
+ pathPattern: string;
25
+ queryTypeName: string;
26
+ responseTypeName: string;
27
+ slugKebabCase: string;
28
+ title: string;
29
+ }
30
+ interface SyncManualRestContractArtifactsOptions {
31
+ clientFile: string;
32
+ outputDir: string;
33
+ projectDir: string;
34
+ typesFile: string;
35
+ validatorsFile: string;
36
+ variables: ManualRestContractTemplateVariablesLike;
37
+ }
17
38
  /**
18
39
  * Build the endpoint manifest for a workspace-level REST resource scaffold.
19
40
  *
@@ -24,6 +45,33 @@ interface SyncRestResourceArtifactsOptions {
24
45
  export declare function buildRestResourceEndpointManifest(variables: RestResourceTemplateVariablesLike, methods: RestResourceMethodId[]): import("@wp-typia/block-runtime/metadata-core").EndpointManifestDefinition<Record<string, {
25
46
  sourceTypeName: string;
26
47
  }>, import("@wp-typia/block-runtime/schema-core").EndpointOpenApiEndpointDefinition[]>;
48
+ /**
49
+ * Build the endpoint manifest for a type-only manual REST contract. Manual
50
+ * contracts describe routes owned by another integration without generating PHP
51
+ * route glue in the workspace.
52
+ *
53
+ * @param variables Template naming data used for contract names, route path,
54
+ * and OpenAPI info.
55
+ * @returns Endpoint manifest consumed by schema, OpenAPI, and client generators.
56
+ */
57
+ export declare function buildManualRestContractEndpointManifest(variables: ManualRestContractTemplateVariablesLike): import("@wp-typia/block-runtime/metadata-core").EndpointManifestDefinition<Record<string, {
58
+ sourceTypeName: string;
59
+ }>, readonly [{
60
+ readonly wordpressAuth?: {
61
+ mechanism: "rest-nonce";
62
+ } | {
63
+ mechanism: "public-signed-token";
64
+ } | undefined;
65
+ readonly method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
66
+ readonly operationId: `call${string}ManualRestContract`;
67
+ readonly path: `/${string}${string}`;
68
+ readonly queryContract: "query";
69
+ readonly responseContract: "response";
70
+ readonly summary: `Call external ${string} REST route.`;
71
+ readonly tags: readonly [string];
72
+ readonly bodyContract?: string | undefined;
73
+ readonly auth: "authenticated" | "public" | "public-write-protected";
74
+ }]>;
27
75
  /**
28
76
  * Synchronize generated schemas, OpenAPI output, and endpoint client code for
29
77
  * a workspace-level REST resource scaffold.
@@ -32,4 +80,12 @@ export declare function buildRestResourceEndpointManifest(variables: RestResourc
32
80
  * @returns A promise that resolves after every generated REST artifact has been refreshed.
33
81
  */
34
82
  export declare function syncRestResourceArtifacts({ clientFile, methods, outputDir, projectDir, typesFile, validatorsFile, variables, }: SyncRestResourceArtifactsOptions): Promise<void>;
83
+ /**
84
+ * Synchronize generated schemas, OpenAPI output, and endpoint client code for a
85
+ * type-only manual REST contract.
86
+ *
87
+ * @param options Contract file paths and naming variables.
88
+ * @returns A promise that resolves after every generated artifact has refreshed.
89
+ */
90
+ export declare function syncManualRestContractArtifacts({ clientFile, outputDir, projectDir, typesFile, validatorsFile, variables, }: SyncManualRestContractArtifactsOptions): Promise<void>;
35
91
  export {};
@@ -1,5 +1,18 @@
1
1
  import path from "node:path";
2
2
  import { defineEndpointManifest, syncEndpointClient, syncRestOpenApi, syncTypeSchemas, } from "@wp-typia/block-runtime/metadata-core";
3
+ function resolveManualRestContractWordPressAuth(auth) {
4
+ if (auth === "authenticated") {
5
+ return {
6
+ mechanism: "rest-nonce",
7
+ };
8
+ }
9
+ if (auth === "public-write-protected") {
10
+ return {
11
+ mechanism: "public-signed-token",
12
+ };
13
+ }
14
+ return undefined;
15
+ }
3
16
  /**
4
17
  * Build the endpoint manifest for a workspace-level REST resource scaffold.
5
18
  *
@@ -9,7 +22,12 @@ import { defineEndpointManifest, syncEndpointClient, syncRestOpenApi, syncTypeSc
9
22
  */
10
23
  export function buildRestResourceEndpointManifest(variables, methods) {
11
24
  const basePath = `/${variables.namespace}/${variables.slugKebabCase}`;
12
- const itemPath = `${basePath}/item`;
25
+ const routePattern = variables.routePattern == null
26
+ ? `/${variables.slugKebabCase}/item`
27
+ : variables.routePattern.startsWith("/")
28
+ ? variables.routePattern
29
+ : `/${variables.routePattern}`;
30
+ const itemPath = `/${variables.namespace}${routePattern}`;
13
31
  const contracts = {};
14
32
  const endpoints = [];
15
33
  if (methods.includes("list")) {
@@ -124,6 +142,52 @@ export function buildRestResourceEndpointManifest(variables, methods) {
124
142
  },
125
143
  });
126
144
  }
145
+ /**
146
+ * Build the endpoint manifest for a type-only manual REST contract. Manual
147
+ * contracts describe routes owned by another integration without generating PHP
148
+ * route glue in the workspace.
149
+ *
150
+ * @param variables Template naming data used for contract names, route path,
151
+ * and OpenAPI info.
152
+ * @returns Endpoint manifest consumed by schema, OpenAPI, and client generators.
153
+ */
154
+ export function buildManualRestContractEndpointManifest(variables) {
155
+ const contracts = {
156
+ query: {
157
+ sourceTypeName: variables.queryTypeName,
158
+ },
159
+ response: {
160
+ sourceTypeName: variables.responseTypeName,
161
+ },
162
+ };
163
+ if (variables.bodyTypeName) {
164
+ contracts.request = {
165
+ sourceTypeName: variables.bodyTypeName,
166
+ };
167
+ }
168
+ const wordpressAuth = resolveManualRestContractWordPressAuth(variables.auth);
169
+ return defineEndpointManifest({
170
+ contracts,
171
+ endpoints: [
172
+ {
173
+ auth: variables.auth,
174
+ ...(variables.bodyTypeName ? { bodyContract: "request" } : {}),
175
+ method: variables.method,
176
+ operationId: `call${variables.pascalCase}ManualRestContract`,
177
+ path: `/${variables.namespace}${variables.pathPattern}`,
178
+ queryContract: "query",
179
+ responseContract: "response",
180
+ summary: `Call external ${variables.title} REST route.`,
181
+ tags: [variables.title],
182
+ ...(wordpressAuth ? { wordpressAuth } : {}),
183
+ },
184
+ ],
185
+ info: {
186
+ title: `${variables.title} Manual REST Contract`,
187
+ version: "1.0.0",
188
+ },
189
+ });
190
+ }
127
191
  /**
128
192
  * Synchronize generated schemas, OpenAPI output, and endpoint client code for
129
193
  * a workspace-level REST resource scaffold.
@@ -156,3 +220,35 @@ export async function syncRestResourceArtifacts({ clientFile, methods, outputDir
156
220
  validatorsFile,
157
221
  });
158
222
  }
223
+ /**
224
+ * Synchronize generated schemas, OpenAPI output, and endpoint client code for a
225
+ * type-only manual REST contract.
226
+ *
227
+ * @param options Contract file paths and naming variables.
228
+ * @returns A promise that resolves after every generated artifact has refreshed.
229
+ */
230
+ export async function syncManualRestContractArtifacts({ clientFile, outputDir, projectDir, typesFile, validatorsFile, variables, }) {
231
+ const manifest = buildManualRestContractEndpointManifest(variables);
232
+ for (const [baseName, contract] of Object.entries(manifest.contracts)) {
233
+ await syncTypeSchemas({
234
+ jsonSchemaFile: path.join(outputDir, "api-schemas", `${baseName}.schema.json`),
235
+ openApiFile: path.join(outputDir, "api-schemas", `${baseName}.openapi.json`),
236
+ projectRoot: projectDir,
237
+ sourceTypeName: contract.sourceTypeName,
238
+ typesFile,
239
+ });
240
+ }
241
+ await syncRestOpenApi({
242
+ manifest,
243
+ openApiFile: path.join(outputDir, "api.openapi.json"),
244
+ projectRoot: projectDir,
245
+ typesFile,
246
+ });
247
+ await syncEndpointClient({
248
+ clientFile,
249
+ manifest,
250
+ projectRoot: projectDir,
251
+ typesFile,
252
+ validatorsFile,
253
+ });
254
+ }
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import path from "node:path";
4
+ import { getOptionalNodeErrorCode } from "./fs-async.js";
4
5
  import { PROJECT_TOOLS_PACKAGE_ROOT } from "./template-registry.js";
5
6
  const require = createRequire(import.meta.url);
6
7
  /**
@@ -8,17 +9,12 @@ const require = createRequire(import.meta.url);
8
9
  * can be resolved from the current runtime package manifests.
9
10
  */
10
11
  export const DEFAULT_SCAFFOLD_REPOSITORY_REFERENCE = "imjlk/wp-typia";
11
- function getErrorCode(error) {
12
- return typeof error === "object" && error !== null && "code" in error
13
- ? String(error.code)
14
- : undefined;
15
- }
16
12
  function readRepositoryPackageManifest(packageJsonPath) {
17
13
  try {
18
14
  return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
19
15
  }
20
16
  catch (error) {
21
- if (getErrorCode(error) === "ENOENT") {
17
+ if (getOptionalNodeErrorCode(error) === "ENOENT") {
22
18
  return null;
23
19
  }
24
20
  throw error;
@@ -29,7 +25,7 @@ function resolveInstalledPackageManifestPath(packageName) {
29
25
  return require.resolve(`${packageName}/package.json`);
30
26
  }
31
27
  catch (error) {
32
- if (getErrorCode(error) === "MODULE_NOT_FOUND") {
28
+ if (getOptionalNodeErrorCode(error) === "MODULE_NOT_FOUND") {
33
29
  return null;
34
30
  }
35
31
  throw error;
@@ -55,4 +55,4 @@ export declare function copyInterpolatedDirectory(sourceDir: string, targetDir:
55
55
  * preview root.
56
56
  */
57
57
  export declare function listInterpolatedDirectoryOutputs(sourceDir: string, view: Record<string, string>): Promise<string[]>;
58
- export declare function pathExists(targetPath: string): boolean;
58
+ export declare function pathExistsSync(targetPath: string): boolean;
@@ -191,6 +191,6 @@ export async function listInterpolatedDirectoryOutputs(sourceDir, view) {
191
191
  });
192
192
  return outputs.sort((left, right) => left.localeCompare(right));
193
193
  }
194
- export function pathExists(targetPath) {
194
+ export function pathExistsSync(targetPath) {
195
195
  return fs.existsSync(targetPath);
196
196
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Marker file written after a cache entry is fully populated.
3
+ */
4
+ export declare const CACHE_MARKER_FILE = "wp-typia-template-cache.json";
5
+ /**
6
+ * Marker file written after a full TTL prune scan completes.
7
+ */
8
+ export declare const CACHE_PRUNE_MARKER_FILE = "wp-typia-template-cache-prune.json";
9
+ /**
10
+ * Serializable metadata recorded in cache markers for diagnostics.
11
+ */
12
+ export type ExternalTemplateCacheMetadata = Record<string, string | null>;
13
+ export interface ExternalTemplateCacheEntryMarker {
14
+ createdAtMs: number;
15
+ metadata: ExternalTemplateCacheMetadata;
16
+ }
17
+ export interface ExternalTemplateCachePruneMarker {
18
+ prunedAtMs: number;
19
+ pruneIntervalMs: number | null;
20
+ ttlMs: number;
21
+ }
22
+ export declare function sanitizeExternalTemplateCacheMetadata(metadata: ExternalTemplateCacheMetadata): ExternalTemplateCacheMetadata;
23
+ export declare function parseExternalTemplateCacheEntryMarker(markerText: string): ExternalTemplateCacheEntryMarker | null;
24
+ export declare function externalTemplateCacheMetadataMatches(actual: ExternalTemplateCacheMetadata, expected: ExternalTemplateCacheMetadata): boolean;
25
+ export declare function isExternalTemplateCacheEntryFreshForTtl(createdAtMs: number, nowMs: number, ttlMs: number | null): boolean;
26
+ export declare function parseExternalTemplateCachePruneMarker(markerText: string): ExternalTemplateCachePruneMarker | null;
27
+ export declare function formatExternalTemplateCacheEntryMarker({ cacheKey, createdAt, metadata, namespace, }: {
28
+ cacheKey: string;
29
+ createdAt: Date;
30
+ metadata: ExternalTemplateCacheMetadata;
31
+ namespace: string;
32
+ }): string;
33
+ export declare function formatExternalTemplateCachePruneMarker({ nowMs, pruneIntervalMs, ttlMs, }: {
34
+ nowMs: number;
35
+ pruneIntervalMs: number | null;
36
+ ttlMs: number;
37
+ }): string;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Marker file written after a cache entry is fully populated.
3
+ */
4
+ export const CACHE_MARKER_FILE = 'wp-typia-template-cache.json';
5
+ /**
6
+ * Marker file written after a full TTL prune scan completes.
7
+ */
8
+ export const CACHE_PRUNE_MARKER_FILE = 'wp-typia-template-cache-prune.json';
9
+ /**
10
+ * Marker value used when URL-like metadata cannot be safely normalized.
11
+ */
12
+ const REDACTED_CACHE_METADATA_VALUE = '[redacted]';
13
+ /**
14
+ * Metadata fields that may contain credentialed or signed URLs.
15
+ */
16
+ const URL_LIKE_METADATA_KEY = /(url|uri|registry|tarball)/iu;
17
+ function sanitizeExternalTemplateCacheMetadataValue(key, value) {
18
+ if (!URL_LIKE_METADATA_KEY.test(key)) {
19
+ return value;
20
+ }
21
+ try {
22
+ const url = new URL(value);
23
+ url.username = '';
24
+ url.password = '';
25
+ url.search = '';
26
+ url.hash = '';
27
+ return url.toString();
28
+ }
29
+ catch {
30
+ return REDACTED_CACHE_METADATA_VALUE;
31
+ }
32
+ }
33
+ export function sanitizeExternalTemplateCacheMetadata(metadata) {
34
+ return Object.fromEntries(Object.entries(metadata).map(([key, value]) => [
35
+ key,
36
+ value === null
37
+ ? null
38
+ : sanitizeExternalTemplateCacheMetadataValue(key, value),
39
+ ]));
40
+ }
41
+ export function parseExternalTemplateCacheEntryMarker(markerText) {
42
+ let marker;
43
+ try {
44
+ marker = JSON.parse(markerText);
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ if (typeof marker !== 'object' || marker === null || Array.isArray(marker)) {
50
+ return null;
51
+ }
52
+ const rawMetadata = marker.metadata;
53
+ if (typeof rawMetadata !== 'object' ||
54
+ rawMetadata === null ||
55
+ Array.isArray(rawMetadata)) {
56
+ return null;
57
+ }
58
+ const metadata = {};
59
+ for (const [key, value] of Object.entries(rawMetadata)) {
60
+ if (typeof value !== 'string' && value !== null) {
61
+ return null;
62
+ }
63
+ metadata[key] = value;
64
+ }
65
+ const rawCreatedAt = marker.createdAt;
66
+ const createdAtMs = typeof rawCreatedAt === 'string' ? Date.parse(rawCreatedAt) : 0;
67
+ return {
68
+ createdAtMs: Number.isFinite(createdAtMs) ? createdAtMs : 0,
69
+ metadata,
70
+ };
71
+ }
72
+ export function externalTemplateCacheMetadataMatches(actual, expected) {
73
+ return Object.entries(expected).every(([key, value]) => actual[key] === value);
74
+ }
75
+ export function isExternalTemplateCacheEntryFreshForTtl(createdAtMs, nowMs, ttlMs) {
76
+ return ttlMs === null || createdAtMs >= nowMs - ttlMs;
77
+ }
78
+ export function parseExternalTemplateCachePruneMarker(markerText) {
79
+ let marker;
80
+ try {
81
+ marker = JSON.parse(markerText);
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ if (typeof marker !== 'object' || marker === null || Array.isArray(marker)) {
87
+ return null;
88
+ }
89
+ const rawPrunedAt = marker.prunedAt;
90
+ const prunedAtMs = typeof rawPrunedAt === 'string' ? Date.parse(rawPrunedAt) : Number.NaN;
91
+ const rawPruneIntervalMs = marker
92
+ .pruneIntervalMs;
93
+ const rawTtlMs = marker.ttlMs;
94
+ if (typeof rawTtlMs !== 'number' || !Number.isFinite(rawTtlMs)) {
95
+ return null;
96
+ }
97
+ if (!Number.isFinite(prunedAtMs)) {
98
+ return null;
99
+ }
100
+ if (rawPruneIntervalMs !== null &&
101
+ (typeof rawPruneIntervalMs !== 'number' ||
102
+ !Number.isFinite(rawPruneIntervalMs))) {
103
+ return null;
104
+ }
105
+ return {
106
+ prunedAtMs,
107
+ pruneIntervalMs: rawPruneIntervalMs ?? null,
108
+ ttlMs: rawTtlMs,
109
+ };
110
+ }
111
+ export function formatExternalTemplateCacheEntryMarker({ cacheKey, createdAt, metadata, namespace, }) {
112
+ return `${JSON.stringify({
113
+ createdAt: createdAt.toISOString(),
114
+ key: cacheKey,
115
+ metadata: sanitizeExternalTemplateCacheMetadata(metadata),
116
+ namespace,
117
+ }, null, 2)}\n`;
118
+ }
119
+ export function formatExternalTemplateCachePruneMarker({ nowMs, pruneIntervalMs, ttlMs, }) {
120
+ return `${JSON.stringify({
121
+ prunedAt: new Date(nowMs).toISOString(),
122
+ pruneIntervalMs,
123
+ ttlMs,
124
+ }, null, 2)}\n`;
125
+ }
@@ -1,8 +1,5 @@
1
+ import { type ExternalTemplateCacheMetadata } from './template-source-cache-markers.js';
1
2
  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';
2
- /**
3
- * Serializable metadata recorded in the cache marker for diagnostics.
4
- */
5
- type ExternalTemplateCacheMetadata = Record<string, string | null>;
6
3
  /**
7
4
  * Describes a deterministic external template cache entry.
8
5
  *