@inneranimalmedia/agentsam-sdk 2.6.10 → 2.6.11
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/README.md +3 -0
- package/TRADEMARKS.md +22 -0
- package/apps/README.md +3 -0
- package/apps/cad-creator/LICENSE +21 -0
- package/apps/cad-creator/README.md +4 -1
- package/apps/cad-creator/shared/cad/package.json +1 -1
- package/apps/client-cms-editor/LICENSE +21 -0
- package/apps/client-cms-editor/README.md +3 -0
- package/apps/client-cms-editor/backend/package.json +2 -2
- package/apps/client-cms-editor/frontend/package.json +3 -3
- package/apps/client-cms-editor/package-lock.json +43 -43
- package/apps/client-cms-editor/package.json +3 -3
- package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +1 -1
- package/apps/client-cms-editor/reference/harvest/README.md +1 -1
- package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +1 -1
- package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +1 -1
- package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +1 -1
- package/apps/client-cms-editor/shared/cms/package.json +1 -1
- package/docs/MINI.md +4 -2
- package/docs/REPO_FILE_MAP.txt +0 -1
- package/docs/SOURCE_ARCHITECTURE.md +1 -1
- package/docs/architecture/DUAL_ANALYTICS_PRODUCT.md +140 -0
- package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
- package/docs/engineering/go-portability/01-product-project-deployment-authority.md +69 -0
- package/docs/engineering/provider-internal-renames/01-lane-initialization.md +20 -0
- package/docs/engineering/provider-internal-renames/01a-runtime-environment-contract.md +115 -0
- package/docs/engineering/provider-internal-renames/02-cfoa.md +57 -0
- package/docs/engineering/provider-internal-renames/03-gclioa.md +32 -0
- package/docs/engineering/provider-internal-renames/04-goaude.md +50 -0
- package/docs/engineering/provider-internal-renames/05-gdrv.md +33 -0
- package/docs/engineering/provider-internal-renames/06-verification.md +103 -0
- package/docs/engineering/provider-internal-renames/README.md +77 -0
- package/docs/engineering/provider-internal-renames/runtime-environment-contract.json +220 -0
- package/docs/engineering/test-diagnostics/go-cli-test-hang-20261003.md +49 -0
- package/docs/engineering/test-diagnostics/go-skip-deploy-docker-fix.md +90 -0
- package/docs/knowledge-service-verification.md +3 -3
- package/docs/plans/AGENTSAM-SUSPEND-QUEUE-KNOWLEDGE-RDP-2026-10-03.md +870 -0
- package/docs/plans/LOCAL-STUDIO-GRADUATION-2026-09-26.md +9 -9
- package/docs/plans/MACHINE-FIRST-APPS-TUI-2026-09-24.md +11 -11
- package/docs/plans/RECONCILIATION-2026-09-19.md +6 -6
- package/docs/plans/SAM_WORK_PROTOCOL.md +667 -0
- package/docs/plans/TOMORROW-EXTENSIVE-TODO-2026-09-26.md +2 -2
- package/docs/plans/UNIVERSAL-INGEST-MACHINE-FIRST-2026-09-24.md +3 -3
- package/examples/hyperspace-demo/src/main.tsx +227 -110
- package/migrations/d1/0020_agentsam_receipt_catalog.sql +35 -0
- package/migrations/runtime/0006_agent_run_suspend_activity.sql +53 -0
- package/migrations/runtime/0007_queue_control.sql +40 -0
- package/migrations/runtime/0008_agent_run_dependencies.sql +24 -0
- package/migrations/runtime/0008_analytics_legacy_bootstrap.sql +43 -0
- package/migrations/runtime/0009_analytics_authority.sql +108 -0
- package/package.json +26 -11
- package/packages/agentsam-brand/README.md +3 -0
- package/packages/agentsam-brand/package.json +2 -2
- package/packages/agentsam-contracts/dist/adapter.d.ts +22 -0
- package/packages/agentsam-contracts/dist/adapter.js +0 -0
- package/packages/agentsam-contracts/dist/agent.d.ts +66 -0
- package/packages/agentsam-contracts/dist/agent.js +15 -0
- package/packages/agentsam-contracts/dist/artifacts.d.ts +70 -0
- package/packages/agentsam-contracts/dist/artifacts.js +0 -0
- package/packages/agentsam-contracts/dist/authority.d.ts +21 -0
- package/packages/agentsam-contracts/dist/authority.js +0 -0
- package/packages/agentsam-contracts/dist/context.d.ts +14 -0
- package/packages/agentsam-contracts/dist/context.js +0 -0
- package/packages/agentsam-contracts/dist/errors.d.ts +121 -0
- package/packages/agentsam-contracts/dist/errors.js +5 -0
- package/packages/agentsam-contracts/dist/events.d.ts +53 -0
- package/packages/agentsam-contracts/dist/events.js +0 -0
- package/packages/agentsam-contracts/dist/execution.d.ts +38 -0
- package/packages/agentsam-contracts/dist/execution.js +0 -0
- package/packages/agentsam-contracts/dist/hooks.d.ts +31 -0
- package/packages/agentsam-contracts/dist/hooks.js +0 -0
- package/packages/agentsam-contracts/dist/identity.d.ts +15 -0
- package/packages/agentsam-contracts/dist/identity.js +0 -0
- package/packages/agentsam-contracts/dist/index.d.ts +16 -0
- package/packages/agentsam-contracts/dist/index.js +164 -0
- package/packages/agentsam-contracts/dist/models.d.ts +9 -0
- package/packages/agentsam-contracts/dist/models.js +0 -0
- package/packages/agentsam-contracts/dist/providers.d.ts +27 -0
- package/packages/agentsam-contracts/dist/providers.js +0 -0
- package/packages/agentsam-contracts/dist/repository.d.ts +56 -0
- package/packages/agentsam-contracts/dist/repository.js +15 -0
- package/packages/agentsam-contracts/dist/run.d.ts +113 -0
- package/packages/agentsam-contracts/dist/run.js +132 -0
- package/packages/agentsam-contracts/dist/tools.d.ts +82 -0
- package/packages/agentsam-contracts/dist/tools.js +0 -0
- package/packages/agentsam-contracts/dist/widgets.d.ts +38 -0
- package/packages/agentsam-contracts/dist/widgets.js +0 -0
- package/packages/agentsam-contracts/package.json +9 -1
- package/packages/agentsam-contracts/scripts/build.mjs +1 -1
- package/packages/agentsam-contracts/src/agent.ts +78 -3
- package/packages/agentsam-contracts/src/index.ts +2 -0
- package/packages/agentsam-contracts/src/run.ts +320 -0
- package/packages/agentsam-contracts/src/widgets.ts +58 -0
- package/packages/agentsam-contracts/test/contracts.test.mjs +1 -1
- package/packages/agentsam-contracts/test/run-contract.test.mjs +94 -0
- package/packages/agentsam-errors/package.json +1 -1
- package/packages/agentsam-goap/README.md +3 -0
- package/packages/agentsam-goap/package.json +2 -2
- package/packages/agentsam-hooks/package.json +2 -2
- package/packages/agentsam-hooks/src/adapters/agents.js +48 -22
- package/packages/agentsam-hooks/src/index.d.ts +16 -0
- package/packages/agentsam-knowledge/package.json +1 -1
- package/packages/agentsam-knowledge/src/backends/index.js +1 -1
- package/packages/agentsam-merch/README.md +62 -0
- package/packages/agentsam-merch/bin/agentsam-merch.mjs +16 -0
- package/packages/agentsam-merch/package.json +65 -0
- package/packages/agentsam-merch/profiles/completeful/dtg-back-large.json +42 -0
- package/packages/agentsam-merch/profiles/completeful/laser-engraving.json +39 -0
- package/packages/agentsam-merch/profiles/generic/embroidery-digitization.json +22 -0
- package/packages/agentsam-merch/schemas/collection-candidate.schema.json +66 -0
- package/packages/agentsam-merch/schemas/manufacturing-profile.schema.json +221 -0
- package/packages/agentsam-merch/src/adapters-completeful.js +50 -0
- package/packages/agentsam-merch/src/cli.js +93 -0
- package/packages/agentsam-merch/src/collection-lab.js +60 -0
- package/packages/agentsam-merch/src/compatibility.js +501 -0
- package/packages/agentsam-merch/src/derivatives.js +112 -0
- package/packages/agentsam-merch/src/index.d.ts +316 -0
- package/packages/agentsam-merch/src/index.js +46 -0
- package/packages/agentsam-merch/src/node/index.d.ts +58 -0
- package/packages/agentsam-merch/src/node/index.js +6 -0
- package/packages/agentsam-merch/src/node/raster.js +142 -0
- package/packages/agentsam-merch/src/node/vector.js +239 -0
- package/packages/agentsam-merch/src/product-concepts.js +118 -0
- package/packages/agentsam-merch/src/profile-registry.js +104 -0
- package/packages/agentsam-merch/src/profile.js +158 -0
- package/packages/agentsam-merch/src/transforms.js +215 -0
- package/packages/agentsam-merch/test/merch.test.mjs +332 -0
- package/packages/agentsam-merch/test/node-transforms.test.mjs +149 -0
- package/packages/agentsam-nav/dist/browser.global.js +71 -34
- package/packages/agentsam-nav/dist/browser.js +3179 -781
- package/packages/agentsam-nav/dist/index.js +3171 -773
- package/packages/agentsam-nav/package.json +1 -1
- package/packages/agentsam-queue-control/README.md +4 -1
- package/packages/agentsam-queue-control/package.json +4 -3
- package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +111 -11
- package/packages/agentsam-queue-control/src/adapters/cloudflare.js +34 -6
- package/packages/agentsam-queue-control/src/adapters/memory.js +11 -2
- package/packages/agentsam-queue-control/src/adapters/sqlite.js +259 -0
- package/packages/agentsam-queue-control/src/contracts.js +207 -3
- package/packages/agentsam-queue-control/src/control-plane.js +62 -2
- package/packages/agentsam-queue-control/src/index.js +1 -0
- package/packages/agentsam-queue-control/test/queue-control.test.mjs +310 -0
- package/packages/agentsam-queue-control/test/sqlite-adapter.test.mjs +91 -0
- package/packages/agentsam-rapid-rust/README.md +3 -0
- package/packages/agentsam-repository/README.md +3 -0
- package/packages/agentsam-repository/package.json +1 -1
- package/packages/agentsam-vault/package.json +1 -1
- package/packages/agentsam-workbench/README.md +43 -0
- package/packages/agentsam-workbench/package.json +13 -3
- package/packages/agentsam-workbench/src/agent/AgentConversationSurface.tsx +108 -0
- package/packages/agentsam-workbench/src/agent/AgentPanel.tsx +25 -8
- package/packages/agentsam-workbench/src/agent/AgentRuntimeField.tsx +152 -0
- package/packages/agentsam-workbench/src/agent/Composer.tsx +10 -1
- package/packages/agentsam-workbench/src/agent/RunMode.ts +26 -0
- package/packages/agentsam-workbench/src/agent/Thread.tsx +18 -3
- package/packages/agentsam-workbench/src/agent/index.ts +3 -0
- package/packages/agentsam-workbench/src/index.ts +4 -0
- package/packages/agentsam-workbench/src/manufacturing/PreflightIndicator.tsx +203 -0
- package/packages/agentsam-workbench/src/manufacturing/index.ts +2 -0
- package/packages/agentsam-workbench/src/manufacturing/loading.ts +147 -0
- package/packages/agentsam-workbench/src/widgets/BuiltinWidget.tsx +22 -0
- package/packages/agentsam-workbench/src/widgets/CountdownWidget.tsx +68 -0
- package/packages/agentsam-workbench/src/widgets/WidgetFrame.tsx +56 -0
- package/packages/agentsam-workbench/src/widgets/countdown.ts +25 -0
- package/packages/agentsam-workbench/src/widgets/index.ts +6 -0
- package/packages/agentsam-workbench/src/widgets/registry.ts +13 -0
- package/packages/agentsam-workbench/src/widgets/useCountdown.ts +128 -0
- package/packages/agentsam-workbench/src/widgets/widgets.css +102 -0
- package/packages/connectors/{cloudflare → cfoa}/package.json +3 -3
- package/packages/heuristic-theme/package.json +1 -1
- package/packages/identity/README.md +3 -0
- package/packages/identity/backend/README.md +3 -0
- package/packages/identity/package.json +1 -1
- package/packages/identity/src/frontend/auth-portal/README.md +3 -0
- package/packages/identity/src/index.js +1 -1
- package/packages/identity/src/oauth/README.md +4 -1
- package/packages/identity/src/oauth/credentials.js +1 -1
- package/packages/identity/src/server/worker-router.js +1 -1
- package/packages/identity/tests/google-desktop-identity-login.test.mjs +1 -1
- package/packages/identity/tests/lane1-packed-consumer.test.mjs +1 -1
- package/packages/identity/tests/oauth-presets-accounts-ssot.test.mjs +1 -1
- package/packages/providers/completeful/package.json +1 -1
- package/packages/theme-church-site/package.json +1 -1
- package/packages/theme-companions-site/package.json +1 -1
- package/packages/theme-floors-site/package.json +1 -1
- package/packages/theme-fuelnfree-site/package.json +1 -1
- package/packages/theme-handyman-site/package.json +1 -1
- package/packages/theme-insurance-site/package.json +1 -1
- package/packages/theme-scenes/package.json +1 -1
- package/packages/theme-shinshu-site/package.json +1 -1
- package/packages/work-graph/package.json +1 -1
- package/protocol/models/README.md +3 -0
- package/protocol/receipts/agentsam.receipt.v1.json +95 -0
- package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +6 -6
- package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +6 -6
- package/skills/README.md +3 -0
- package/src/acp/agent-executor.js +106 -0
- package/src/acp/client.js +316 -0
- package/src/acp/dependencies.js +426 -0
- package/src/acp/http.js +207 -0
- package/src/acp/index.js +22 -0
- package/src/acp/runs.js +125 -0
- package/src/acp/runtime-selection.js +180 -0
- package/src/cli/command-catalog.js +3 -0
- package/src/cli.js +24 -0
- package/src/commands/cloudflare-login.js +1 -1
- package/src/commands/cloudflare.js +2 -2
- package/src/commands/connections.js +1 -1
- package/src/commands/go.js +76 -6
- package/src/commands/merch.js +5 -0
- package/src/commands/mini.js +1 -1
- package/src/commands/receipts.js +140 -0
- package/src/commands/run.js +253 -0
- package/src/go/container.js +1 -1
- package/src/go/contract.js +61 -18
- package/src/go/discover.js +0 -4
- package/src/go/index.js +13 -1
- package/src/go/probe.js +109 -0
- package/src/go/{cloudflare.js → providers/cloudflare.js} +36 -136
- package/src/go/verify.js +1 -1
- package/src/indexing/ingest/inventory.js +1 -1
- package/src/lib/deploy/local-studio.js +2 -2
- package/src/lib/mini/scaffold.js +1 -1
- package/src/lib/whoami-capabilities.js +1 -1
- package/src/local/runtime-store.js +8 -0
- package/src/mcp/cloudflare-bundles.js +1 -1
- package/src/plugins/index.js +1 -0
- package/src/plugins/registry.js +59 -6
- package/src/receipts/contract.js +68 -0
- package/src/receipts/index.js +20 -0
- package/src/receipts/prune.js +64 -0
- package/src/receipts/sinks.js +285 -0
- package/templates/mini/gadget/app.js +15 -40
- package/templates/mini/gadget/index.html +7 -7
- package/templates/mini/shared/style.css +1 -1
- package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +0 -670
- /package/packages/connectors/{cloudflare → cfoa}/OAUTH_STORAGE_AUDIT.md +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/api-client.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/capabilities.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/credential.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/brand-protection.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/keyless.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/pages.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/scanner.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/snippets.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/tag-gateway.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/tags.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/token-validation.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/vectorize.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/families/workflows.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/index.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/oauth-persist.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/owner.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/permission-options.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/routes.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/scope-catalog.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/src/vault.js +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/tests/capabilities.test.mjs +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/tests/connector.test.mjs +0 -0
- /package/packages/connectors/{cloudflare → cfoa}/tests/oauth-persist.test.mjs +0 -0
- /package/packages/identity/src/oauth/{google-desktop-exchange.js → goaude.js} +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
|
|
9
|
+
async function loadSharp(injected) {
|
|
10
|
+
if (injected) return injected;
|
|
11
|
+
try {
|
|
12
|
+
const mod = await import("sharp");
|
|
13
|
+
return mod.default || mod;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const wrapped = new Error(
|
|
16
|
+
"Raster-to-vector tracing requires the optional 'sharp' runtime dependency.",
|
|
17
|
+
);
|
|
18
|
+
wrapped.code = "sharp_unavailable";
|
|
19
|
+
wrapped.cause = error;
|
|
20
|
+
throw wrapped;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function commandAvailable(command) {
|
|
25
|
+
try {
|
|
26
|
+
await execFileAsync(command, ["--version"], { timeout: 2500 });
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function detectNodeTransformCapabilities() {
|
|
34
|
+
let rasterNormalizer = false;
|
|
35
|
+
let svgNormalizer = false;
|
|
36
|
+
try {
|
|
37
|
+
await import("sharp");
|
|
38
|
+
rasterNormalizer = true;
|
|
39
|
+
} catch {}
|
|
40
|
+
try {
|
|
41
|
+
await import("svgo");
|
|
42
|
+
svgNormalizer = true;
|
|
43
|
+
} catch {}
|
|
44
|
+
|
|
45
|
+
const [potrace, vtracer, inkscape] = await Promise.all([
|
|
46
|
+
commandAvailable("potrace"),
|
|
47
|
+
commandAvailable("vtracer"),
|
|
48
|
+
commandAvailable("inkscape"),
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
raster_normalizer: rasterNormalizer,
|
|
53
|
+
format_converter: rasterNormalizer,
|
|
54
|
+
vector_rasterizer: rasterNormalizer,
|
|
55
|
+
vector_tracer: potrace || vtracer,
|
|
56
|
+
svg_normalizer: svgNormalizer,
|
|
57
|
+
vector_normalizer: svgNormalizer,
|
|
58
|
+
text_outliner: inkscape,
|
|
59
|
+
tools: Object.freeze({ potrace, vtracer, inkscape, svgo: svgNormalizer }),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function occupancyIou(a, b) {
|
|
64
|
+
const length = Math.min(a.length, b.length);
|
|
65
|
+
let intersection = 0;
|
|
66
|
+
let union = 0;
|
|
67
|
+
for (let i = 0; i < length; i += 1) {
|
|
68
|
+
const left = a[i] < 128;
|
|
69
|
+
const right = b[i] < 128;
|
|
70
|
+
if (left && right) intersection += 1;
|
|
71
|
+
if (left || right) union += 1;
|
|
72
|
+
}
|
|
73
|
+
return union === 0 ? 1 : intersection / union;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function rasterMask(sharp, input, width, height, threshold) {
|
|
77
|
+
const { data } = await sharp(input, { failOn: "warning" })
|
|
78
|
+
.rotate()
|
|
79
|
+
.resize(width, height, { fit: "fill" })
|
|
80
|
+
.flatten({ background: "#ffffff" })
|
|
81
|
+
.grayscale()
|
|
82
|
+
.threshold(threshold)
|
|
83
|
+
.raw()
|
|
84
|
+
.toBuffer({ resolveWithObject: true });
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function traceRasterToSvg(
|
|
89
|
+
input,
|
|
90
|
+
{
|
|
91
|
+
tracer = "auto",
|
|
92
|
+
threshold = 190,
|
|
93
|
+
turdSize = 2,
|
|
94
|
+
sharp: injectedSharp,
|
|
95
|
+
} = {},
|
|
96
|
+
) {
|
|
97
|
+
const sharp = await loadSharp(injectedSharp);
|
|
98
|
+
const metadata = await sharp(input, { failOn: "warning" }).metadata();
|
|
99
|
+
const width = metadata.width;
|
|
100
|
+
const height = metadata.height;
|
|
101
|
+
if (!width || !height) {
|
|
102
|
+
const error = new Error("Raster dimensions are required before tracing.");
|
|
103
|
+
error.code = "raster_dimensions_missing";
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const potraceAvailable = await commandAvailable("potrace");
|
|
108
|
+
const vtracerAvailable = await commandAvailable("vtracer");
|
|
109
|
+
let selected = tracer;
|
|
110
|
+
if (selected === "auto") {
|
|
111
|
+
selected = vtracerAvailable ? "vtracer" : potraceAvailable ? "potrace" : null;
|
|
112
|
+
}
|
|
113
|
+
if (selected === "vtracer" && !vtracerAvailable) selected = null;
|
|
114
|
+
if (selected === "potrace" && !potraceAvailable) selected = null;
|
|
115
|
+
if (!selected) {
|
|
116
|
+
const error = new Error("No supported vector tracer is installed (vtracer or potrace).");
|
|
117
|
+
error.code = "vector_tracer_unavailable";
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const dir = await mkdtemp(join(tmpdir(), "agentsam-vectorize-"));
|
|
122
|
+
try {
|
|
123
|
+
const inputPath = join(dir, "source.png");
|
|
124
|
+
const outputPath = join(dir, "output.svg");
|
|
125
|
+
await writeFile(inputPath, Buffer.from(input));
|
|
126
|
+
|
|
127
|
+
if (selected === "vtracer") {
|
|
128
|
+
await execFileAsync(
|
|
129
|
+
"vtracer",
|
|
130
|
+
[
|
|
131
|
+
"--input",
|
|
132
|
+
inputPath,
|
|
133
|
+
"--output",
|
|
134
|
+
outputPath,
|
|
135
|
+
"--colormode",
|
|
136
|
+
"binary",
|
|
137
|
+
],
|
|
138
|
+
{ timeout: 30_000, maxBuffer: 1024 * 1024 },
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
const pgmPath = join(dir, "source.pgm");
|
|
142
|
+
const { data } = await sharp(input, { failOn: "warning" })
|
|
143
|
+
.rotate()
|
|
144
|
+
.flatten({ background: "#ffffff" })
|
|
145
|
+
.grayscale()
|
|
146
|
+
.threshold(threshold)
|
|
147
|
+
.raw()
|
|
148
|
+
.toBuffer({ resolveWithObject: true });
|
|
149
|
+
const header = Buffer.from(`P5\n${width} ${height}\n255\n`);
|
|
150
|
+
await writeFile(pgmPath, Buffer.concat([header, data]));
|
|
151
|
+
await execFileAsync(
|
|
152
|
+
"potrace",
|
|
153
|
+
[
|
|
154
|
+
pgmPath,
|
|
155
|
+
"--svg",
|
|
156
|
+
"--output",
|
|
157
|
+
outputPath,
|
|
158
|
+
"--turdsize",
|
|
159
|
+
String(Math.max(0, Number(turdSize) || 0)),
|
|
160
|
+
],
|
|
161
|
+
{ timeout: 30_000, maxBuffer: 1024 * 1024 },
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const svg = await readFile(outputPath, "utf8");
|
|
166
|
+
const sourceMask = await rasterMask(sharp, input, width, height, threshold);
|
|
167
|
+
const tracedMask = await rasterMask(
|
|
168
|
+
sharp,
|
|
169
|
+
Buffer.from(svg),
|
|
170
|
+
width,
|
|
171
|
+
height,
|
|
172
|
+
threshold,
|
|
173
|
+
);
|
|
174
|
+
const boundaryIntegrity = occupancyIou(sourceMask, tracedMask);
|
|
175
|
+
|
|
176
|
+
return Object.freeze({
|
|
177
|
+
svg,
|
|
178
|
+
tracer: selected,
|
|
179
|
+
descriptor: Object.freeze({
|
|
180
|
+
format: "svg",
|
|
181
|
+
mediaKind: "vector",
|
|
182
|
+
widthPx: width,
|
|
183
|
+
heightPx: height,
|
|
184
|
+
contentWidthPx: width,
|
|
185
|
+
contentHeightPx: height,
|
|
186
|
+
colorMode: "monochrome",
|
|
187
|
+
hasGradients: false,
|
|
188
|
+
hasOpenPaths: false,
|
|
189
|
+
hasLiveText: false,
|
|
190
|
+
hasExplicitViewBox: /\bviewBox\s*=/.test(svg),
|
|
191
|
+
boundaryIntegrity,
|
|
192
|
+
}),
|
|
193
|
+
metrics: Object.freeze({
|
|
194
|
+
boundaryIntegrity,
|
|
195
|
+
width,
|
|
196
|
+
height,
|
|
197
|
+
}),
|
|
198
|
+
});
|
|
199
|
+
} finally {
|
|
200
|
+
await rm(dir, { recursive: true, force: true }).catch(() => {});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function normalizeSvgWithSvgo(
|
|
205
|
+
svg,
|
|
206
|
+
{ multipass = true } = {},
|
|
207
|
+
) {
|
|
208
|
+
let optimize;
|
|
209
|
+
try {
|
|
210
|
+
({ optimize } = await import("svgo"));
|
|
211
|
+
} catch (error) {
|
|
212
|
+
const wrapped = new Error(
|
|
213
|
+
"SVG normalization requires the optional 'svgo' runtime dependency.",
|
|
214
|
+
);
|
|
215
|
+
wrapped.code = "svgo_unavailable";
|
|
216
|
+
wrapped.cause = error;
|
|
217
|
+
throw wrapped;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const result = optimize(String(svg), {
|
|
221
|
+
multipass,
|
|
222
|
+
plugins: [
|
|
223
|
+
"preset-default",
|
|
224
|
+
"removeDimensions",
|
|
225
|
+
{
|
|
226
|
+
name: "removeAttrs",
|
|
227
|
+
params: { attrs: "(data-name|data-layer|xml:space)" },
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return Object.freeze({
|
|
233
|
+
svg: result.data,
|
|
234
|
+
hasExplicitViewBox: /\bviewBox\s*=/.test(result.data),
|
|
235
|
+
hasLiveText: /<text\b/i.test(result.data),
|
|
236
|
+
note:
|
|
237
|
+
"SVGO normalizes SVG structure but does not outline live fonts. Use a font-aware outliner for <text> elements.",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
function normalizeProvider(value) {
|
|
2
|
+
return value == null ? null : String(value).trim().toLowerCase();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function normalizeTarget(target = {}, index = 0) {
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
...target,
|
|
8
|
+
provider: normalizeProvider(target.provider),
|
|
9
|
+
profileId: target.profileId ? String(target.profileId) : null,
|
|
10
|
+
priority: Number.isFinite(Number(target.priority))
|
|
11
|
+
? Number(target.priority)
|
|
12
|
+
: index,
|
|
13
|
+
enabled: target.enabled !== false,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function normalizeProductConcept(input = {}) {
|
|
18
|
+
if (!input.id) throw new TypeError("product concept id is required");
|
|
19
|
+
const targets = (input.targets || []).map(normalizeTarget);
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
...input,
|
|
22
|
+
id: String(input.id),
|
|
23
|
+
label: input.label ? String(input.label) : String(input.id),
|
|
24
|
+
category: input.category ? String(input.category) : null,
|
|
25
|
+
targets: Object.freeze(targets),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createProductConceptRegistry(initial = []) {
|
|
30
|
+
const concepts = new Map();
|
|
31
|
+
|
|
32
|
+
function register(input, { replace = false } = {}) {
|
|
33
|
+
const concept = normalizeProductConcept(input);
|
|
34
|
+
if (!replace && concepts.has(concept.id)) {
|
|
35
|
+
throw new Error(`Product concept already registered: ${concept.id}`);
|
|
36
|
+
}
|
|
37
|
+
concepts.set(concept.id, concept);
|
|
38
|
+
return concept;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function get(id) {
|
|
42
|
+
return concepts.get(String(id)) || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function list() {
|
|
46
|
+
return [...concepts.values()];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const concept of initial) register(concept);
|
|
50
|
+
|
|
51
|
+
return Object.freeze({ register, get, list });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function resolveProductConceptTarget(
|
|
55
|
+
conceptInput,
|
|
56
|
+
{
|
|
57
|
+
profileRegistry,
|
|
58
|
+
availableProviders = [],
|
|
59
|
+
preferredProvider = null,
|
|
60
|
+
} = {},
|
|
61
|
+
) {
|
|
62
|
+
const concept = normalizeProductConcept(conceptInput);
|
|
63
|
+
if (!profileRegistry?.get) {
|
|
64
|
+
throw new TypeError("profileRegistry is required");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const available = new Set(
|
|
68
|
+
availableProviders.map(normalizeProvider).filter(Boolean),
|
|
69
|
+
);
|
|
70
|
+
const preferred = normalizeProvider(preferredProvider);
|
|
71
|
+
|
|
72
|
+
const ranked = concept.targets
|
|
73
|
+
.filter((target) => target.enabled)
|
|
74
|
+
.map((target) => {
|
|
75
|
+
const profile = target.profileId
|
|
76
|
+
? profileRegistry.get(target.profileId)
|
|
77
|
+
: null;
|
|
78
|
+
const providerAvailable =
|
|
79
|
+
available.size === 0 || available.has(target.provider);
|
|
80
|
+
const preferredBoost =
|
|
81
|
+
preferred && target.provider === preferred ? -1000 : 0;
|
|
82
|
+
return {
|
|
83
|
+
target,
|
|
84
|
+
profile,
|
|
85
|
+
providerAvailable,
|
|
86
|
+
rank: target.priority + preferredBoost,
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
|
+
.filter((entry) => entry.profile && entry.providerAvailable)
|
|
90
|
+
.sort(
|
|
91
|
+
(a, b) =>
|
|
92
|
+
a.rank - b.rank ||
|
|
93
|
+
String(a.target.provider).localeCompare(String(b.target.provider)),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const selected = ranked[0] || null;
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
conceptId: concept.id,
|
|
99
|
+
selected: selected
|
|
100
|
+
? Object.freeze({
|
|
101
|
+
provider: selected.target.provider,
|
|
102
|
+
profileId: selected.target.profileId,
|
|
103
|
+
profile: selected.profile,
|
|
104
|
+
adapter: selected.target.adapter || null,
|
|
105
|
+
metadata: selected.target.metadata || null,
|
|
106
|
+
})
|
|
107
|
+
: null,
|
|
108
|
+
candidates: Object.freeze(
|
|
109
|
+
ranked.map((entry) =>
|
|
110
|
+
Object.freeze({
|
|
111
|
+
provider: entry.target.provider,
|
|
112
|
+
profileId: entry.target.profileId,
|
|
113
|
+
priority: entry.target.priority,
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { validateManufacturingProfile } from "./profile.js";
|
|
2
|
+
|
|
3
|
+
function text(value) {
|
|
4
|
+
return value == null ? "" : String(value).trim().toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function asArray(value) {
|
|
8
|
+
if (value == null) return [];
|
|
9
|
+
return Array.isArray(value) ? value : [value];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function equalsAny(expected, actual) {
|
|
13
|
+
const target = text(actual);
|
|
14
|
+
return asArray(expected).some((value) => text(value) === target);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function scoreProfile(profile, context = {}) {
|
|
18
|
+
const match = profile.match || {};
|
|
19
|
+
let score = 0;
|
|
20
|
+
|
|
21
|
+
if (match.manufacturer != null) {
|
|
22
|
+
if (!equalsAny(match.manufacturer, context.manufacturer)) return -1;
|
|
23
|
+
score += 20;
|
|
24
|
+
}
|
|
25
|
+
if (match.process != null) {
|
|
26
|
+
if (!equalsAny(match.process, context.process)) return -1;
|
|
27
|
+
score += 20;
|
|
28
|
+
}
|
|
29
|
+
if (match.locationName != null) {
|
|
30
|
+
if (!equalsAny(match.locationName, context.locationName)) return -1;
|
|
31
|
+
score += 8;
|
|
32
|
+
}
|
|
33
|
+
if (match.productType != null) {
|
|
34
|
+
if (!equalsAny(match.productType, context.productType)) return -1;
|
|
35
|
+
score += 6;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const width = Number(context.printWidthIn || 0);
|
|
39
|
+
const height = Number(context.printHeightIn || 0);
|
|
40
|
+
|
|
41
|
+
if (match.minWidthIn != null) {
|
|
42
|
+
if (!width || width < Number(match.minWidthIn)) return -1;
|
|
43
|
+
score += 3;
|
|
44
|
+
}
|
|
45
|
+
if (match.maxWidthIn != null) {
|
|
46
|
+
if (!width || width > Number(match.maxWidthIn)) return -1;
|
|
47
|
+
score += 3;
|
|
48
|
+
}
|
|
49
|
+
if (match.minHeightIn != null) {
|
|
50
|
+
if (!height || height < Number(match.minHeightIn)) return -1;
|
|
51
|
+
score += 3;
|
|
52
|
+
}
|
|
53
|
+
if (match.maxHeightIn != null) {
|
|
54
|
+
if (!height || height > Number(match.maxHeightIn)) return -1;
|
|
55
|
+
score += 3;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return score;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createManufacturingProfileRegistry(initialProfiles = []) {
|
|
62
|
+
const profiles = new Map();
|
|
63
|
+
|
|
64
|
+
function register(input, { replace = false } = {}) {
|
|
65
|
+
const result = validateManufacturingProfile(input);
|
|
66
|
+
if (!result.ok) {
|
|
67
|
+
throw new TypeError(`Invalid manufacturing profile: ${result.errors.join("; ")}`);
|
|
68
|
+
}
|
|
69
|
+
if (!replace && profiles.has(result.profile.id)) {
|
|
70
|
+
throw new Error(`Manufacturing profile already registered: ${result.profile.id}`);
|
|
71
|
+
}
|
|
72
|
+
profiles.set(result.profile.id, result.profile);
|
|
73
|
+
return result.profile;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function get(id) {
|
|
77
|
+
return profiles.get(String(id)) || null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function list(filter = {}) {
|
|
81
|
+
return [...profiles.values()].filter((profile) => {
|
|
82
|
+
if (filter.manufacturer && profile.manufacturer !== text(filter.manufacturer)) return false;
|
|
83
|
+
if (filter.process && profile.process !== text(filter.process)) return false;
|
|
84
|
+
return true;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function select(context = {}) {
|
|
89
|
+
return (
|
|
90
|
+
list()
|
|
91
|
+
.map((profile) => ({ profile, score: scoreProfile(profile, context) }))
|
|
92
|
+
.filter((entry) => entry.score >= 0)
|
|
93
|
+
.sort((a, b) => b.score - a.score || a.profile.id.localeCompare(b.profile.id))[0]?.profile || null
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function unregister(id) {
|
|
98
|
+
return profiles.delete(String(id));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const profile of initialProfiles) register(profile);
|
|
102
|
+
|
|
103
|
+
return Object.freeze({ register, get, list, select, unregister });
|
|
104
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export const MANUFACTURING_PROFILE_SCHEMA = "agentsam.manufacturing-profile.v1";
|
|
2
|
+
|
|
3
|
+
export const RASTER_FORMATS = Object.freeze(["png", "jpg", "jpeg", "webp", "avif", "gif", "tiff"]);
|
|
4
|
+
export const VECTOR_FORMATS = Object.freeze(["svg", "eps", "ai"]);
|
|
5
|
+
export const DOCUMENT_FORMATS = Object.freeze(["pdf"]);
|
|
6
|
+
|
|
7
|
+
const supportedFormats = new Set([...RASTER_FORMATS, ...VECTOR_FORMATS, ...DOCUMENT_FORMATS]);
|
|
8
|
+
|
|
9
|
+
function cleanString(value) {
|
|
10
|
+
return value == null ? null : String(value).trim();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function positiveNumber(value) {
|
|
14
|
+
const number = Number(value);
|
|
15
|
+
return Number.isFinite(number) && number > 0 ? number : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function booleanOr(value, fallback = false) {
|
|
19
|
+
return typeof value === "boolean" ? value : fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function normalizeFormat(value) {
|
|
23
|
+
const format = cleanString(value)?.toLowerCase().replace(/^\./, "") || null;
|
|
24
|
+
return format === "jpeg" ? "jpg" : format;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function mediaKindForFormat(value) {
|
|
28
|
+
const format = normalizeFormat(value);
|
|
29
|
+
if (!format) return "unknown";
|
|
30
|
+
if (RASTER_FORMATS.includes(format)) return "raster";
|
|
31
|
+
if (VECTOR_FORMATS.includes(format)) return "vector";
|
|
32
|
+
if (DOCUMENT_FORMATS.includes(format)) return "document";
|
|
33
|
+
return "unknown";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function normalizeResolutionPolicy(input = {}, fallbackPpi = null) {
|
|
37
|
+
const readyPpi = positiveNumber(input.readyPpi) || positiveNumber(fallbackPpi);
|
|
38
|
+
const minimumPpi = positiveNumber(input.minimumPpi) || readyPpi;
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
readyPpi,
|
|
41
|
+
minimumPpi,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function normalizeManufacturingProfile(input = {}) {
|
|
46
|
+
const format = normalizeFormat(input.format || input.preferredFormat);
|
|
47
|
+
const formatKind = mediaKindForFormat(format);
|
|
48
|
+
const allowRaster =
|
|
49
|
+
typeof input.allowRaster === "boolean" ? input.allowRaster : formatKind === "raster";
|
|
50
|
+
const allowVector =
|
|
51
|
+
typeof input.allowVector === "boolean" ? input.allowVector : formatKind === "vector";
|
|
52
|
+
|
|
53
|
+
const ppi = positiveNumber(input.ppi);
|
|
54
|
+
const resolution = normalizeResolutionPolicy(input.resolution || {}, ppi);
|
|
55
|
+
|
|
56
|
+
const autoNormalizeInput =
|
|
57
|
+
input.autoNormalize && typeof input.autoNormalize === "object" ? input.autoNormalize : {};
|
|
58
|
+
const autoNormalize = Object.freeze({
|
|
59
|
+
trimAlpha: booleanOr(autoNormalizeInput.trimAlpha, false),
|
|
60
|
+
colorSpace: booleanOr(autoNormalizeInput.colorSpace, false),
|
|
61
|
+
svgOptimize: booleanOr(autoNormalizeInput.svgOptimize, false),
|
|
62
|
+
outlineText: booleanOr(autoNormalizeInput.outlineText, false),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const vectorizationInput =
|
|
66
|
+
input.vectorization && typeof input.vectorization === "object" ? input.vectorization : {};
|
|
67
|
+
const vectorization = Object.freeze({
|
|
68
|
+
enabled: booleanOr(vectorizationInput.enabled, false),
|
|
69
|
+
preferredTracer: cleanString(vectorizationInput.preferredTracer)?.toLowerCase() || null,
|
|
70
|
+
maxAspectDrift: positiveNumber(vectorizationInput.maxAspectDrift) ?? 0.02,
|
|
71
|
+
minBoundaryIntegrity:
|
|
72
|
+
Number.isFinite(Number(vectorizationInput.minBoundaryIntegrity))
|
|
73
|
+
? Math.max(0, Math.min(1, Number(vectorizationInput.minBoundaryIntegrity)))
|
|
74
|
+
: 0.98,
|
|
75
|
+
outputFormat: normalizeFormat(vectorizationInput.outputFormat || "svg"),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
...input,
|
|
80
|
+
schemaVersion: cleanString(input.schemaVersion) || MANUFACTURING_PROFILE_SCHEMA,
|
|
81
|
+
id: cleanString(input.id),
|
|
82
|
+
manufacturer: cleanString(input.manufacturer)?.toLowerCase() || null,
|
|
83
|
+
process: cleanString(input.process)?.toLowerCase() || null,
|
|
84
|
+
format,
|
|
85
|
+
preferredFormat: format,
|
|
86
|
+
colorSpace: cleanString(input.colorSpace)?.toLowerCase() || null,
|
|
87
|
+
colorMode: cleanString(input.colorMode)?.toLowerCase() || null,
|
|
88
|
+
ppi: resolution.readyPpi || ppi,
|
|
89
|
+
resolution,
|
|
90
|
+
maxBytes: positiveNumber(input.maxBytes),
|
|
91
|
+
preferredPrintWidthIn: positiveNumber(input.preferredPrintWidthIn),
|
|
92
|
+
preferredPrintHeightIn: positiveNumber(input.preferredPrintHeightIn),
|
|
93
|
+
allowRaster,
|
|
94
|
+
allowVector,
|
|
95
|
+
allowGradients: input.allowGradients !== false,
|
|
96
|
+
requireClosedPaths: input.requireClosedPaths === true,
|
|
97
|
+
requireViewBox: input.requireViewBox === true,
|
|
98
|
+
outlineText: input.outlineText === true,
|
|
99
|
+
requireAlpha: input.requireAlpha === true,
|
|
100
|
+
alpha: input.alpha === true,
|
|
101
|
+
autoNormalize,
|
|
102
|
+
vectorization,
|
|
103
|
+
handoff:
|
|
104
|
+
input.handoff && typeof input.handoff === "object"
|
|
105
|
+
? Object.freeze({ ...input.handoff })
|
|
106
|
+
: null,
|
|
107
|
+
match:
|
|
108
|
+
input.match && typeof input.match === "object"
|
|
109
|
+
? Object.freeze({ ...input.match })
|
|
110
|
+
: null,
|
|
111
|
+
source:
|
|
112
|
+
input.source && typeof input.source === "object"
|
|
113
|
+
? Object.freeze({ ...input.source })
|
|
114
|
+
: null,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function validateManufacturingProfile(input) {
|
|
119
|
+
const profile = normalizeManufacturingProfile(input);
|
|
120
|
+
const errors = [];
|
|
121
|
+
|
|
122
|
+
if (!profile.id) errors.push("id is required");
|
|
123
|
+
if (!profile.manufacturer) errors.push("manufacturer is required");
|
|
124
|
+
if (!profile.process) errors.push("process is required");
|
|
125
|
+
if (!profile.format) errors.push("format or preferredFormat is required");
|
|
126
|
+
if (profile.format && !supportedFormats.has(profile.format)) {
|
|
127
|
+
errors.push(`unsupported format: ${profile.format}`);
|
|
128
|
+
}
|
|
129
|
+
if (!profile.allowRaster && !profile.allowVector && profile.format !== "pdf") {
|
|
130
|
+
errors.push("profile must allow raster, vector, or a documented handoff format");
|
|
131
|
+
}
|
|
132
|
+
if (profile.ppi != null && profile.ppi < 36) {
|
|
133
|
+
errors.push("ppi is implausibly low; use null for resolution-independent processes");
|
|
134
|
+
}
|
|
135
|
+
if (
|
|
136
|
+
profile.resolution.readyPpi != null &&
|
|
137
|
+
profile.resolution.minimumPpi != null &&
|
|
138
|
+
profile.resolution.minimumPpi > profile.resolution.readyPpi
|
|
139
|
+
) {
|
|
140
|
+
errors.push("resolution.minimumPpi cannot exceed resolution.readyPpi");
|
|
141
|
+
}
|
|
142
|
+
if (profile.vectorization.enabled && !profile.allowVector) {
|
|
143
|
+
errors.push("vectorization.enabled requires allowVector=true");
|
|
144
|
+
}
|
|
145
|
+
if (
|
|
146
|
+
profile.vectorization.enabled &&
|
|
147
|
+
profile.vectorization.outputFormat &&
|
|
148
|
+
mediaKindForFormat(profile.vectorization.outputFormat) !== "vector"
|
|
149
|
+
) {
|
|
150
|
+
errors.push("vectorization.outputFormat must be a vector format");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
ok: errors.length === 0,
|
|
155
|
+
errors: Object.freeze(errors),
|
|
156
|
+
profile,
|
|
157
|
+
});
|
|
158
|
+
}
|