blogwright-core 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.
Files changed (84) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +7 -0
  3. package/dist/adapters/memory-fs.d.ts +9 -0
  4. package/dist/adapters/memory-fs.js +54 -0
  5. package/dist/adapters/memory-fs.js.map +1 -0
  6. package/dist/adapters/node-fs.d.ts +4 -0
  7. package/dist/adapters/node-fs.js +65 -0
  8. package/dist/adapters/node-fs.js.map +1 -0
  9. package/dist/adapters/node-terminal.d.ts +29 -0
  10. package/dist/adapters/node-terminal.js +58 -0
  11. package/dist/adapters/node-terminal.js.map +1 -0
  12. package/dist/adapters/script-terminal.d.ts +22 -0
  13. package/dist/adapters/script-terminal.js +38 -0
  14. package/dist/adapters/script-terminal.js.map +1 -0
  15. package/dist/aws/acm.d.ts +20 -0
  16. package/dist/aws/acm.js +55 -0
  17. package/dist/aws/acm.js.map +1 -0
  18. package/dist/aws/cloudfront.d.ts +72 -0
  19. package/dist/aws/cloudfront.js +341 -0
  20. package/dist/aws/cloudfront.js.map +1 -0
  21. package/dist/aws/credentials.d.ts +19 -0
  22. package/dist/aws/credentials.js +36 -0
  23. package/dist/aws/credentials.js.map +1 -0
  24. package/dist/aws/endpoint.d.ts +29 -0
  25. package/dist/aws/endpoint.js +58 -0
  26. package/dist/aws/endpoint.js.map +1 -0
  27. package/dist/aws/errors.d.ts +20 -0
  28. package/dist/aws/errors.js +35 -0
  29. package/dist/aws/errors.js.map +1 -0
  30. package/dist/aws/form.d.ts +2 -0
  31. package/dist/aws/form.js +11 -0
  32. package/dist/aws/form.js.map +1 -0
  33. package/dist/aws/iam.d.ts +18 -0
  34. package/dist/aws/iam.js +122 -0
  35. package/dist/aws/iam.js.map +1 -0
  36. package/dist/aws/logs.d.ts +32 -0
  37. package/dist/aws/logs.js +148 -0
  38. package/dist/aws/logs.js.map +1 -0
  39. package/dist/aws/microvms.d.ts +85 -0
  40. package/dist/aws/microvms.js +182 -0
  41. package/dist/aws/microvms.js.map +1 -0
  42. package/dist/aws/route53.d.ts +19 -0
  43. package/dist/aws/route53.js +71 -0
  44. package/dist/aws/route53.js.map +1 -0
  45. package/dist/aws/s3.d.ts +31 -0
  46. package/dist/aws/s3.js +178 -0
  47. package/dist/aws/s3.js.map +1 -0
  48. package/dist/aws/secretsmanager.d.ts +21 -0
  49. package/dist/aws/secretsmanager.js +84 -0
  50. package/dist/aws/secretsmanager.js.map +1 -0
  51. package/dist/aws/signer.d.ts +44 -0
  52. package/dist/aws/signer.js +144 -0
  53. package/dist/aws/signer.js.map +1 -0
  54. package/dist/aws/sts.d.ts +7 -0
  55. package/dist/aws/sts.js +22 -0
  56. package/dist/aws/sts.js.map +1 -0
  57. package/dist/aws/xml.d.ts +18 -0
  58. package/dist/aws/xml.js +65 -0
  59. package/dist/aws/xml.js.map +1 -0
  60. package/dist/clients.d.ts +33 -0
  61. package/dist/clients.js +38 -0
  62. package/dist/clients.js.map +1 -0
  63. package/dist/colors.d.ts +11 -0
  64. package/dist/colors.js +24 -0
  65. package/dist/colors.js.map +1 -0
  66. package/dist/config.d.ts +136 -0
  67. package/dist/config.js +236 -0
  68. package/dist/config.js.map +1 -0
  69. package/dist/index.d.ts +26 -0
  70. package/dist/index.js +27 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/ports.d.ts +53 -0
  73. package/dist/ports.js +15 -0
  74. package/dist/ports.js.map +1 -0
  75. package/dist/repo-root.d.ts +8 -0
  76. package/dist/repo-root.js +20 -0
  77. package/dist/repo-root.js.map +1 -0
  78. package/dist/state.d.ts +23 -0
  79. package/dist/state.js +44 -0
  80. package/dist/state.js.map +1 -0
  81. package/dist/util.d.ts +15 -0
  82. package/dist/util.js +38 -0
  83. package/dist/util.js.map +1 -0
  84. package/package.json +42 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Repo-owned ports shared across packages. Domain code depends on these
3
+ * interfaces; adapters (see `adapters/`) implement them against real
4
+ * infrastructure and are constructed only at a composition root.
5
+ */
6
+ /**
7
+ * File access in domain vocabulary. Paths are platform paths (absolute in
8
+ * practice); implementations raise {@link FileNotFoundError} for absent files
9
+ * so callers can branch on absence without knowing adapter error codes.
10
+ */
11
+ export interface FileSystem {
12
+ /** Read a UTF-8 text file. Throws {@link FileNotFoundError} when absent. */
13
+ readText(path: string): Promise<string>;
14
+ /** Read a file's raw bytes. Throws {@link FileNotFoundError} when absent. */
15
+ readBytes(path: string): Promise<Uint8Array>;
16
+ /** Write a UTF-8 text file, creating parent directories as needed. */
17
+ writeText(path: string, text: string): Promise<void>;
18
+ /** True when a file or directory exists at the path. */
19
+ exists(path: string): Promise<boolean>;
20
+ /**
21
+ * List every file under `dir` recursively as sorted, `dir`-relative paths.
22
+ * Throws {@link FileNotFoundError} when `dir` does not exist.
23
+ */
24
+ listFiles(dir: string): Promise<string[]>;
25
+ }
26
+ /**
27
+ * The operator's terminal in domain vocabulary: leveled line output plus one
28
+ * question/answer primitive. `isInteractive` reflects TTY attachment, captured
29
+ * once when the adapter is constructed; callers key formatting (color) and
30
+ * prompting decisions off it.
31
+ */
32
+ export interface Terminal {
33
+ /** True when the session has an interactive TTY on both input and output. */
34
+ readonly isInteractive: boolean;
35
+ /** Write one line to standard output. */
36
+ write(line: string): void;
37
+ /** Write one line to standard error. */
38
+ error(line: string): void;
39
+ /**
40
+ * Show a transient single-line status (elapsed time, progress). Each call
41
+ * replaces the previous status; the empty string clears it. Adapters that
42
+ * cannot rewrite a line (piped output, CI, plain mode) make this a no-op,
43
+ * so callers must still emit durable lines for those sessions.
44
+ */
45
+ status(line: string): void;
46
+ /** Show `prompt` and resolve with the operator's answer (newline excluded). */
47
+ question(prompt: string): Promise<string>;
48
+ }
49
+ /** Raised by {@link FileSystem} implementations when a path has no file. */
50
+ export declare class FileNotFoundError extends Error {
51
+ readonly path: string;
52
+ constructor(path: string);
53
+ }
package/dist/ports.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Repo-owned ports shared across packages. Domain code depends on these
3
+ * interfaces; adapters (see `adapters/`) implement them against real
4
+ * infrastructure and are constructed only at a composition root.
5
+ */
6
+ /** Raised by {@link FileSystem} implementations when a path has no file. */
7
+ export class FileNotFoundError extends Error {
8
+ path;
9
+ constructor(path) {
10
+ super(`file not found: ${path}`);
11
+ this.name = 'FileNotFoundError';
12
+ this.path = path;
13
+ }
14
+ }
15
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA+CH,4EAA4E;AAC5E,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,CAAS;IAEtB,YAAY,IAAY;QACtB,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import type { FileSystem } from 'blogwright-core';
2
+ /**
3
+ * Locate the site repo root by walking up to the VCS directory (`.git`, which is
4
+ * a file in worktrees, or `.jj`). The pds commands read/write repo files
5
+ * (public/, src/) and the deploy zips `git ls-files` output, so the CLI must
6
+ * anchor on the checkout root regardless of the invocation directory.
7
+ */
8
+ export declare function findRepoRoot(fs: FileSystem, start?: string): Promise<string>;
@@ -0,0 +1,20 @@
1
+ import { dirname, join, resolve } from 'node:path';
2
+ /**
3
+ * Locate the site repo root by walking up to the VCS directory (`.git`, which is
4
+ * a file in worktrees, or `.jj`). The pds commands read/write repo files
5
+ * (public/, src/) and the deploy zips `git ls-files` output, so the CLI must
6
+ * anchor on the checkout root regardless of the invocation directory.
7
+ */
8
+ export async function findRepoRoot(fs, start = process.cwd()) {
9
+ let dir = resolve(start);
10
+ for (;;) {
11
+ if ((await fs.exists(join(dir, '.git'))) || (await fs.exists(join(dir, '.jj'))))
12
+ return dir;
13
+ const parent = dirname(dir);
14
+ if (parent === dir) {
15
+ throw new Error(`could not find the repo root (no .git or .jj above ${start})`);
16
+ }
17
+ dir = parent;
18
+ }
19
+ }
20
+ //# sourceMappingURL=repo-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-root.js","sourceRoot":"","sources":["../src/repo-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAInD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAc,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE;IACtE,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,SAAS,CAAC;QACR,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC5F,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,sDAAsD,KAAK,GAAG,CAAC,CAAC;QAClF,CAAC;QACD,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { S3Client } from './aws/s3.js';
2
+ export type ResourceOutputs = Record<string, string | number | boolean | string[]>;
3
+ export interface OpsState {
4
+ version: number;
5
+ env: string;
6
+ updatedAt: string | undefined;
7
+ /** nodeId -> recorded outputs (ARNs, ids, domains). */
8
+ resources: Record<string, ResourceOutputs>;
9
+ }
10
+ export declare function emptyState(env: string): OpsState;
11
+ /**
12
+ * S3-backed topology state. Lives at `s3://<bucket>/state/<env>.json` so it is shared
13
+ * across machines and is the single source of truth for what has been provisioned.
14
+ */
15
+ export declare class StateStore {
16
+ private readonly s3;
17
+ private readonly bucket;
18
+ private readonly env;
19
+ constructor(s3: S3Client, bucket: string, env: string);
20
+ load(): Promise<OpsState>;
21
+ save(state: OpsState): Promise<void>;
22
+ delete(): Promise<void>;
23
+ }
package/dist/state.js ADDED
@@ -0,0 +1,44 @@
1
+ export function emptyState(env) {
2
+ return { version: 1, env, updatedAt: undefined, resources: {} };
3
+ }
4
+ function stateKey(env) {
5
+ return `state/${env}.json`;
6
+ }
7
+ /**
8
+ * S3-backed topology state. Lives at `s3://<bucket>/state/<env>.json` so it is shared
9
+ * across machines and is the single source of truth for what has been provisioned.
10
+ */
11
+ export class StateStore {
12
+ s3;
13
+ bucket;
14
+ env;
15
+ constructor(s3, bucket, env) {
16
+ this.s3 = s3;
17
+ this.bucket = bucket;
18
+ this.env = env;
19
+ }
20
+ async load() {
21
+ // getObjectText returns undefined only when the object/bucket does not exist (a fresh
22
+ // environment). A present-but-corrupt document must NOT be silently treated as empty —
23
+ // that would cause duplicate-resource creation — so let a parse error surface.
24
+ const text = await this.s3.getObjectText(this.bucket, stateKey(this.env));
25
+ // Strictly undefined: a present-but-empty (zero-byte) state object is
26
+ // corruption, not a fresh environment, and must hit the guard below.
27
+ if (text === undefined)
28
+ return emptyState(this.env);
29
+ try {
30
+ return JSON.parse(text);
31
+ }
32
+ catch (err) {
33
+ throw new Error(`state/${this.env}.json in s3://${this.bucket} is not valid JSON — refusing to proceed with empty state`, { cause: err });
34
+ }
35
+ }
36
+ async save(state) {
37
+ state.updatedAt = new Date().toISOString();
38
+ await this.s3.putObject(this.bucket, stateKey(this.env), JSON.stringify(state, null, 2), 'application/json');
39
+ }
40
+ async delete() {
41
+ await this.s3.deleteObject(this.bucket, stateKey(this.env)).catch(() => undefined);
42
+ }
43
+ }
44
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,SAAS,GAAG,OAAO,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,UAAU;IAEF;IACA;IACA;IAHnB,YACmB,EAAY,EACZ,MAAc,EACd,GAAW;QAFX,OAAE,GAAF,EAAE,CAAU;QACZ,WAAM,GAAN,MAAM,CAAQ;QACd,QAAG,GAAH,GAAG,CAAQ;IAC3B,CAAC;IAEJ,KAAK,CAAC,IAAI;QACR,sFAAsF;QACtF,uFAAuF;QACvF,+EAA+E;QAC/E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1E,sEAAsE;QACtE,qEAAqE;QACrE,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,SAAS,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,MAAM,2DAA2D,EACxG,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAe;QACxB,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CACrB,IAAI,CAAC,MAAM,EACX,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAClB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAC9B,kBAAkB,CACnB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACrF,CAAC;CACF"}
package/dist/util.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export declare function sleep(ms: number): Promise<void>;
2
+ export interface RetryOptions {
3
+ attempts?: number;
4
+ baseDelayMs?: number;
5
+ maxDelayMs?: number;
6
+ /** Predicate for whether an error is retryable (defaults to isRetryable). */
7
+ retryable?: (err: unknown) => boolean;
8
+ }
9
+ /** Retry a fn on transient AWS/network errors with exponential backoff. */
10
+ export declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
11
+ /** Poll `fn` until `done` returns true or the deadline passes. Returns the last value. */
12
+ export declare function pollUntil<T>(fn: () => Promise<T>, done: (value: T) => boolean, opts: {
13
+ intervalMs: number;
14
+ timeoutMs: number;
15
+ }): Promise<T>;
package/dist/util.js ADDED
@@ -0,0 +1,38 @@
1
+ import { isRetryable } from './aws/errors.js';
2
+ export function sleep(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ /** Retry a fn on transient AWS/network errors with exponential backoff. */
6
+ export async function withRetry(fn, opts = {}) {
7
+ const attempts = opts.attempts ?? 5;
8
+ const base = opts.baseDelayMs ?? 200;
9
+ const max = opts.maxDelayMs ?? 5000;
10
+ const retryable = opts.retryable ?? isRetryable;
11
+ let lastErr;
12
+ for (let i = 0; i < attempts; i++) {
13
+ try {
14
+ return await fn();
15
+ }
16
+ catch (err) {
17
+ lastErr = err;
18
+ if (i === attempts - 1 || !retryable(err))
19
+ throw err;
20
+ const delay = Math.min(max, base * 2 ** i);
21
+ await sleep(delay);
22
+ }
23
+ }
24
+ throw lastErr;
25
+ }
26
+ /** Poll `fn` until `done` returns true or the deadline passes. Returns the last value. */
27
+ export async function pollUntil(fn, done, opts) {
28
+ const deadline = Date.now() + opts.timeoutMs;
29
+ let value = await fn();
30
+ while (!done(value)) {
31
+ if (Date.now() >= deadline)
32
+ return value;
33
+ await sleep(opts.intervalMs);
34
+ value = await fn();
35
+ }
36
+ return value;
37
+ }
38
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAUD,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,EAAoB,EAAE,OAAqB,EAAE;IAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,WAAW,CAAC;IAChD,IAAI,OAAgB,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,GAAG,CAAC;YACd,IAAI,CAAC,KAAK,QAAQ,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAC;YACrD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3C,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,MAAM,OAAO,CAAC;AAChB,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAoB,EACpB,IAA2B,EAC3B,IAA+C;IAE/C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IAC7C,IAAI,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC;IACvB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;YAAE,OAAO,KAAK,CAAC;QACzC,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7B,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC;IACrB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "blogwright-core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "dependencies": {
16
+ "@aws-crypto/sha256-js": "^5.2.0",
17
+ "@aws-sdk/credential-providers": "^3.1079.0",
18
+ "@smithy/protocol-http": "^5.5.6",
19
+ "@smithy/signature-v4": "^5.6.2"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^26.1.0",
23
+ "oxlint": "^1.72.0",
24
+ "typescript": "^6.0.3",
25
+ "vitest": "^4.1.9"
26
+ },
27
+ "description": "SigV4 transport, per-service AWS HTTP clients, config, and S3 state store for blogwright",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/antstanley/blogwright.git"
32
+ },
33
+ "engines": {
34
+ "node": ">=22"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json",
38
+ "typecheck": "tsc -p tsconfig.typecheck.json",
39
+ "lint": "oxlint src",
40
+ "test": "vitest run"
41
+ }
42
+ }