@retinue/agentkit 0.1.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.
- package/LICENSE +22 -0
- package/README.md +310 -0
- package/dist/adapters/bullmq/consumer.d.ts +33 -0
- package/dist/adapters/bullmq/consumer.js +41 -0
- package/dist/adapters/bullmq/dispatcher.d.ts +74 -0
- package/dist/adapters/bullmq/dispatcher.js +160 -0
- package/dist/adapters/bullmq/export.d.ts +31 -0
- package/dist/adapters/bullmq/export.js +53 -0
- package/dist/adapters/bullmq/extraction.d.ts +42 -0
- package/dist/adapters/bullmq/extraction.js +63 -0
- package/dist/adapters/bullmq/index.d.ts +13 -0
- package/dist/adapters/bullmq/index.js +13 -0
- package/dist/adapters/bullmq/lock.d.ts +77 -0
- package/dist/adapters/bullmq/lock.js +126 -0
- package/dist/adapters/bullmq/queue.d.ts +50 -0
- package/dist/adapters/bullmq/queue.js +81 -0
- package/dist/adapters/memory/artifact-exports.d.ts +11 -0
- package/dist/adapters/memory/artifact-exports.js +102 -0
- package/dist/adapters/memory/artifacts.d.ts +15 -0
- package/dist/adapters/memory/artifacts.js +134 -0
- package/dist/adapters/memory/blobs.d.ts +7 -0
- package/dist/adapters/memory/blobs.js +27 -0
- package/dist/adapters/memory/evaluation.d.ts +18 -0
- package/dist/adapters/memory/evaluation.js +148 -0
- package/dist/adapters/memory/files.d.ts +27 -0
- package/dist/adapters/memory/files.js +0 -0
- package/dist/adapters/memory/flows.d.ts +16 -0
- package/dist/adapters/memory/flows.js +117 -0
- package/dist/adapters/memory/hitl.d.ts +9 -0
- package/dist/adapters/memory/hitl.js +130 -0
- package/dist/adapters/memory/idempotency.d.ts +13 -0
- package/dist/adapters/memory/idempotency.js +32 -0
- package/dist/adapters/memory/index.d.ts +39 -0
- package/dist/adapters/memory/index.js +107 -0
- package/dist/adapters/memory/knowledge.d.ts +43 -0
- package/dist/adapters/memory/knowledge.js +248 -0
- package/dist/adapters/memory/mcp.d.ts +9 -0
- package/dist/adapters/memory/mcp.js +37 -0
- package/dist/adapters/memory/message-store.d.ts +17 -0
- package/dist/adapters/memory/message-store.js +70 -0
- package/dist/adapters/memory/principal-memory.d.ts +7 -0
- package/dist/adapters/memory/principal-memory.js +83 -0
- package/dist/adapters/memory/runtime.d.ts +29 -0
- package/dist/adapters/memory/runtime.js +0 -0
- package/dist/adapters/memory/sessions.d.ts +29 -0
- package/dist/adapters/memory/sessions.js +0 -0
- package/dist/adapters/memory/skills.d.ts +10 -0
- package/dist/adapters/memory/skills.js +41 -0
- package/dist/adapters/memory/thread-summary.d.ts +7 -0
- package/dist/adapters/memory/thread-summary.js +29 -0
- package/dist/adapters/memory/usage-limits.d.ts +13 -0
- package/dist/adapters/memory/usage-limits.js +72 -0
- package/dist/adapters/memory/usage.d.ts +16 -0
- package/dist/adapters/memory/usage.js +279 -0
- package/dist/adapters/otel/index.d.ts +111 -0
- package/dist/adapters/otel/index.js +133 -0
- package/dist/adapters/postgres/artifact-exports.d.ts +12 -0
- package/dist/adapters/postgres/artifact-exports.js +117 -0
- package/dist/adapters/postgres/artifacts.d.ts +16 -0
- package/dist/adapters/postgres/artifacts.js +172 -0
- package/dist/adapters/postgres/checkpoint-store.d.ts +16 -0
- package/dist/adapters/postgres/checkpoint-store.js +34 -0
- package/dist/adapters/postgres/config.d.ts +15 -0
- package/dist/adapters/postgres/config.js +187 -0
- package/dist/adapters/postgres/conversation-store.d.ts +4 -0
- package/dist/adapters/postgres/conversation-store.js +82 -0
- package/dist/adapters/postgres/evaluation.d.ts +17 -0
- package/dist/adapters/postgres/evaluation.js +193 -0
- package/dist/adapters/postgres/file-content.d.ts +30 -0
- package/dist/adapters/postgres/file-content.js +111 -0
- package/dist/adapters/postgres/files.d.ts +19 -0
- package/dist/adapters/postgres/files.js +209 -0
- package/dist/adapters/postgres/flows.d.ts +20 -0
- package/dist/adapters/postgres/flows.js +206 -0
- package/dist/adapters/postgres/hitl.d.ts +5 -0
- package/dist/adapters/postgres/hitl.js +247 -0
- package/dist/adapters/postgres/index.d.ts +35 -0
- package/dist/adapters/postgres/index.js +35 -0
- package/dist/adapters/postgres/knowledge.d.ts +48 -0
- package/dist/adapters/postgres/knowledge.js +255 -0
- package/dist/adapters/postgres/memory.d.ts +14 -0
- package/dist/adapters/postgres/memory.js +194 -0
- package/dist/adapters/postgres/message-store.d.ts +11 -0
- package/dist/adapters/postgres/message-store.js +145 -0
- package/dist/adapters/postgres/migrations.d.ts +69 -0
- package/dist/adapters/postgres/migrations.js +1594 -0
- package/dist/adapters/postgres/pg-executor.d.ts +19 -0
- package/dist/adapters/postgres/pg-executor.js +32 -0
- package/dist/adapters/postgres/retention.d.ts +26 -0
- package/dist/adapters/postgres/retention.js +59 -0
- package/dist/adapters/postgres/rollups.d.ts +17 -0
- package/dist/adapters/postgres/rollups.js +267 -0
- package/dist/adapters/postgres/run-coordinator.d.ts +5 -0
- package/dist/adapters/postgres/run-coordinator.js +98 -0
- package/dist/adapters/postgres/run-event-log.d.ts +26 -0
- package/dist/adapters/postgres/run-event-log.js +30 -0
- package/dist/adapters/postgres/run-store.d.ts +4 -0
- package/dist/adapters/postgres/run-store.js +199 -0
- package/dist/adapters/postgres/schema.d.ts +39 -0
- package/dist/adapters/postgres/schema.js +70 -0
- package/dist/adapters/postgres/session-state.d.ts +7 -0
- package/dist/adapters/postgres/session-state.js +99 -0
- package/dist/adapters/postgres/sql.d.ts +8 -0
- package/dist/adapters/postgres/sql.js +2 -0
- package/dist/adapters/postgres/transaction.d.ts +37 -0
- package/dist/adapters/postgres/transaction.js +93 -0
- package/dist/adapters/postgres/unit-of-work.d.ts +18 -0
- package/dist/adapters/postgres/unit-of-work.js +8 -0
- package/dist/adapters/postgres/usage-limits.d.ts +15 -0
- package/dist/adapters/postgres/usage-limits.js +136 -0
- package/dist/adapters/postgres/usage.d.ts +15 -0
- package/dist/adapters/postgres/usage.js +226 -0
- package/dist/adapters/redis/index.d.ts +9 -0
- package/dist/adapters/redis/index.js +9 -0
- package/dist/adapters/redis/realtime.d.ts +74 -0
- package/dist/adapters/redis/realtime.js +112 -0
- package/dist/adapters/supabase/index.d.ts +88 -0
- package/dist/adapters/supabase/index.js +84 -0
- package/dist/adapters/supabase/realtime.d.ts +30 -0
- package/dist/adapters/supabase/realtime.js +53 -0
- package/dist/adapters/supabase/rls.d.ts +99 -0
- package/dist/adapters/supabase/rls.js +216 -0
- package/dist/adapters/supabase/storage.d.ts +50 -0
- package/dist/adapters/supabase/storage.js +207 -0
- package/dist/agents/agent.d.ts +66 -0
- package/dist/agents/agent.js +209 -0
- package/dist/agents/define.d.ts +21 -0
- package/dist/agents/define.js +63 -0
- package/dist/agents/engine.d.ts +98 -0
- package/dist/agents/engine.js +462 -0
- package/dist/agents/index.d.ts +50 -0
- package/dist/agents/index.js +17 -0
- package/dist/artifacts/index.d.ts +114 -0
- package/dist/artifacts/index.js +219 -0
- package/dist/authorization/index.d.ts +76 -0
- package/dist/authorization/index.js +76 -0
- package/dist/capabilities/index.d.ts +120 -0
- package/dist/capabilities/index.js +167 -0
- package/dist/capabilities/runtime.d.ts +89 -0
- package/dist/capabilities/runtime.js +84 -0
- package/dist/citations/index.d.ts +161 -0
- package/dist/citations/index.js +182 -0
- package/dist/context/assembler.d.ts +82 -0
- package/dist/context/assembler.js +129 -0
- package/dist/context/compaction.d.ts +45 -0
- package/dist/context/compaction.js +55 -0
- package/dist/context/index.d.ts +75 -0
- package/dist/context/index.js +17 -0
- package/dist/core/content-parts.d.ts +194 -0
- package/dist/core/content-parts.js +23 -0
- package/dist/core/context.d.ts +51 -0
- package/dist/core/context.js +9 -0
- package/dist/core/env.d.ts +25 -0
- package/dist/core/env.js +41 -0
- package/dist/core/errors.d.ts +30 -0
- package/dist/core/errors.js +65 -0
- package/dist/core/events.d.ts +139 -0
- package/dist/core/events.js +99 -0
- package/dist/core/ids.d.ts +52 -0
- package/dist/core/ids.js +13 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +9 -0
- package/dist/core/tokens.d.ts +22 -0
- package/dist/core/tokens.js +22 -0
- package/dist/core/validation.d.ts +34 -0
- package/dist/core/validation.js +176 -0
- package/dist/documents/extraction.d.ts +121 -0
- package/dist/documents/extraction.js +293 -0
- package/dist/documents/index.d.ts +199 -0
- package/dist/documents/index.js +65 -0
- package/dist/documents/parsers/pdf.d.ts +47 -0
- package/dist/documents/parsers/pdf.js +508 -0
- package/dist/documents/parsers/text.d.ts +59 -0
- package/dist/documents/parsers/text.js +325 -0
- package/dist/documents/read-tool.d.ts +52 -0
- package/dist/documents/read-tool.js +109 -0
- package/dist/documents/render.d.ts +29 -0
- package/dist/documents/render.js +59 -0
- package/dist/documents/vision.d.ts +159 -0
- package/dist/documents/vision.js +214 -0
- package/dist/entries/adapters-bullmq.d.ts +8 -0
- package/dist/entries/adapters-bullmq.js +8 -0
- package/dist/entries/adapters-otel.d.ts +13 -0
- package/dist/entries/adapters-otel.js +13 -0
- package/dist/entries/adapters-postgres.d.ts +10 -0
- package/dist/entries/adapters-postgres.js +10 -0
- package/dist/entries/adapters-redis.d.ts +3 -0
- package/dist/entries/adapters-redis.js +3 -0
- package/dist/entries/context.d.ts +20 -0
- package/dist/entries/context.js +20 -0
- package/dist/entries/flows.d.ts +15 -0
- package/dist/entries/flows.js +15 -0
- package/dist/entries/hitl.d.ts +10 -0
- package/dist/entries/hitl.js +10 -0
- package/dist/entries/knowledge.d.ts +18 -0
- package/dist/entries/knowledge.js +19 -0
- package/dist/entries/mcp.d.ts +10 -0
- package/dist/entries/mcp.js +10 -0
- package/dist/entries/observability.d.ts +14 -0
- package/dist/entries/observability.js +16 -0
- package/dist/entries/persistence.d.ts +11 -0
- package/dist/entries/persistence.js +11 -0
- package/dist/entries/providers.d.ts +14 -0
- package/dist/entries/providers.js +14 -0
- package/dist/entries/runtime.d.ts +13 -0
- package/dist/entries/runtime.js +13 -0
- package/dist/entries/server.d.ts +24 -0
- package/dist/entries/server.js +24 -0
- package/dist/entries/tools.d.ts +21 -0
- package/dist/entries/tools.js +21 -0
- package/dist/entries/usage.d.ts +10 -0
- package/dist/entries/usage.js +10 -0
- package/dist/evaluation/gate.d.ts +168 -0
- package/dist/evaluation/gate.js +180 -0
- package/dist/evaluation/graders.d.ts +125 -0
- package/dist/evaluation/graders.js +203 -0
- package/dist/evaluation/index.d.ts +120 -0
- package/dist/evaluation/index.js +183 -0
- package/dist/evaluation/judge.d.ts +75 -0
- package/dist/evaluation/judge.js +111 -0
- package/dist/export/index.d.ts +162 -0
- package/dist/export/index.js +363 -0
- package/dist/export/markdown.d.ts +19 -0
- package/dist/export/markdown.js +29 -0
- package/dist/export/pdf.d.ts +73 -0
- package/dist/export/pdf.js +407 -0
- package/dist/files/context.d.ts +97 -0
- package/dist/files/context.js +185 -0
- package/dist/files/index.d.ts +210 -0
- package/dist/files/index.js +338 -0
- package/dist/files/read-tool.d.ts +81 -0
- package/dist/files/read-tool.js +163 -0
- package/dist/files/turn-parts.d.ts +96 -0
- package/dist/files/turn-parts.js +171 -0
- package/dist/flows/index.d.ts +270 -0
- package/dist/flows/index.js +62 -0
- package/dist/flows/interpreter.d.ts +146 -0
- package/dist/flows/interpreter.js +426 -0
- package/dist/flows/runner.d.ts +145 -0
- package/dist/flows/runner.js +270 -0
- package/dist/graphql/index.d.ts +8 -0
- package/dist/graphql/index.js +8 -0
- package/dist/graphql/resolvers.d.ts +237 -0
- package/dist/graphql/resolvers.js +253 -0
- package/dist/graphql/schema.d.ts +11 -0
- package/dist/graphql/schema.js +258 -0
- package/dist/graphql/sse.d.ts +77 -0
- package/dist/graphql/sse.js +100 -0
- package/dist/hitl/approved-execution.d.ts +127 -0
- package/dist/hitl/approved-execution.js +177 -0
- package/dist/hitl/index.d.ts +79 -0
- package/dist/hitl/index.js +12 -0
- package/dist/hitl/service.d.ts +221 -0
- package/dist/hitl/service.js +268 -0
- package/dist/idempotency/index.d.ts +70 -0
- package/dist/idempotency/index.js +59 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +60 -0
- package/dist/knowledge/chunking.d.ts +57 -0
- package/dist/knowledge/chunking.js +158 -0
- package/dist/knowledge/index.d.ts +119 -0
- package/dist/knowledge/index.js +166 -0
- package/dist/knowledge/retrieval.d.ts +146 -0
- package/dist/knowledge/retrieval.js +170 -0
- package/dist/loadtest/harness.d.ts +168 -0
- package/dist/loadtest/harness.js +507 -0
- package/dist/loadtest/index.d.ts +13 -0
- package/dist/loadtest/index.js +13 -0
- package/dist/loadtest/injection.d.ts +89 -0
- package/dist/loadtest/injection.js +147 -0
- package/dist/loadtest/metrics.d.ts +197 -0
- package/dist/loadtest/metrics.js +160 -0
- package/dist/loadtest/runbooks.d.ts +28 -0
- package/dist/loadtest/runbooks.js +159 -0
- package/dist/loadtest/scenario.d.ts +104 -0
- package/dist/loadtest/scenario.js +208 -0
- package/dist/mcp/egress.d.ts +53 -0
- package/dist/mcp/egress.js +115 -0
- package/dist/mcp/index.d.ts +93 -0
- package/dist/mcp/index.js +33 -0
- package/dist/mcp/provider.d.ts +62 -0
- package/dist/mcp/provider.js +0 -0
- package/dist/models/index.d.ts +98 -0
- package/dist/models/index.js +74 -0
- package/dist/models/pricing.d.ts +24 -0
- package/dist/models/pricing.js +37 -0
- package/dist/models/provider-factory.d.ts +31 -0
- package/dist/models/provider-factory.js +67 -0
- package/dist/models/streaming.d.ts +145 -0
- package/dist/models/streaming.js +272 -0
- package/dist/models/vision.d.ts +38 -0
- package/dist/models/vision.js +62 -0
- package/dist/persistence/index.d.ts +1654 -0
- package/dist/persistence/index.js +226 -0
- package/dist/principal-memory/index.d.ts +106 -0
- package/dist/principal-memory/index.js +89 -0
- package/dist/retention/index.d.ts +89 -0
- package/dist/retention/index.js +70 -0
- package/dist/runtime/checkpoint.d.ts +37 -0
- package/dist/runtime/checkpoint.js +22 -0
- package/dist/runtime/index.d.ts +118 -0
- package/dist/runtime/index.js +69 -0
- package/dist/runtime/retry.d.ts +95 -0
- package/dist/runtime/retry.js +126 -0
- package/dist/runtime/serialization.d.ts +85 -0
- package/dist/runtime/serialization.js +95 -0
- package/dist/runtime/streaming.d.ts +54 -0
- package/dist/runtime/streaming.js +115 -0
- package/dist/runtime/worker.d.ts +130 -0
- package/dist/runtime/worker.js +405 -0
- package/dist/security/checklist.d.ts +53 -0
- package/dist/security/checklist.js +204 -0
- package/dist/security/findings.d.ts +56 -0
- package/dist/security/findings.js +168 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/index.js +14 -0
- package/dist/security/prompt-safety.d.ts +100 -0
- package/dist/security/prompt-safety.js +133 -0
- package/dist/server/boot.d.ts +32 -0
- package/dist/server/boot.js +36 -0
- package/dist/server/cli-worker.d.ts +37 -0
- package/dist/server/cli-worker.js +151 -0
- package/dist/server/cli.d.ts +27 -0
- package/dist/server/cli.js +74 -0
- package/dist/server/config.d.ts +42 -0
- package/dist/server/config.js +127 -0
- package/dist/server/health.d.ts +59 -0
- package/dist/server/health.js +90 -0
- package/dist/server/host.d.ts +39 -0
- package/dist/server/host.js +124 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.js +15 -0
- package/dist/server/main.d.ts +16 -0
- package/dist/server/main.js +31 -0
- package/dist/server/sse-route.d.ts +21 -0
- package/dist/server/sse-route.js +282 -0
- package/dist/skills/index.d.ts +67 -0
- package/dist/skills/index.js +31 -0
- package/dist/skills/resolver.d.ts +54 -0
- package/dist/skills/resolver.js +121 -0
- package/dist/teams/index.d.ts +93 -0
- package/dist/teams/index.js +207 -0
- package/dist/telemetry/index.d.ts +157 -0
- package/dist/telemetry/index.js +71 -0
- package/dist/telemetry/instrument.d.ts +108 -0
- package/dist/telemetry/instrument.js +232 -0
- package/dist/telemetry/log-events.d.ts +17 -0
- package/dist/telemetry/log-events.js +58 -0
- package/dist/telemetry/metrics.d.ts +123 -0
- package/dist/telemetry/metrics.js +135 -0
- package/dist/telemetry/noop.d.ts +39 -0
- package/dist/telemetry/noop.js +143 -0
- package/dist/telemetry/redaction.d.ts +64 -0
- package/dist/telemetry/redaction.js +153 -0
- package/dist/telemetry/spans.d.ts +56 -0
- package/dist/telemetry/spans.js +78 -0
- package/dist/telemetry/trace-context.d.ts +55 -0
- package/dist/telemetry/trace-context.js +60 -0
- package/dist/toolkit/compute.d.ts +53 -0
- package/dist/toolkit/compute.js +152 -0
- package/dist/toolkit/data.d.ts +98 -0
- package/dist/toolkit/data.js +235 -0
- package/dist/toolkit/http.d.ts +113 -0
- package/dist/toolkit/http.js +205 -0
- package/dist/toolkit/index.d.ts +21 -0
- package/dist/toolkit/index.js +17 -0
- package/dist/toolkit/web.d.ts +107 -0
- package/dist/toolkit/web.js +147 -0
- package/dist/tools/define.d.ts +25 -0
- package/dist/tools/define.js +45 -0
- package/dist/tools/delegating.d.ts +132 -0
- package/dist/tools/delegating.js +211 -0
- package/dist/tools/index.d.ts +129 -0
- package/dist/tools/index.js +33 -0
- package/dist/tools/library/compute.d.ts +11 -0
- package/dist/tools/library/compute.js +46 -0
- package/dist/tools/library/data.d.ts +16 -0
- package/dist/tools/library/data.js +92 -0
- package/dist/tools/library/http.d.ts +28 -0
- package/dist/tools/library/http.js +71 -0
- package/dist/tools/library/index.d.ts +97 -0
- package/dist/tools/library/index.js +134 -0
- package/dist/tools/library/knowledge.d.ts +39 -0
- package/dist/tools/library/knowledge.js +58 -0
- package/dist/tools/library/web.d.ts +19 -0
- package/dist/tools/library/web.js +65 -0
- package/dist/tools/meta-tools.d.ts +19 -0
- package/dist/tools/meta-tools.js +36 -0
- package/dist/tools/registry.d.ts +146 -0
- package/dist/tools/registry.js +291 -0
- package/dist/usage/index.d.ts +105 -0
- package/dist/usage/index.js +20 -0
- package/dist/usage/quota.d.ts +258 -0
- package/dist/usage/quota.js +510 -0
- package/dist/usage/recorder.d.ts +29 -0
- package/dist/usage/recorder.js +96 -0
- package/dist/usage/rollups.d.ts +121 -0
- package/dist/usage/rollups.js +157 -0
- package/dist/worker/export.d.ts +57 -0
- package/dist/worker/export.js +81 -0
- package/dist/worker/extraction.d.ts +57 -0
- package/dist/worker/extraction.js +84 -0
- package/dist/worker/main.d.ts +103 -0
- package/dist/worker/main.js +159 -0
- package/package.json +187 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The export pipeline (#134).
|
|
3
|
+
*
|
|
4
|
+
* The renderers turn blocks into bytes; this decides *when*, *where the bytes go*, and *what happens when a
|
|
5
|
+
* render fails* — and one thing the issue asked for differently, which is worth stating plainly.
|
|
6
|
+
*
|
|
7
|
+
* **An export is not an artifact version.** The issue's wording says a render should produce "a new blob and
|
|
8
|
+
* an artifact version". #133's versions are versions of the *content*; a PDF is a rendering of one. Making a
|
|
9
|
+
* render a new version would bump `latestVersion` for a reason unrelated to what the assistant wrote, and
|
|
10
|
+
* "the newest version" would stop meaning "the newest content" — and exporting the same artifact to two
|
|
11
|
+
* formats would create two content versions that are not content. So an export is its own record, keyed on
|
|
12
|
+
* `(artifactId, version, format)`, which delivers the property the issue actually wanted: the same export is
|
|
13
|
+
* re-downloaded rather than re-rendered.
|
|
14
|
+
*
|
|
15
|
+
* Four properties, each answering an AC:
|
|
16
|
+
*
|
|
17
|
+
* - **The bytes go through the file ports** (AC-5). A rendered PDF becomes a `FileMetadata` row plus content
|
|
18
|
+
* in `FileContentStore`, so it inherits #129's conversation entitlement and 15-minute signed URLs rather
|
|
19
|
+
* than needing a second mediated-download path. There is no code here that could produce a permanent URL.
|
|
20
|
+
* - **Rendering is enqueued, not awaited** (AC-3). `requestExport` claims the slot and returns a reference.
|
|
21
|
+
* - **A failure creates no partial anything** (AC-4). The bytes are written before the export row is
|
|
22
|
+
* completed, and the row is only marked `rendered` once the file exists — so a failed render leaves a
|
|
23
|
+
* `failed` row with a reason and no file, never a row promising a download that is not there.
|
|
24
|
+
* - **Identical input yields identical bytes** (AC-6). The renderers are pure and the PDF's date is fixed;
|
|
25
|
+
* the checksum is recorded so a re-render can be compared without re-downloading.
|
|
26
|
+
*/
|
|
27
|
+
import { AgentPlatformError } from "../core/errors.js";
|
|
28
|
+
import { asId } from "../core/ids.js";
|
|
29
|
+
import { parseMarkdown } from "../documents/parsers/text.js";
|
|
30
|
+
import { DEFAULT_EXTRACTION_LIMITS } from "../documents/index.js";
|
|
31
|
+
import { MAX_SIGNED_URL_SECONDS } from "../files/index.js";
|
|
32
|
+
import { renderMarkdown } from "./markdown.js";
|
|
33
|
+
import { FIXED_CREATION_DATE, renderPdf } from "./pdf.js";
|
|
34
|
+
/** The media type each format is served as. Exact, because a browser decides how to treat it from this. */
|
|
35
|
+
export const EXPORT_MEDIA_TYPES = {
|
|
36
|
+
pdf: "application/pdf",
|
|
37
|
+
markdown: "text/markdown",
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The largest export this platform will render.
|
|
41
|
+
*
|
|
42
|
+
* A ceiling exists because an artifact is bounded but its *rendering* is not obviously so — a table of ten
|
|
43
|
+
* thousand rows is a small JSON value and a very large PDF. 32 MiB is far past any document a person reads.
|
|
44
|
+
*/
|
|
45
|
+
export const MAX_EXPORT_BYTES = 32 * 1024 * 1024;
|
|
46
|
+
const refuse = (code, message) => new AgentPlatformError({ code, message, retryable: false });
|
|
47
|
+
/**
|
|
48
|
+
* An artifact's content as blocks.
|
|
49
|
+
*
|
|
50
|
+
* Accepts either the block list #131 produces or a markdown string, and parses the latter with #131's own
|
|
51
|
+
* parser rather than a second one. Two parsers for markdown would eventually disagree, and the one that
|
|
52
|
+
* disagreed would be whichever the exporter used.
|
|
53
|
+
*/
|
|
54
|
+
export const toBlocks = (value) => {
|
|
55
|
+
if (typeof value === "string")
|
|
56
|
+
return parseMarkdown(new TextEncoder().encode(value), DEFAULT_EXTRACTION_LIMITS).blocks;
|
|
57
|
+
if (Array.isArray(value))
|
|
58
|
+
return value;
|
|
59
|
+
if (typeof value === "object" && value !== null && Array.isArray(value.blocks))
|
|
60
|
+
return value.blocks;
|
|
61
|
+
// A JSON artifact with no block structure: rendered as one preformatted paragraph rather than refused, so
|
|
62
|
+
// exporting a data artifact produces something rather than an error.
|
|
63
|
+
return [{ kind: "paragraph", text: JSON.stringify(value, null, 2) }];
|
|
64
|
+
};
|
|
65
|
+
/** Citations carried on the artifact's content, if any. Absent is normal and not an error. */
|
|
66
|
+
export const toCitations = (value) => {
|
|
67
|
+
if (typeof value !== "object" || value === null)
|
|
68
|
+
return [];
|
|
69
|
+
const raw = value.citations;
|
|
70
|
+
if (!Array.isArray(raw))
|
|
71
|
+
return [];
|
|
72
|
+
return raw
|
|
73
|
+
.filter((c) => typeof c === "object" && c !== null)
|
|
74
|
+
.map((c, i) => ({
|
|
75
|
+
// The marker defaults to position, so a citation list with no explicit numbering still renders
|
|
76
|
+
// consistently rather than as a row of zeroes.
|
|
77
|
+
marker: typeof c["marker"] === "number" ? c["marker"] : i + 1,
|
|
78
|
+
title: typeof c["title"] === "string" ? c["title"] : "Untitled source",
|
|
79
|
+
...(typeof c["locator"] === "string" ? { locator: c["locator"] } : {}),
|
|
80
|
+
...(typeof c["url"] === "string" ? { url: c["url"] } : {}),
|
|
81
|
+
}));
|
|
82
|
+
};
|
|
83
|
+
const sha256Hex = async (bytes) => {
|
|
84
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
85
|
+
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
86
|
+
};
|
|
87
|
+
export const createExportService = (deps) => {
|
|
88
|
+
const clock = deps.clock ?? (() => new Date().toISOString());
|
|
89
|
+
const documentDate = deps.documentDate ?? (() => FIXED_CREATION_DATE);
|
|
90
|
+
const newExportId = deps.exportId ?? (() => `exp_${crypto.randomUUID()}`);
|
|
91
|
+
const newFileId = deps.fileId ?? (() => `file_${crypto.randomUUID()}`);
|
|
92
|
+
const newContentKey = deps.contentKey ?? (() => `x_${crypto.randomUUID()}`);
|
|
93
|
+
const log = deps.log ?? (() => { });
|
|
94
|
+
const maxBytes = deps.maxExportBytes ?? MAX_EXPORT_BYTES;
|
|
95
|
+
return {
|
|
96
|
+
/**
|
|
97
|
+
* Ask for an export — AC-3.
|
|
98
|
+
*
|
|
99
|
+
* Entitlement first, through `ArtifactService`, so an export is never more accessible than the
|
|
100
|
+
* conversation that produced the artifact and there is no second permission model here.
|
|
101
|
+
*
|
|
102
|
+
* An existing export is *returned*, not re-rendered. That is the property the issue wanted from "an
|
|
103
|
+
* artifact version rather than a transient stream", and the unique constraint is what makes it hold under
|
|
104
|
+
* two simultaneous requests rather than under one.
|
|
105
|
+
*/
|
|
106
|
+
async requestExport(context, input) {
|
|
107
|
+
// Reads the artifact through the service, which authorises on the conversation and refuses a deleted
|
|
108
|
+
// artifact -- so both checks happen before an export row exists.
|
|
109
|
+
const { artifact, version } = await this.resolveTarget(context, input);
|
|
110
|
+
const claim = await deps.exports.claim({
|
|
111
|
+
tenantId: context.tenantId,
|
|
112
|
+
export: {
|
|
113
|
+
id: newExportId(),
|
|
114
|
+
artifactId: artifact.id,
|
|
115
|
+
version,
|
|
116
|
+
format: input.format,
|
|
117
|
+
requestedBy: context.principalId,
|
|
118
|
+
createdAt: clock(),
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
// Someone already has it: rendered, or being rendered. Either way the answer is this row.
|
|
122
|
+
if (!claim.claimed)
|
|
123
|
+
return { export: claim.export, enqueued: false };
|
|
124
|
+
if (deps.dispatcher === undefined)
|
|
125
|
+
return { export: claim.export, enqueued: false };
|
|
126
|
+
try {
|
|
127
|
+
await deps.dispatcher.enqueueExport({ tenantId: context.tenantId, exportId: claim.export.id });
|
|
128
|
+
return { export: claim.export, enqueued: true };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
// Left `pending` rather than propagated. The claim succeeded and the artifact is untouched; an
|
|
132
|
+
// unreachable queue must not turn a request into a failure the user has to understand.
|
|
133
|
+
log("export enqueue failed; left pending", { exportId: claim.export.id, error });
|
|
134
|
+
return { export: claim.export, enqueued: false };
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
/** Which version an export targets. The latest unless one is named. */
|
|
138
|
+
async resolveTarget(context, input) {
|
|
139
|
+
const read = await deps.artifacts.read(context, {
|
|
140
|
+
id: input.id,
|
|
141
|
+
...(input.version === undefined ? {} : { version: input.version }),
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
artifact: {
|
|
145
|
+
id: read.artifact.id,
|
|
146
|
+
name: read.artifact.name,
|
|
147
|
+
conversationId: read.artifact.conversationId,
|
|
148
|
+
},
|
|
149
|
+
version: read.version.version,
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
/**
|
|
153
|
+
* Render. Called by the worker, and directly by a single-process host.
|
|
154
|
+
*
|
|
155
|
+
* Returns the record rather than throwing for a render problem. The only throw is for an export row that
|
|
156
|
+
* is not there, which is a caller error rather than a rendering one.
|
|
157
|
+
*/
|
|
158
|
+
async render(job, context) {
|
|
159
|
+
const record = await deps.exports.get({ tenantId: context.tenantId, id: job.exportId });
|
|
160
|
+
if (record === null)
|
|
161
|
+
throw refuse("not_found", "no such export");
|
|
162
|
+
if (record.state === "rendered")
|
|
163
|
+
return record;
|
|
164
|
+
const fail = async (reason, message) => {
|
|
165
|
+
await deps.exports.complete({
|
|
166
|
+
tenantId: context.tenantId,
|
|
167
|
+
id: record.id,
|
|
168
|
+
state: "failed",
|
|
169
|
+
failureReason: reason,
|
|
170
|
+
failureMessage: message,
|
|
171
|
+
at: clock(),
|
|
172
|
+
});
|
|
173
|
+
return { ...record, state: "failed", failureReason: reason, failureMessage: message };
|
|
174
|
+
};
|
|
175
|
+
let read;
|
|
176
|
+
try {
|
|
177
|
+
// Through the service again, so the render is authorised as the requester rather than as the worker.
|
|
178
|
+
read = await deps.artifacts.read(context, { id: record.artifactId, version: record.version });
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return fail("source-unavailable", error instanceof AgentPlatformError ? error.message : "That artifact version could not be read.");
|
|
182
|
+
}
|
|
183
|
+
const blocks = toBlocks(read.content);
|
|
184
|
+
const citations = toCitations(read.content);
|
|
185
|
+
let bytes;
|
|
186
|
+
let warnings = [];
|
|
187
|
+
try {
|
|
188
|
+
if (record.format === "pdf") {
|
|
189
|
+
const rendered = renderPdf({
|
|
190
|
+
title: read.artifact.name,
|
|
191
|
+
blocks,
|
|
192
|
+
...(citations.length === 0 ? {} : { citations }),
|
|
193
|
+
createdAt: documentDate(),
|
|
194
|
+
});
|
|
195
|
+
bytes = rendered.bytes;
|
|
196
|
+
warnings =
|
|
197
|
+
rendered.unsupportedCharacters.length === 0
|
|
198
|
+
? []
|
|
199
|
+
: [
|
|
200
|
+
// Reported, because a report whose title rendered without its accents is a document someone
|
|
201
|
+
// forwards believing it is correct.
|
|
202
|
+
`These characters are not supported by the export font and were omitted: ${rendered.unsupportedCharacters.join(" ")}`,
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
bytes = new TextEncoder().encode(renderMarkdown({ title: read.artifact.name, blocks, ...(citations.length === 0 ? {} : { citations }) }));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
// A thrown renderer is the renderer being broken, not the artifact being unrenderable. Logged with
|
|
211
|
+
// the detail; the user gets a sentence.
|
|
212
|
+
log("renderer threw", { exportId: record.id, format: record.format, error });
|
|
213
|
+
return fail("render-failed", "That artifact could not be rendered.");
|
|
214
|
+
}
|
|
215
|
+
if (bytes.byteLength > maxBytes)
|
|
216
|
+
return fail("too-large",
|
|
217
|
+
// The limit named, because "too large" sends someone to guess what to cut.
|
|
218
|
+
`The rendered export is ${bytes.byteLength} bytes and the limit is ${maxBytes} bytes.`);
|
|
219
|
+
const fileId = asId(newFileId());
|
|
220
|
+
const contentKey = newContentKey();
|
|
221
|
+
const at = clock();
|
|
222
|
+
// The file row first, then the bytes, then the export row -- the same ordering #129 chose and for the
|
|
223
|
+
// same reason: a crash leaves a `pending` file row reconciliation can see, rather than an object nothing
|
|
224
|
+
// references. And the export stays `pending`, so nothing offers a download that does not exist.
|
|
225
|
+
await deps.files.create({
|
|
226
|
+
tenantId: context.tenantId,
|
|
227
|
+
file: {
|
|
228
|
+
id: fileId,
|
|
229
|
+
conversationId: read.artifact.conversationId,
|
|
230
|
+
filename: exportFilename(read.artifact.name, record.format),
|
|
231
|
+
mediaType: EXPORT_MEDIA_TYPES[record.format],
|
|
232
|
+
byteSize: bytes.byteLength,
|
|
233
|
+
contentKey,
|
|
234
|
+
state: "pending",
|
|
235
|
+
uploadedBy: record.requestedBy,
|
|
236
|
+
createdAt: at,
|
|
237
|
+
// Nothing to extract: this file *is* the extraction's output, rendered. Marking it skipped stops the
|
|
238
|
+
// extraction sweep from picking up every export forever.
|
|
239
|
+
extraction: { state: "skipped", failureMessage: "A rendered export is not extracted.", at },
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
const stored = await deps.content.putFile({
|
|
243
|
+
tenantId: context.tenantId,
|
|
244
|
+
contentKey,
|
|
245
|
+
mediaType: EXPORT_MEDIA_TYPES[record.format],
|
|
246
|
+
bytes: (async function* () {
|
|
247
|
+
yield bytes;
|
|
248
|
+
})(),
|
|
249
|
+
maxBytes,
|
|
250
|
+
});
|
|
251
|
+
const moved = await deps.files.transition({
|
|
252
|
+
tenantId: context.tenantId,
|
|
253
|
+
id: fileId,
|
|
254
|
+
from: "pending",
|
|
255
|
+
to: "stored",
|
|
256
|
+
at: clock(),
|
|
257
|
+
checksum: stored.checksum,
|
|
258
|
+
});
|
|
259
|
+
if (!moved.moved) {
|
|
260
|
+
// The conversation was deleted mid-render. The bytes are unreferenced, so they go now rather than
|
|
261
|
+
// waiting for a sweep -- and the export fails with a reason rather than pointing at a dead file.
|
|
262
|
+
await deps.content.deleteFile({ tenantId: context.tenantId, contentKey });
|
|
263
|
+
return fail("source-unavailable", "That conversation was deleted while the export was rendering.");
|
|
264
|
+
}
|
|
265
|
+
const checksum = await sha256Hex(bytes);
|
|
266
|
+
await deps.exports.complete({
|
|
267
|
+
tenantId: context.tenantId,
|
|
268
|
+
id: record.id,
|
|
269
|
+
state: "rendered",
|
|
270
|
+
fileId,
|
|
271
|
+
byteSize: bytes.byteLength,
|
|
272
|
+
checksum,
|
|
273
|
+
at: clock(),
|
|
274
|
+
});
|
|
275
|
+
if (warnings.length > 0)
|
|
276
|
+
log("export rendered with warnings", { exportId: record.id, warnings });
|
|
277
|
+
return {
|
|
278
|
+
...record,
|
|
279
|
+
state: "rendered",
|
|
280
|
+
fileId,
|
|
281
|
+
byteSize: bytes.byteLength,
|
|
282
|
+
checksum,
|
|
283
|
+
renderedAt: clock(),
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
/**
|
|
287
|
+
* A short-lived download URL — AC-5.
|
|
288
|
+
*
|
|
289
|
+
* Delegated to the file ports, which already clamp the expiry to fifteen minutes and refuse a file whose
|
|
290
|
+
* conversation the caller is not entitled to. Nothing here can produce a permanent URL because nothing
|
|
291
|
+
* here constructs a URL at all.
|
|
292
|
+
*/
|
|
293
|
+
async downloadUrl(context, exportId) {
|
|
294
|
+
const record = await this.readyExport(context, exportId);
|
|
295
|
+
const file = await deps.files.get({ tenantId: context.tenantId, id: record.fileId });
|
|
296
|
+
if (file === null || file.state !== "stored")
|
|
297
|
+
throw refuse("not_found", "that export's file is missing");
|
|
298
|
+
return deps.content.signedUrl({
|
|
299
|
+
tenantId: context.tenantId,
|
|
300
|
+
contentKey: file.contentKey,
|
|
301
|
+
expiresInSeconds: MAX_SIGNED_URL_SECONDS,
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
/** The proxied download, for a content store that cannot sign — and the fallback when one can. */
|
|
305
|
+
async download(context, exportId) {
|
|
306
|
+
const record = await this.readyExport(context, exportId);
|
|
307
|
+
const file = await deps.files.get({ tenantId: context.tenantId, id: record.fileId });
|
|
308
|
+
if (file === null || file.state !== "stored")
|
|
309
|
+
throw refuse("not_found", "that export's file is missing");
|
|
310
|
+
const bytes = await deps.content.readFile({ tenantId: context.tenantId, contentKey: file.contentKey });
|
|
311
|
+
if (bytes === null)
|
|
312
|
+
throw refuse("not_found", "that export's contents are missing");
|
|
313
|
+
return bytes;
|
|
314
|
+
},
|
|
315
|
+
/**
|
|
316
|
+
* An export that is ready to download, or the reason it is not.
|
|
317
|
+
*
|
|
318
|
+
* Entitlement is re-checked through `ArtifactService` on every download rather than trusted from the
|
|
319
|
+
* request that created the export: a user removed from a conversation must stop being able to download
|
|
320
|
+
* what they exported while they were in it.
|
|
321
|
+
*/
|
|
322
|
+
async readyExport(context, exportId) {
|
|
323
|
+
const record = await deps.exports.get({ tenantId: context.tenantId, id: exportId });
|
|
324
|
+
if (record === null)
|
|
325
|
+
throw refuse("not_found", "no such export");
|
|
326
|
+
// Re-authorised here. The artifact read throws `not_found` for an unentitled caller.
|
|
327
|
+
await deps.artifacts.read(context, { id: record.artifactId, version: record.version });
|
|
328
|
+
if (record.state === "failed")
|
|
329
|
+
throw refuse("invalid_input", record.failureMessage ?? "That export failed to render.");
|
|
330
|
+
if (record.state !== "rendered" || record.fileId === undefined)
|
|
331
|
+
throw refuse("conflict", "That export is still rendering. Try again shortly.");
|
|
332
|
+
return record;
|
|
333
|
+
},
|
|
334
|
+
async listForArtifact(context, input) {
|
|
335
|
+
// Authorised before the query runs: for an unentitled caller a page count still answers "has this been
|
|
336
|
+
// exported".
|
|
337
|
+
await deps.artifacts.read(context, { id: input.id });
|
|
338
|
+
return deps.exports.listByArtifact({
|
|
339
|
+
tenantId: context.tenantId,
|
|
340
|
+
artifactId: input.id,
|
|
341
|
+
limit: input.limit,
|
|
342
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
/**
|
|
348
|
+
* A safe download filename.
|
|
349
|
+
*
|
|
350
|
+
* Derived from the artifact's name, which is user input, so it is reduced to a conservative character class
|
|
351
|
+
* rather than trusted: a filename is a `Content-Disposition` header value, and a header value containing a
|
|
352
|
+
* newline is a response-splitting bug rather than a badly-named file.
|
|
353
|
+
*/
|
|
354
|
+
export const exportFilename = (name, format) => {
|
|
355
|
+
const stem = name
|
|
356
|
+
.normalize("NFKD")
|
|
357
|
+
.replace(/[^A-Za-z0-9._ -]/g, "")
|
|
358
|
+
.trim()
|
|
359
|
+
.replace(/\s+/g, "-")
|
|
360
|
+
.slice(0, 80) || "export";
|
|
361
|
+
return `${stem}.${format === "pdf" ? "pdf" : "md"}`;
|
|
362
|
+
};
|
|
363
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown export (#134).
|
|
3
|
+
*
|
|
4
|
+
* The easy format, and worth doing deliberately rather than by concatenation. Two things it must get right:
|
|
5
|
+
*
|
|
6
|
+
* - **Citations.** Inline markers and a `References` section, so a claim in the export still points at its
|
|
7
|
+
* source. An export that dropped them would be a document someone forwards as if it were unsourced.
|
|
8
|
+
* - **Determinism** (AC-6). No timestamps, no ordering that depends on object key iteration — the output is a
|
|
9
|
+
* pure function of the blocks, so a re-render is byte-identical.
|
|
10
|
+
*/
|
|
11
|
+
import type { DocumentBlock } from "../documents/index.js";
|
|
12
|
+
import type { ExportCitation } from "./pdf.js";
|
|
13
|
+
export type MarkdownRenderInput = {
|
|
14
|
+
readonly title: string;
|
|
15
|
+
readonly blocks: readonly DocumentBlock[];
|
|
16
|
+
readonly citations?: readonly ExportCitation[];
|
|
17
|
+
};
|
|
18
|
+
export declare const renderMarkdown: (input: MarkdownRenderInput) => string;
|
|
19
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown export (#134).
|
|
3
|
+
*
|
|
4
|
+
* The easy format, and worth doing deliberately rather than by concatenation. Two things it must get right:
|
|
5
|
+
*
|
|
6
|
+
* - **Citations.** Inline markers and a `References` section, so a claim in the export still points at its
|
|
7
|
+
* source. An export that dropped them would be a document someone forwards as if it were unsourced.
|
|
8
|
+
* - **Determinism** (AC-6). No timestamps, no ordering that depends on object key iteration — the output is a
|
|
9
|
+
* pure function of the blocks, so a re-render is byte-identical.
|
|
10
|
+
*/
|
|
11
|
+
import { renderBlocks } from "../documents/render.js";
|
|
12
|
+
export const renderMarkdown = (input) => {
|
|
13
|
+
const parts = [`# ${input.title}`, renderBlocks(input.blocks)];
|
|
14
|
+
if (input.citations !== undefined && input.citations.length > 0) {
|
|
15
|
+
parts.push("## References");
|
|
16
|
+
parts.push([...input.citations]
|
|
17
|
+
// Sorted by marker, not by insertion: the numbers in the text have to match the order of the list, and
|
|
18
|
+
// relying on the caller's ordering is relying on something no type enforces.
|
|
19
|
+
.sort((a, b) => a.marker - b.marker)
|
|
20
|
+
.map((c) => {
|
|
21
|
+
const detail = [c.locator, c.url].filter((p) => p !== undefined && p !== "");
|
|
22
|
+
return `${c.marker}. ${c.title}${detail.length > 0 ? ` — ${detail.join(" — ")}` : ""}`;
|
|
23
|
+
})
|
|
24
|
+
.join("\n"));
|
|
25
|
+
}
|
|
26
|
+
// A single trailing newline, because a file without one is a file every diff tool complains about.
|
|
27
|
+
return `${parts.filter((p) => p.trim() !== "").join("\n\n")}\n`;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A deterministic PDF writer (#134).
|
|
3
|
+
*
|
|
4
|
+
* Writing a PDF is far more tractable than reading one, because the output is ours: no unknown producers, no
|
|
5
|
+
* broken xrefs, no font encodings to guess. This emits a small, valid subset — Base-14 fonts, one content
|
|
6
|
+
* stream per page, an uncompressed xref table — which every viewer has supported since 1993.
|
|
7
|
+
*
|
|
8
|
+
* **Determinism is a requirement, not a nicety** (AC-6). A PDF normally carries a `CreationDate` and often a
|
|
9
|
+
* random `/ID`, either of which makes two renders of the same input differ. Both are fixed here: the date
|
|
10
|
+
* comes from the caller (and defaults to a constant), and the `/ID` is derived from the content. That is what
|
|
11
|
+
* lets a re-render be compared byte-for-byte, which is the only way to notice a renderer that has quietly
|
|
12
|
+
* started producing something else.
|
|
13
|
+
*
|
|
14
|
+
* **No font embedding.** Base-14 fonts are guaranteed present in every viewer, so the file carries no font
|
|
15
|
+
* program — which keeps a report a few kilobytes instead of a few hundred, and sidesteps the licensing
|
|
16
|
+
* question entirely. The cost is the character repertoire: WinAnsi, so no CJK. Stated rather than discovered,
|
|
17
|
+
* and `unsupportedCharacters` reports what was dropped instead of silently emitting blanks.
|
|
18
|
+
*/
|
|
19
|
+
import type { DocumentBlock } from "../documents/index.js";
|
|
20
|
+
/** Points. A4 rather than Letter: the deployment is European and the difference is visible when it is wrong. */
|
|
21
|
+
export declare const PAGE_WIDTH = 595.28;
|
|
22
|
+
export declare const PAGE_HEIGHT = 841.89;
|
|
23
|
+
export declare const MARGIN = 56.7;
|
|
24
|
+
export type FontFace = "regular" | "bold" | "mono";
|
|
25
|
+
/** Width of a string at a size, in points. */
|
|
26
|
+
export declare const textWidth: (text: string, size: number, face: FontFace) => number;
|
|
27
|
+
/**
|
|
28
|
+
* WinAnsi-encode a string for a PDF literal, reporting what could not be represented.
|
|
29
|
+
*
|
|
30
|
+
* Reported rather than substituted silently: a report whose CJK title rendered as `????` is a report someone
|
|
31
|
+
* sends to a colleague believing it is correct, and finding out later is worse than being told now.
|
|
32
|
+
*/
|
|
33
|
+
export declare const encodeWinAnsi: (text: string) => {
|
|
34
|
+
encoded: string;
|
|
35
|
+
dropped: string[];
|
|
36
|
+
};
|
|
37
|
+
/** Break a string into lines that fit `maxWidth`, breaking on words and only inside a word when it must. */
|
|
38
|
+
export declare const wrapText: (text: string, maxWidth: number, size: number, face: FontFace) => readonly string[];
|
|
39
|
+
/** A citation, rendered as a numbered marker inline and an entry in a References section. */
|
|
40
|
+
export type ExportCitation = {
|
|
41
|
+
readonly marker: number;
|
|
42
|
+
readonly title: string;
|
|
43
|
+
readonly locator?: string;
|
|
44
|
+
readonly url?: string;
|
|
45
|
+
};
|
|
46
|
+
export type PdfRenderInput = {
|
|
47
|
+
readonly title: string;
|
|
48
|
+
readonly blocks: readonly DocumentBlock[];
|
|
49
|
+
readonly citations?: readonly ExportCitation[];
|
|
50
|
+
/**
|
|
51
|
+
* The document's creation date.
|
|
52
|
+
*
|
|
53
|
+
* A parameter, and constant by default, because `new Date()` here would make AC-6 unachievable: two renders
|
|
54
|
+
* of the same artifact would differ in a field nobody reads and every byte-comparison would fail.
|
|
55
|
+
*/
|
|
56
|
+
readonly createdAt?: string;
|
|
57
|
+
};
|
|
58
|
+
export type PdfRenderResult = {
|
|
59
|
+
readonly bytes: Uint8Array;
|
|
60
|
+
readonly pageCount: number;
|
|
61
|
+
/** Characters the Base-14 fonts cannot represent, deduplicated. Reported, never silently dropped. */
|
|
62
|
+
readonly unsupportedCharacters: readonly string[];
|
|
63
|
+
};
|
|
64
|
+
export declare const renderPdf: (input: PdfRenderInput) => PdfRenderResult;
|
|
65
|
+
/**
|
|
66
|
+
* The default creation date.
|
|
67
|
+
*
|
|
68
|
+
* A constant, and that is the point. `new Date()` would put a different timestamp in every render and make
|
|
69
|
+
* byte-for-byte comparison impossible — so a caller that wants a real date passes one, and a caller that
|
|
70
|
+
* wants reproducibility gets it by default. The date chosen is the PDF epoch's own convention for "unset".
|
|
71
|
+
*/
|
|
72
|
+
export declare const FIXED_CREATION_DATE = "2000-01-01T00:00:00.000Z";
|
|
73
|
+
//# sourceMappingURL=pdf.d.ts.map
|