/** * Node-only `Assets` namespace enrichments (v1.48 unified-apply). * * `NodeAssets` extends the isomorphic {@link Assets} with directory-walking * helpers — `uploadDir` (additive), `syncDir` (declarative prune), and * `prepareDir` (pre-commit URL injection) — that read bytes from disk * lazily, compute SHA-256s in streaming chunks, and submit through the * single hero `r.project(id).apply(spec)` engine. See design D8/D10/D11. * * Imports `node:fs/promises` via `fileSetFromDir`, so this module is * Node-only — V8 isolates use `r.project(id).assets.putMany(items)` with * in-memory byte sources. */ import { Assets } from "../namespaces/assets.js"; import { LocalError } from "../errors.js"; import type { AssetPutEntryInput, AssetSpec, AssetSyncPruneConfirm, ContentSource, DeployEvent, LocalDirRef } from "../namespaces/deploy.types.js"; /** * SDK-input-only marker for "walk this directory at submission time." The * canonical type now lives in `deploy.types.ts` so the isomorphic SDK can * reference it from `SiteSpec` / `AssetSpec` without a dependency on this * Node-only module. Re-exported here for backwards compatibility with * `@run402/sdk/node` callers (and to keep `dir()` co-located with its * return type). * * The gateway never sees a `LocalDirRef` — submitting one in a JSON body * is rejected with HTTP 400 `INVALID_WIRE_SCHEMA`. The kind discriminator * `"local-dir"` is stable for type-narrowing. */ export type { LocalDirRef }; export interface DirOptions { /** Optional key prefix applied to every walked entry. `"static/"` → * every file's relative path becomes `"static/"`. */ prefix?: string; /** Additional file/dir names to skip at any depth. Merged with the * defaults (`.git`, `node_modules`, `.DS_Store`, and sensitive * filenames unless `includeSensitive: true`). */ ignore?: ReadonlyArray; /** Opt in to collecting `.env`-style files. Same semantics as * `fileSetFromDir`. */ includeSensitive?: boolean; } /** * Build a `LocalDirRef` for the given filesystem path. Synchronous — the * actual filesystem walk happens at `apply()` submission time per design * D12 ("dir(path) returns synchronous LocalDirRef; SDK-input-only"). * * Accepted as `site.replace` and `site.patch.put` on `r.project(p).apply`. * For assets, use `r.project(p).assets.uploadDir(path)` / * `syncDir` / `prepareDir` — those wrap a `LocalDirRef` walk into the * unified-apply pipeline with directory-specific options (prune, prepare, * progress events). * * @example * import { dir, run402 } from "@run402/sdk/node"; * const r = run402(); * await r.project(p).apply({ * site: dir("./dist"), * }); * await r.project(p).assets.uploadDir("./assets", { prefix: "static/" }); */ export declare function dir(path: string, opts?: DirOptions): LocalDirRef; /** * Walk a `LocalDirRef` and emit `AssetPutEntryInput[]` carrying a * `ContentSource` per file (rather than a pre-computed SHA). The actual * hashing + byte-reader registration happens inside the SDK's * `normalizeAssetSlice` pipeline so the bytes are uploaded via the shared * `byteReaders` map (v1.48 unified-apply). Returning wire-shaped entries * here would skip byte-reader registration and the deploy would fail with * `Missing bytes for sha=<...>` at upload time. * * The prefix is applied to relative keys. content_type/visibility/immutable * are left to the normalizer's defaults (visibility=public, immutable=true, * content_type derived from extension). */ export declare function entriesFromLocalDir(ref: LocalDirRef): Promise; export interface AssetManifestEntry { key: string; sha256: string; size_bytes: number; content_type: string; visibility: "public" | "private"; url: string | null; immutable_url: string | null; cdn_url: string | null; cdn_immutable_url: string | null; sri: string | null; etag: string | null; content_digest: string | null; } export interface AssetManifestTotals { files: number; bytes_uploaded: number; bytes_reused: number; duration_ms: number; } /** * Batch asset operation result (per design D9). `list` and `byKey` * share the same `AssetManifestEntry` instances in memory; `manifest` * is a plain-data shallow copy suitable for JSON serialization. * `byKey` and `manifest` are constructed with `Object.create(null)` so * attacker-controlled keys like `__proto__` don't collide with * `Object.prototype`. * * Note: v1.48 surfaces plain-data entries (URLs + sha + metadata) only. * The richer `AssetRef` shape with HTML tag emitters lands once the * gateway plan-response enrichment is wired (Phase 3.5 follow-up). */ export interface AssetManifest { list: AssetManifestEntry[]; byKey: Record; manifest: Record; totals: AssetManifestTotals; /** Present when `syncDir({ prune: true })` ran. */ pruned?: string[]; } export interface PutManyItem { key: string; source: ContentSource; contentType?: string; visibility?: "public" | "private"; immutable?: boolean; /** v1.50: per-item caller-provided metadata. Same shape and validation * as {@link AssetPutEntryInput.metadata}. */ metadata?: Record; /** v1.50: per-item EXIF policy override. */ exifPolicy?: "keep" | "strip"; } export interface UploadDirOptions extends DirOptions { /** Project id the apply targets. */ project: string; /** Optional progress callback. */ onEvent?: (event: DeployEvent) => void; /** v1.50: default metadata applied to every entry walked from the * directory. Per-call default — entries that need per-key metadata * should use {@link NodeAssets.putMany} instead. */ metadata?: Record; /** v1.50: default EXIF policy applied to every entry walked from the * directory. */ exifPolicy?: "keep" | "strip"; } export interface SyncDirOptions extends UploadDirOptions { /** Without `prune: true`, behaves additively (equivalent to * `uploadDir`). With `prune: true`, the apply is destructive and * requires a confirmation token from a prior plan call. */ prune?: boolean; /** When the caller already has a confirmation block from a prior plan, * passing it commits in one round-trip. Without it on a destructive * call, the SDK throws `LocalError` with code * `PRUNE_CONFIRMATION_REQUIRED` carrying the values to acknowledge. */ confirm?: AssetSyncPruneConfirm; } export interface PrepareDirOptions extends DirOptions { /** Project id the apply targets. */ project: string; } /** * Error thrown by `syncDir({ prune: true })` when called without a * `confirm` token. Carries the values the caller must echo back to * commit the destructive operation. The SDK auto-throws this before the * destructive apply lands so the agent has a chance to surface the * planned delete set to the user. */ export declare class PruneConfirmationRequired extends LocalError { readonly code: "PRUNE_CONFIRMATION_REQUIRED"; readonly base_revision: string; readonly delete_set_digest: string; readonly expected_delete_count: number; readonly sample_keys: string[]; constructor(args: { base_revision: string; delete_set_digest: string; expected_delete_count: number; sample_keys: string[]; }); } export declare class NodeAssets extends Assets { /** * Additive directory upload. Existing keys under the (optional) prefix * that aren't in the new directory are left untouched. Per design * D10. Wraps `r._applyEngine.apply({ project, assets: { put: ... } })`. */ uploadDir(path: string, opts: UploadDirOptions): Promise; /** * Declarative directory sync. Per design D10: * - Without `prune: true`, behaves identically to {@link uploadDir} * (additive only). * - With `prune: true` AND no `confirm` token, runs a plan first and * throws {@link PruneConfirmationRequired} carrying the values the * caller must echo back to commit. * - With `prune: true` AND a `confirm` token, commits the destructive * sync. The gateway's activation-time drift check * (`ASSET_SYNC_DRIFT`) catches the narrower race where inventory * mutates between commit and activation. */ syncDir(path: string, opts: SyncDirOptions): Promise; /** * Pre-commit URL injection helper (design D8 / spec Requirement * "assets.prepareDir"). Runs a plan against `r.project(id).apply.plan` * and returns `{ manifest, applySlice }` so the caller can render HTML * against the final resolved URLs and submit the same `applySlice` to * the commit step without re-uploading. Currently a thin shim — the * full plan-response enrichment lands in a follow-up. */ prepareDir(path: string, opts: PrepareDirOptions): Promise<{ manifest: AssetManifest; applySlice: AssetSpec; }>; /** * In-memory batch upload. Each item carries the key + an in-memory * `ContentSource` (string, Uint8Array, ArrayBuffer, Blob). SHA-256 is * computed locally, then a single apply call submits all entries. * Returns `AssetManifest`. */ putMany(items: PutManyItem[], opts: { project: string; onEvent?: (event: DeployEvent) => void; }): Promise; /** * Internal: instantiate a Deploy (engine) bound to the same Client * this Assets instance was constructed with. Avoids requiring a * separate apply-engine parameter on the namespace constructor. */ private applyEngine; } //# sourceMappingURL=assets-node.d.ts.map