Preparación e inventario

Etapas 2 y 3. Prepara un release candidato a partir de un registro fijado, inspecciona el inventario de lo que es alcanzable y entiende cuándo un release está listo.

  • Disponibilidad: Planificado
  • Evidencia: Leído del código fuente
  • Referencia

Qué hace la preparación

Preparar un registro fijado crea un release candidato y un trabajo durable con cuatro etapas:

Etapa Trabajo
prepare Descarga todos los paquetes del grafo fijado que el CDN aún no tiene. Verifica el archivo contra la integridad fijada, y aplica los límites de tamaño comprimido, tamaño extraído y cantidad de entradas mientras descarga y extrae.
analyze Rastrea lo que es alcanzable desde las entradas de los targets y guarda el inventario. En esta etapa no se genera ninguna salida.
generate Pone en cola solo las salidas que faltan. Una salida compatible que un ámbito autorizado ya tiene se reutiliza sin ejecución nueva.
validate Comprueba que todo el cierre de entrega requerido sea durable y se pueda obtener. Solo entonces el release pasa a ready.

El release activo nunca cambia durante la preparación, sea cual sea el resultado.

Operaciones

Operación Solicitud Capacidad Reintento
registrations.prepare POST /v1/applications/{application}/registrations/{registration}/prepare release.prepare key
releases.inventory GET /v1/applications/{application}/releases/{release}/inventory application.read

Iniciar una preparación

El cuerpo es opcional:

Miembro Predeterminado Significado
diagnostics false Ejecuta los diagnósticos semánticos de TypeScript. Exige la habilitación diagnostics; sin ella la respuesta es 403 ENTITLEMENT_REQUIRED. Los errores de compilación se informan en cualquier caso.
note Hasta 500 caracteres

La respuesta es 202 con el release candidato y el job. Un reintento con el mismo Idempotency-Key responde el mismo par.

La admisión comprueba las cuotas, el crédito de la organización y el presupuesto global en conjunto, antes de que comience cualquier trabajo. Puede rechazar con QUOTA_EXCEEDED, CREDIT_INSUFFICIENT o BUDGET_EXHAUSTED. Un registro que no está en pinned responde 409 STATE_INVALID.

La secuencia completa, desde crear la aplicación hasta tener un release listo:

JavaScriptprepare-release.mjs
// Register an application, pin its dependency graph and prepare a candidate
// release. Every step is an explicit request: nothing here is triggered by a GET.
const api = process.env.CDN_API_ORIGIN;
const token = process.env.CDN_TOKEN;
const organization = process.env.CDN_ORGANIZATION;

// Reusing the label makes a retry of this script safe: the same Idempotency-Key
// with the same request answers the original result instead of repeating the work.
const label = process.env.RUN_LABEL ?? 'docs-example-0001';

async function call(method, path, body, key) {
	const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };
	if (key) headers['Idempotency-Key'] = `${label}.${key}`;

	const response = await fetch(new URL(path, api), { method, headers, body: body && JSON.stringify(body) });
	const document = response.status === 204 ? undefined : await response.json();
	if (!response.ok) throw new Error(`${method} ${path}: ${response.status} ${document.error.code}${document.error.message}`);
	return document;
}

async function finished(job) {
	const final = ['succeeded', 'failed', 'cancelled', 'limit_exceeded'];
	while (!final.includes(job.state)) {
		console.log(`  job ${job.id}: ${job.state}${job.stage ? ` (${job.stage})` : ''}${job.queue ? `, queue position ${job.queue.position}` : ''}`);
		await new Promise(resolve => setTimeout(resolve, 2000));
		job = await call('GET', `/v1/jobs/${job.id}`);
	}
	if (job.state !== 'succeeded') throw new Error(`job ${job.id} ended ${job.state}: ${job.failure.code}${job.failure.message}`);
	return job;
}

const application = await call('POST', `/v1/organizations/${organization}/applications`, { name: 'docs-example' }, 'application');
console.log(`application ${application.id}`);

// The entry is a public subpath of the package, never a source file
await call('PUT', `/v1/applications/${application.id}/targets/web`, {
	kind: 'frontend',
	package: '@example/app',
	selection: '1.0.0',
	entry: '.'
});

// Stage 1: register and resolve. Metadata only; no archive is downloaded.
const registered = await call('POST', `/v1/applications/${application.id}/registrations`, {}, 'registration');
await finished(registered.job);

// Stages 2 and 3: download, analyze, generate what is missing, validate the closure
const prepared = await call('POST', `/v1/applications/${application.id}/registrations/${registered.registration.id}/prepare`, {}, 'preparation');
await finished(prepared.job);

const release = await call('GET', `/v1/applications/${application.id}/releases/${prepared.release.id}`);
console.log(`release ${release.id} #${release.number}: ${release.state}, closure ${release.readiness.durable}/${release.readiness.required}`);

El inventario

El inventario está disponible en cuanto termina el análisis, antes de la generación y sin necesidad de ella. Enumera cada elemento alcanzable del release con su estado en el CDN.

Miembro del elemento Valores
kind module, style, asset
package, subpath Qué es el elemento
loading eager o lazy
targets Los targets que lo alcanzan
key La clave de compatibilidad de la salida
state available (ya existía una salida compatible autorizada), missing (en cola para generarse), generated, failed, limit_exceeded
artifact El digest de contenido de la salida, una vez que existe

counts totaliza los elementos por estado, y agrega unknown: la cantidad de imports dinámicos sin declarar.

Las claves de compatibilidad no son digests de contenido

La key de un elemento identifica la receta: el módulo, la integridad de sus fuentes, su porción de la resolución, la identidad del compilador con su versión y su configuración, las condiciones, el formato y el tipo de salida. El digest artifact identifica los bytes resultantes. La versión de un paquete por sí sola nunca basta para decidir que dos salidas son intercambiables.

La reutilización también respeta el acceso. Dos salidas técnicamente iguales se comparten solo cuando el consumidor está autorizado para el ámbito que las tiene. Las dependencias públicas siguen siendo públicas; las fuentes y salidas privadas se quedan dentro de su organización.

Los imports dinámicos y el límite del rastreo estático

La alcanzabilidad cubre módulos públicos de carga inmediata y diferida, estilos como módulos y recursos estáticos declarados. Proviene del rastreo estático, que no puede seguir un import cuyo destino se calcula en tiempo de ejecución.

Un import así se informa en unknown. Mientras counts.unknown sea mayor que cero el cierre nunca está completo, y el release no pasa a estar listo: el trabajo termina con DYNAMIC_IMPORT_UNKNOWN, antes de que se guarde nada.

Declara los destinos posibles para que el análisis pueda incluirlos: en el manifiesto de módulo del paquete o, para un paquete que no puedes modificar, en el miembro declared del registro. Un import declarado se sigue como una referencia de carga diferida y se informa como DYNAMIC_IMPORT_DECLARED.

Cuándo está listo

Un release está ready cuando su readiness informa complete: true, lo que significa que durable es igual a required: cada salida del cierre de entrega requerido está almacenada y se puede obtener. Un worker que terminó o un archivo JavaScript almacenado no es estar listo. Un recurso requerido que falta o una entrada sin resolver termina el trabajo con CLOSURE_INCOMPLETE.

Diagnósticos semánticos

Los errores esenciales de compilación siempre se informan, en todos los planes: un módulo que no compila falla con BUILD_FAILED y los diagnósticos de su compilador. Una habilitación nunca oculta un fallo que necesitas para entender por qué no resultó una compilación.

Los diagnósticos semánticos reales de TypeScript, con archivo, rango y código, son una capacidad premium. Su duración se mide en una categoría de consumo propia, diagnostics.