Enterprise édition
Stream : traitement de tâches documentaires
NextPDF\Enterprise\Stream\DocumentJobStreamProcessor transforme un flux de manifestes de rendu en résultats durables et traçables. Il consomme un iterable<RenderManifest> sous forme de générateur, effectue le rendu de fenêtres bornées à travers le moteur de rendu Pro, et finalise chaque tâche dans l’ordre de la source. Chaque tâche se termine dans exactement un état terminal : sortie validée, reconnue comme déjà validée, ou dead-lettered. La progression est enregistrée sur checkpoint, si bien qu’un run interrompu par un crash reprend sans rien republier.
L’histoire de Stream se répartit sur deux éditions, et cette répartition est délibérée. Pro fournit le moteur de rendu durable et concurrent ainsi que les stores locaux mono-hôte sur système de fichiers — la moitié en cours de processus. Enterprise fournit ce processeur de flux de tâches documentaires, plus les pièces qui franchissent les frontières entre hôtes : le committer de stockage d’objets (ObjectStorageCommitter) et l’outbox durable des événements terminaux (FilesystemOutboxEmitter). La page Pro Stream énonce la même frontière depuis son côté.
Disponibilité et licence
Section intitulée « Disponibilité et licence »Cette capacité est livrée dans NextPDF Enterprise (nextpdf/enterprise) et s’active avec une enveloppe de licence de niveau Enterprise. Un déploiement sans cette autorisation ne charge pas les classes de la capacité. Compare les éditions et obtiens une licence.
Installation
Section intitulée « Installation »composer require nextpdf/enterpriseLes classes de cette page se situent sous NextPDF\Enterprise\Stream et NextPDF\Enterprise\Stream\Storage. Elles consomment les contrats Pro figés de NextPDF\Pro\Stream — les interfaces de moteur, committer, checkpoint, idempotence, réessai et dead-letter.
Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »Le rôle du processeur relève de la sémantique de livraison, pas du rendu. Il regroupe le flux de manifestes en fenêtres d’offsets source pas plus grandes que la taille de lot du moteur. Chaque fenêtre est rendue à travers RenderEngineInterface::renderBatch(), avec un réessai borné et déterministe des timeouts par item. Ensuite, chaque item est finalisé dans l’ordre des offsets source vers un résultat terminal.
La frontière exactly-once s’applique par item, et elle est ancrée dans le committer, pas dans la coordination. La progression durable est un high-watermark d’offset en base 1 : tout offset inférieur ou égal au checkpoint a atteint un résultat terminal. L’ordre de la barrière est fixe : valider les octets, avancer le watermark, sauvegarder le checkpoint, purger les marques d’idempotence tamponnées, puis émettre les événements terminaux. Un crash entre la validation et le checkpoint revalide de façon idempotente à la reprise, car le committer compare les empreintes. Un crash après le checkpoint avance rapidement au-delà de l’offset, si bien que rien n’est publié deux fois.
ObjectStorageCommitter implémente le OutputCommitterInterface Pro face à un store d’objets à travers l’interface minimale ObjectStorageClientInterface. Le container de la cible est le bucket et sa key la clé d’objet. Revalider des octets identiques est un no-op comparé par empreinte. Des octets divergents sans overwrite déclenchent le conflit SPEC-COMMIT-409. Un objet neuf n’est jamais créé qu’avec l’écriture conditionnelle atomique putIfAbsent() ; perdre cette course déclenche une boucle bornée de relecture-et-résolution. L’exactly-once inter-writer tient donc exactement aussi loin que le putIfAbsent() de ton adaptateur est une véritable écriture conditionnelle — If-None-Match: * sur S3, ifGenerationMatch: 0 sur GCS. Ce cycle livre l’interface plus le NullObjectStorageClient en mémoire ; l’adaptateur S3/GCS en direct est fourni par l’hôte.
Les événements terminaux bouclent la boucle pour les systèmes en aval. Après la barrière du checkpoint, le processeur tente d’émettre un JobTerminalEvent pour chaque tâche finalisée — identifiants, statut, reçu, détails d’erreur, nombre de tentatives, et jamais aucun octet PDF. Avec un émetteur à callback simple, l’émission est at-most-once : les événements postérieurs à un checkpoint peuvent être sautés lors d’une reprise après crash. FilesystemOutboxEmitter rend chaque événement durable dès que emit() s’exécute : chaque événement est un fichier JSON atomique nommé d’après un hash de son eventId déterministe, si bien que réémettre après une reprise est idempotent, un relais livre at-least-once, et les consommateurs déduplicent sur eventId. Une frontière subsiste dans les deux cas : l’émission survient après la barrière du checkpoint, si bien qu’un crash entre checkpoint.save() et emit() saute l’événement terminal de cet item à la reprise. Les systèmes en aval qui exigent un registre d’événements complet devraient se réconcilier avec les objets validés (le store est la source de vérité), pas avec l’outbox seul.
La licence est câblée dans le chemin de sortie. La fabrique withBrandingFromLicense() résout une stratégie de marquage d’évaluation une fois par run à partir de la licence. Une licence payante se résout en une transformation identité. Une licence d’évaluation ou absente appose un filigrane sur chaque document validé, et un document qui ne peut pas être marqué est dead-lettered — le processeur ne valide jamais des octets d’évaluation sans marquage.
Pourquoi cela fonctionne ainsi
Section intitulée « Pourquoi cela fonctionne ainsi »La décision porteuse est que l’exactly-once repose sur l’objet du committer, créé conditionnellement et comparé par empreinte — pas sur des verrous distribués ni du consensus. L’écriture conditionnelle du store d’objets est la seule primitive atomique que la conception exige, et tout le reste a le droit d’échouer et de récupérer. Voilà pourquoi le moteur de rendu doit rester sans effet de bord, pourquoi l’état à clé et les caches de déduplication en cours de run sont traités comme des accélérations recalculables, et pourquoi une validation ambiguë interrompt le run plutôt que de deviner : le chemin de reprise converge à travers la même comparaison d’empreintes. C’est aussi pourquoi le writer unique par runId est une exigence énoncée plutôt qu’un bail imposé — le store de checkpoint reste délibérément simple, et la couche de validation reste le filet de sécurité.
Contexte de conception : Génération de documents à haut volume.
Surface d’API
Section intitulée « Surface d’API »DocumentJobStreamProcessor
Section intitulée « DocumentJobStreamProcessor »Les hôtes devraient construire via la fabrique, afin que le contrôle licence-vers-marquage ne soit jamais laissé non câblé :
public static function withBrandingFromLicense( RenderEngineInterface $engine, OutputCommitterInterface $committer, IdempotencyStoreInterface $idempotency, CheckpointStoreInterface $checkpoints, KeyedStateStoreInterface $state, DeadLetterStoreInterface $deadLetters, RetryPolicy $retryPolicy, ClockInterface $clock, EntitlementEvaluator $entitlementEvaluator, ?LicenseKey $license, ?StreamProcessorProbe $probe = null, ?JobCompletionEmitterInterface $emitter = null, ?BrandingApplicator $brandingApplicator = null,): self$clock est Symfony\Component\Clock\ClockInterface (le backoff de réessai s’endort à travers lui). Une licence null se résout fail-closed vers le marquage d’évaluation.
Le point d’entrée unique traite un run et retourne ses compteurs :
public function process(iterable $manifests, StreamProcessorConfig $config): ProcessingSummaryLève ou échoue avec : NextPDF\Enterprise\Stream\Exception\StreamProcessorException quand les préconditions de sûreté au crash échouent (un run crashSafe avec des collaborateurs non durables) ou quand une validation est ambiguë ; InvalidArgumentException quand windowSize dépasse le maxBatchSize() du moteur.
StreamProcessorConfig
Section intitulée « StreamProcessorConfig »public function __construct( public string $runId, int $windowSize = 32, int $checkpointIntervalJobs = 100, public bool $crashSafe = true, public bool $emitSkippedCompletions = false,)Lève ou échoue avec : InvalidArgumentException quand windowSize ou checkpointIntervalJobs est inférieur à 1. $runId est l’identifiant de run stable, à writer unique, qui sert de clé à la reprise sur checkpoint.
ObjectStorageCommitter
Section intitulée « ObjectStorageCommitter »public function __construct( private ObjectStorageClientInterface $client, private string $scheme, private ClockInterface $clock,) {}$scheme nomme le schéma cible que ce committer dessert (par exemple s3 ou gcs) ; $clock ici est Psr\Clock\ClockInterface.
public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceiptLève ou échoue avec : UnsupportedTargetException en cas de non-correspondance de schéma ; RenderManifestException quand la clé cible n’est pas sûre relativement au container ; CommitIntegrityException quand le sha-256 déclaré ne correspond pas aux octets ; OutputCommitConflictException (SPEC-COMMIT-409) sur des octets divergents sans overwrite ; RuntimeException quand la course à la création ne peut pas converger après 5 tentatives sous mutation concurrente.
ObjectStorageClientInterface
Section intitulée « ObjectStorageClientInterface »La surface d’adaptateur minimale qu’implémente une intégration S3/GCS en direct :
public function shaOf(string $bucket, string $key): ?string;
public function put(string $bucket, string $key, string $bytes, string $sha256): void;
public function putIfAbsent(string $bucket, string $key, string $bytes, string $sha256): bool;putIfAbsent() doit être une véritable création conditionnelle atomique (If-None-Match: * sur S3, ifGenerationMatch: 0 sur GCS) et retourne true uniquement quand cet appel a écrit l’objet. put() est l’écrasement inconditionnel utilisé uniquement quand le manifeste a demandé overwrite.
JobCompletionEmitterInterface et FilesystemOutboxEmitter
Section intitulée « JobCompletionEmitterInterface et FilesystemOutboxEmitter »public function emit(JobTerminalEvent $event): void;L’émetteur est optionnel sur le processeur. Les événements ne se déclenchent qu’après qu’un item est durablement finalisé. FilesystemOutboxEmitter est l’implémentation durable livrée :
public function __construct(string $directory, ?AtomicFileWriter $writer = null)Lève ou échoue avec : InvalidArgumentException quand le répertoire n’existe pas ; emit() lève RuntimeException si un événement ne peut pas être encodé en JSON. hasEvent(string $eventId): bool interroge l’outbox ; count(): int rapporte les événements non livrés.
JobTerminalEvent et JobTerminalStatus
Section intitulée « JobTerminalEvent et JobTerminalStatus »public function __construct( public string $eventId, public string $runId, public int $sourceOffset, public string $jobId, public string $idempotencyKeyValue, public JobTerminalStatus $status, public ?CommitReceipt $receipt, public ?string $errorCode, public ?string $errorMessage, public int $attempts, public DateTimeImmutable $occurredAt,) {}eventId est déterministe — runId:sourceOffset:idempotencyKey:status — ce qui est précisément ce qui rend possible la déduplication de l’outbox. toArray() sérialise l’événement pour le transport ; il ne porte aucun octet PDF. JobTerminalStatus est une énumération de chaîne : Committed (committed), DeadLettered (dead_lettered), Skipped (skipped).
ProcessingSummary
Section intitulée « ProcessingSummary »Compteurs immuables retournés par process() : runId, sourceRead, fastForwardedByCheckpoint, skippedByIdempotency, windows, renderBatchCalls, renderRetries, commitReceipts, deadLettered, checkpointSaves, et finalCommittedOffset (le high-watermark terminal final).
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »Validation d’objet exactly-once en isolation. Le NullObjectStorageClient en mémoire tient lieu de ton adaptateur S3/GCS ; la sémantique que tu observes est celle qu’un adaptateur en direct doit préserver.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Stream\Storage\NullObjectStorageClient;use NextPDF\Enterprise\Stream\Storage\ObjectStorageCommitter;use NextPDF\Manifest\OutputObjectKey;use NextPDF\Pro\Stream\Exception\OutputCommitConflictException;use Symfony\Component\Clock\NativeClock;
$committer = new ObjectStorageCommitter( client: new NullObjectStorageClient(), // swap in your S3/GCS adapter scheme: 's3', clock: new NativeClock(),);
$target = new OutputObjectKey(scheme: 's3', container: 'invoices', key: '2026/07/inv-1001.pdf');$bytes = '%PDF-1.7 example-rendered-bytes';$sha = hash('sha256', $bytes);
$first = $committer->commit('inv-1001', $target, $bytes, $sha);$replay = $committer->commit('inv-1001', $target, $bytes, $sha); // crash-resume replay
printf("first : reuse=%s, %d bytes\n", var_export($first->idempotentReuse, true), $first->bytesWritten);printf("replay: reuse=%s\n", var_export($replay->idempotentReuse, true));
try { $divergent = '%PDF-1.7 different-bytes'; $committer->commit('inv-1001', $target, $divergent, hash('sha256', $divergent));} catch (OutputCommitConflictException $conflict) { echo 'conflict: ' . $conflict->specCode() . "\n"; // no silent clobber}Sortie attendue :
first : reuse=false, 31 bytesreplay: reuse=trueconflict: SPEC-COMMIT-409Exemple de code — Production
Section intitulée « Exemple de code — Production »Un run complet sûr au crash : stores Pro durables, le committer de stockage d’objets, un outbox durable, et un marquage résolu par licence. Relancer le même runId après un crash avance rapidement et converge.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Licensing\EntitlementEvaluator;use NextPDF\Enterprise\Stream\DocumentJobStreamProcessor;use NextPDF\Enterprise\Stream\Exception\StreamProcessorException;use NextPDF\Enterprise\Stream\FilesystemOutboxEmitter;use NextPDF\Enterprise\Stream\Storage\ObjectStorageCommitter;use NextPDF\Enterprise\Stream\StreamProcessorConfig;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifest;use NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore;use NextPDF\Pro\Stream\Dedup\FilesystemIdempotencyStore;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;use NextPDF\Pro\Stream\Retry\FilesystemDeadLetterStore;use NextPDF\Pro\Stream\Retry\RetryPolicy;use NextPDF\Pro\Stream\State\InMemoryKeyedStateStore;use Symfony\Component\Clock\NativeClock;
// Production requires a host-supplied adapter whose putIfAbsent() is a TRUE// atomic conditional create (S3 If-None-Match: *, GCS ifGenerationMatch: 0)// and whose shaOf() reads durable object state. NullObjectStorageClient is// for the quick start only - it keeps nothing across processes.$s3Client = new \Aws\S3\S3Client(['region' => 'eu-central-1', 'version' => 'latest']);$objectClient = new \Acme\Storage\S3ObjectStorageClient($s3Client); // implements ObjectStorageClientInterface
$stateDir = '/var/lib/nextpdf/stream';foreach (['checkpoints', 'idempotency', 'dead-letters', 'outbox'] as $sub) { if (!is_dir($stateDir . '/' . $sub)) { mkdir($stateDir . '/' . $sub, 0770, true); }}
// One manifest per JSONL line; the generator never materialises the batch.$manifests = (static function (string $path): Generator { $handle = fopen($path, 'rb'); if ($handle === false) { throw new RuntimeException('Cannot open job stream: ' . $path); } try { while (($line = fgets($handle)) !== false) { if (trim($line) !== '') { yield RenderManifest::fromJson(trim($line)); } } } finally { fclose($handle); }})('/var/spool/nextpdf/jobs.jsonl');
$license = null; // your licensing bootstrap yields a LicenseKey; null = evaluation branding
$processor = DocumentJobStreamProcessor::withBrandingFromLicense( engine: new InProcessRenderEngine(SingleDocumentRenderer::standalone()), // For a live bucket, implement ObjectStorageClientInterface over your S3/GCS SDK. committer: new ObjectStorageCommitter($objectClient, 's3', new NativeClock()), idempotency: new FilesystemIdempotencyStore($stateDir . '/idempotency'), checkpoints: new FilesystemCheckpointStore($stateDir . '/checkpoints'), state: new InMemoryKeyedStateStore(), // recomputable; durability not required here deadLetters: new FilesystemDeadLetterStore($stateDir . '/dead-letters'), retryPolicy: new RetryPolicy(maxAttempts: 3, baseDelayMs: 200, maxDelayMs: 5_000), clock: new NativeClock(), entitlementEvaluator: new EntitlementEvaluator(), license: $license, emitter: new FilesystemOutboxEmitter($stateDir . '/outbox'),);
$config = new StreamProcessorConfig( runId: 'nightly-invoices-2026-07-03', windowSize: 32, checkpointIntervalJobs: 100, crashSafe: true,);
try { $summary = $processor->process($manifests, $config);} catch (StreamProcessorException $e) { // Ambiguous commit or a non-durable collaborator: the finalized prefix is // checkpointed. Re-run the SAME runId; the committer converges by digest. fwrite(STDERR, 'Run aborted for safe resume: ' . $e->getMessage() . PHP_EOL); exit(1);}
printf( "run %s: read=%d committed=%d dedup-skipped=%d dead-lettered=%d checkpoints=%d final-offset=%d\n", $summary->runId, $summary->sourceRead, $summary->commitReceipts, $summary->skippedByIdempotency, $summary->deadLettered, $summary->checkpointSaves, $summary->finalCommittedOffset,);Exemple de sortie (les compteurs dépendent de ton flux de tâches) :
run nightly-invoices-2026-07-03: read=1200 committed=1187 dedup-skipped=13 dead-lettered=0 checkpoints=12 final-offset=1200Cas limites et pièges
Section intitulée « Cas limites et pièges »- Le writer unique par
runIdest ta responsabilité. Le store de checkpoint n’a ni bail ni compare-and-swap. Deux writers concurrents sur un mêmerunIdsont hors contrat ; impose l’exclusivité dans ton ordonnanceur. crashSafe: trueéchoue vite sur des collaborateurs non durables. Les stores de committer, checkpoint, idempotence et dead-letter doivent tous implémenter le marqueurDurableCapability, sinonprocess()lèveStreamProcessorExceptionen nommant les fautifs. Le store d’état à clé est délibérément exempté : un état à clé perdu est recalculé depuis le checkpoint vers l’avant.windowSizedoit tenir dans le moteur. Une fenêtre plus grande quemaxBatchSize()lèveInvalidArgumentExceptionavant que le moindre travail ne démarre.- Une validation ambiguë interrompt ; un conflit non.
SPEC-COMMIT-409est un conflit terminal déterministe : l’item passe en dead-letter et le run continue. Toute autre défaillance de validation est ambiguë : le préfixe finalisé est enregistré sur checkpoint et le run lève pour une reprise sûre. - Les échecs de rendu n’interrompent jamais le run. Un résultat
Failedpar item, un budget de réessai épuisé, ou des octets d’évaluation non marquables passent tous cet item en dead-letter et continuent. - Les valeurs
jobIden double sont sûres ; le travail en double est indexé suridempotencyKey. Les résultats corrèlent aux items par offset source unique, jamais parjobId. Une clé d’idempotence en double est reconnue même au sein du même intervalle de barrière, avant tout re-rendu. - La durabilité de l’émetteur décide de la sémantique des événements. Un émetteur à callback simple est en observation seule et at-most-once à travers un crash.
FilesystemOutboxEmitterrend l’outbox durable et à clé de déduplication ; la livraison par relais est alors at-least-once, et l’exactly-once en aval requiert une déduplication côté consommateur sureventId. Son répertoire (comme celui de tout store sur système de fichiers) doit préexister, sinon le constructeur lèveInvalidArgumentException. - Les événements sautés sont désactivés par défaut. Positionne
emitSkippedCompletions: truepour émettre aussi un événement terminalSkippedpour les items court-circuités par déduplication.
Notes de sécurité
Section intitulée « Notes de sécurité »- Les clés de sortie échouent en mode fermé.
commit()réaffirme que la clé cible est sûre relativement au container : pas de traversée.., pas d’échappement absolu, pas d’octet nul, pas de schéma de stream-wrapper embarqué, et pas de deux-points (qui ferme le vecteur de flux de données alternatif NTFS). Les clés non sûres lèvent avant tout appel au stockage. - L’intégrité est revérifiée à la frontière. Le committer recalcule le sha-256 sur les octets réels et rejette une non-correspondance avec
CommitIntegrityException, si bien qu’un transfert corrompu ne peut pas atterrir silencieusement. - Les événements ne portent aucun contenu de document.
JobTerminalEventet les lignes de l’outbox ne contiennent qu’identifiants, empreintes, horodatages et chaînes d’erreur. Les messages d’erreur peuvent répercuter des diagnostics du moteur ; nettoie-les, ainsi que tout schéma dejobIdidentifiant un locataire, avant d’expédier des fichiers d’outbox vers des puits tiers. - La sortie d’évaluation n’est jamais publiée sans marquage. Quand le marquage est requis et ne peut pas être appliqué, l’item est dead-lettered plutôt que validé.
- L’exactly-once inter-writer n’est que aussi solide que ton adaptateur. Si
putIfAbsent()n’est pas une véritable écriture conditionnelle atomique, la garantie se dégrade en sémantique de writer unique. Les identifiants du store d’objets et la politique de bucket sont des préoccupations de l’hôte ; le module ne les gère jamais.
Conformité
Section intitulée « Conformité »Aucune norme publiée ne définit le comportement de ce module. Les garanties d’exactly-once, de checkpoint et d’outbox de cette page sont des contrats d’ingénierie de l’API NextPDF Enterprise, énoncés ici comme comportement observable de l’extérieur — elles ne constituent ni une conformité à, ni une certification par rapport à, une quelconque norme. L’usage interne de SHA-256 comme empreinte d’intégrité est de même de la plomberie, pas une revendication de conformité. Comme partout dans NextPDF : le support n’est pas la conformité, et la conformité n’est pas la certification. NextPDF ne détient aucune certification et n’en accorde aucune ; savoir si un déploiement construit sur ce module satisfait tes obligations réglementaires ou contractuelles est une détermination qui revient à tes évaluateurs.
Contrat de comportement
Section intitulée « Contrat de comportement »Les événements terminaux ne font pas partie de la transaction de checkpoint : l’émission s’exécute après checkpoint.save(), si bien que l’outbox conserve durablement chaque événement émis mais n’est pas un registre complet à travers les crashs. Les objets validés demeurent la source de vérité.
- Chaque offset source inférieur ou égal à
finalCommittedOffseta atteint exactement un résultat terminal :Committed,SkippedouDeadLettered. - Les items sont finalisés dans l’ordre des offsets source ; l’ordre de la barrière est validation, sauvegarde du checkpoint, purge des marques d’idempotence, puis émission des événements.
- Relancer un run avec le même
runIdne publie jamais deux fois : les offsets enregistrés sur checkpoint avancent rapidement, et les revalidations d’octets identiques sont des no-op comparés par empreinte avecidempotentReuse: true. - Un objet neuf n’est jamais créé qu’à travers la création conditionnelle atomique ; des octets divergents sur une clé occupée sans
overwritesont un dead-letterSPEC-COMMIT-409déterministe, jamais un écrasement. - Une validation ambiguë enregistre le préfixe finalisé sur checkpoint et interrompt avec
StreamProcessorException; l’offset en échec n’est pas avancé. - Un run
crashSaferefuse un committer, checkpoint, idempotence ou dead-letter non durable avant de lire la moindre entrée. - Les ids d’événement sont une fonction pure du run, de l’offset, de la clé d’idempotence et du statut, si bien qu’un outbox durable conserve au plus une ligne par événement.
Repli sur Core
Section intitulée « Repli sur Core »NextPDF Core effectue le rendu d’un document à la fois à travers le writer et le contrat de manifeste de rendu — voir Writer. Core seul n’a ni flux de tâches durables, ni reprise sur checkpoint, ni déduplication par idempotence, ni validation de stockage d’objets, ni outbox d’événements terminaux. NextPDF Pro ajoute le moteur de rendu durable et concurrent ainsi que les stores mono-hôte sur système de fichiers (Stream dans Pro). La moitié inter-hôtes — ce processeur, le committer de stockage d’objets, et l’outbox durable — requiert NextPDF Enterprise.
Frontière de publication
Section intitulée « Frontière de publication »Cette page documente uniquement le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins de namespace internes, les classes utilitaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de ticket sont hors périmètre.
Voir aussi
Section intitulée « Voir aussi »- Stream (Pro) — la moitié en cours de processus : moteur de rendu, exécuteurs et stores durables locaux.
- Stream — Référence approfondie — la référence au niveau contrat pour les interfaces Stream partagées.
- Output Pipeline (Enterprise) — orchestration de lots sur des manifestes de pipeline.
- Essai et marquage — comment le marquage d’évaluation est résolu et appliqué.
- Génération de documents à haut volume — le scénario pour lequel ce module existe.
- Exploiter NextPDF en production — posture de déploiement pour des workers de longue durée.