forest-cli
Version:
The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.
116 lines • 5.71 kB
TypeScript
/**
* BPMN sidecar transport for `layout pull/apply --with-workflows`.
*
* A workflow's BPMN is not part of the patchable document — it lives in S3 and
* `bpmnAwsS3Identifier` is a per-environment pointer, not portable across envs.
* So the round-trip stores the bytes in `workflows/<id>.bpmn` files next to the
* layout: `pull` downloads them, `apply` re-uploads them to the target and links
* the fresh version. All the pure decision logic lives here (the commands only
* do I/O) so it can be unit-tested.
*/
import type LayoutManager from './layout-manager';
import type { LayoutScope } from './types';
/**
* A workflow id becomes a sidecar filename (`workflows/<id>.bpmn`). Because that
* id comes from a layout file, a crafted file could set it to `../../.ssh/id_rsa`
* and make `apply --with-workflows` read an arbitrary file (or `pull` write
* outside the workflows dir). Only accept ids that are a single, traversal-free
* path segment — Forest ids are UUIDs, so this is deliberately strict.
*/
export declare function isSafeWorkflowId(id: unknown): id is string;
/** The sidecar path for a workflow id, or null when the id is unsafe. */
export declare function sidecarPath(dir: string, id: unknown): string | null;
/**
* Resolve a file workflow to its target-env counterpart by DELEGATING to the
* diff engine's own resolver (`remoteResolver` in diff.ts), so the sidecar
* upload targets the very workflow the domain patch addressed — including on
* duplicate names, where the resolver's Map makes the LAST remote win. A
* dev→prod promote typically matches by NAME (each env mints its own ids).
*/
export declare function resolveRemoteWorkflow(remoteWorkflows: Array<Record<string, unknown>>, workflow: {
id: string;
name?: string;
}): Record<string, unknown> | undefined;
/** A file workflow that declares BPMN in the layout but has no sidecar on disk. */
export type MissingSidecar = {
id: string;
name: string;
};
/**
* Split the missing-sidecar workflows by what actually happens to their BPMN:
* - `targetKeepsOwn`: the workflow exists in the target env (id-then-name), so
* the strip of `bpmnAwsS3Identifier` leaves the target's own BPMN untouched;
* - `createdWithoutBpmn`: the workflow matches nothing in the target — this
* apply `add`s it, and with its declared pointer stripped and no sidecar to
* upload, it is created with NO BPMN at all. The caller must warn explicitly:
* "keeps its own" would be a lie here.
*/
export declare function partitionMissingSidecars(missing: MissingSidecar[], remoteWorkflows: Array<Record<string, unknown>>): {
createdWithoutBpmn: MissingSidecar[];
targetKeepsOwn: MissingSidecar[];
};
/**
* When `--with-workflows`, BPMN transport is owned by the sidecar upload, not by
* the JSON patch: `bpmnAwsS3Identifier` is a per-environment S3 pointer and is not
* portable. Drop it from the diff so a pulled file never patches a target workflow
* to the source env's (nonexistent) version — for workflows with a sidecar the
* upload links the fresh id; for those without, the target keeps its own BPMN.
*/
export declare function stripWorkflowBpmnOps<T extends {
op: string;
path: string;
value?: unknown;
}>(ops: T[]): T[];
/**
* True when the plan would write a `bpmnAwsS3Identifier` into the target — either
* as a replace of the pointer itself or inside an added workflow. Used by `apply`
* WITHOUT `--with-workflows` to warn on cross-environment applies: the pointer is
* env-local, so patching it into another env plants a dead S3 reference.
*/
export declare function hasWorkflowBpmnOps(ops: Array<{
op: string;
path: string;
value?: unknown;
}>): boolean;
/** A sidecar file read from disk, keyed by the SOURCE (layout-file) workflow. */
export type SidecarFile = {
bpmn: string;
workflow: {
collectionId: string;
id: string;
name: string;
};
};
/** A sidecar that must be uploaded, with the TARGET-env workflow it links to. */
export type SidecarUpload = SidecarFile & {
/**
* The workflow to upload to in the target env (resolved id-then-name), or
* null when the workflow does not exist there yet — it is being added by this
* very apply, so the target must be re-resolved after the domain patch.
*/
target: {
collectionId: string;
id: string;
} | null;
};
/**
* Decide which sidecars need uploading, resolving each to its TARGET-env
* workflow first (id-then-name, like the diff) so that:
* - a same-name workflow with a different id (dev→prod promote) is uploaded
* under the TARGET id, never the source one;
* - byte-identical BPMN is skipped (idempotent re-apply), including in that
* name-matched case.
*/
export declare function planSidecarUploads(manager: LayoutManager, scope: LayoutScope, sidecars: SidecarFile[], remoteWorkflows: Array<Record<string, unknown>>, renderingId: number): Promise<SidecarUpload[]>;
/**
* The sidecar filenames a pull should prune: files named after a server
* workflow id (`<num-or-uuid>.bpmn`) that matches no workflow left in the
* environment. Everything else is preserved:
* - files whose basename is not a server-shaped id (a user's own BPMN files,
* e.g. `review.bpmn` — pull never wrote them, so pull never deletes them);
* - sidecars of workflows still present in the env, even when their download
* was skipped (dead S3 ref) — that sidecar may be the last copy of the BPMN,
* and a backup tool must never destroy the backup.
*/
export declare function selectStaleSidecars(entries: string[], keepIds: ReadonlySet<string>): string[];
//# sourceMappingURL=workflow-sidecar.d.ts.map