@prosewire/sdk 0.2.1 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @prosewire/sdk
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c5310e0: Add post revision history and confirmed restore operations. Restores save the current post first and are available through the dashboard, management API, SDK, CLI, and MCP server.
8
+ - 01df471: Add streamed create-only Prosewire portable imports with preview and explicit apply. Import JSON v2 plus managed original media, remap publication relationships and saved revisions, preserve slugs and dates, and default imported posts to drafts. Matching unchanged imports are safely retryable; collisions never overwrite existing content.
9
+
10
+ The SDK adds `imports.run` and the Node-only `@prosewire/sdk/import-node` file helper. The CLI adds `import --data ... --media ...`, `--apply`, and `--preserve-states`. MCP adds the approval-gated `publication_import` tool. This capability requires a deployment containing the corresponding import endpoint.
11
+ - 7e23d93: Expose configured publication languages and let the server apply each publication's default language when a post create request omits its locale.
12
+ - 78fdf77: Add managed media upload, validation, processing, CDN metadata, quota, reference, deletion, and export workflows across the management clients.
13
+
14
+ ### Patch Changes
15
+
16
+ - a6a9f37: Decode typed HTTP 409 post conflicts from the shared private API contract.
17
+ - 27532b0: Upgrade the published Effect-based clients and tools to Effect 4.0.0-rc.112.
18
+ - 99a284f: Document and enforce the optional publication assertion on authenticated post listings.
19
+ - 93d88b7: Reject impossible calendar dates in post scheduling inputs and share post field validation with dashboard and application commands.
20
+ - 48d27cf: Update Effect to 4.0.0-rc.115 across the SDK, CLI, and MCP server. Adapt CLI
21
+ arguments, flags, and MCP environment configuration to the renamed Effect
22
+ constructors while preserving existing commands and settings.
23
+
24
+ Use explicit Next.js module extensions so the published reader package can be
25
+ imported by Node.js ES module consumers.
26
+ - 8528b33: Return a structured invalid-revision error when historical revision data cannot be decoded, instead of an unclassified server failure.
27
+
28
+ ## 0.3.0
29
+
30
+ ### Minor Changes
31
+
32
+ - 433ad8b: Add first-class, headless Next.js and Astro readers, monorepo-aware framework scaffolding, and public redirect resolution for static and server-rendered integrations.
33
+
34
+ ### Patch Changes
35
+
36
+ - 15bf694: Harden package documentation with corrected response shapes, complete operation boundaries, configuration details, and safer integration guidance.
37
+
3
38
  ## 0.2.1
4
39
 
5
40
  ### Patch Changes
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  Typed clients for Prosewire's authenticated management API and public content API.
4
4
 
5
+ The package is pre-1.0. Pin a compatible version and review the changelog before upgrading.
6
+
7
+ Full guide: [Build a native reader with TypeScript](https://prosewire.com/docs/integrate/typescript/)
8
+
5
9
  ## Install
6
10
 
7
11
  ```sh
@@ -21,12 +25,31 @@ const publicContent = createPublicClient({
21
25
  });
22
26
 
23
27
  const page = await publicContent.listPosts({ page: 1, pageSize: 20 });
24
- console.log(page.items.map((post) => post.title));
28
+ console.log(page.posts.map((post) => post.title));
29
+ ```
30
+
31
+ Filter lists and retrieve JSON or rendered HTML:
32
+
33
+ ```ts
34
+ const page = await publicContent.listPosts({
35
+ search: "portable",
36
+ category: "engineering",
37
+ page: 1,
38
+ pageSize: 20,
39
+ });
40
+
41
+ console.log(page.posts.map((post) => post.title));
42
+ const article = await publicContent.getPost("welcome");
43
+ const resolution = await publicContent.resolvePost("old-welcome-slug");
44
+ const redirects = await publicContent.listRedirects();
45
+ const html = await publicContent.getRendered("welcome");
25
46
  ```
26
47
 
27
- Public clients do not require a key. For private management operations, create a
28
- publication-scoped API key in **Settings → Developer** and keep it on a trusted
29
- server:
48
+ Public clients need no key and only return published content whose publication time has arrived. `pageSize` is capped at 100. `listAllPosts` follows every page for static builds, while `resolvePost` preserves the difference between a canonical post, redirect, and 404. The deprecated `limit` option remains an alias for `pageSize`.
49
+
50
+ ## Manage a publication
51
+
52
+ Create a publication-scoped API key in **Integrate → Scoped API keys** and keep it on a trusted server:
30
53
 
31
54
  ```ts
32
55
  import { createClient } from "@prosewire/sdk";
@@ -39,6 +62,38 @@ const management = createClient({
39
62
  const drafts = await management.posts.list({ status: "draft" });
40
63
  ```
41
64
 
65
+ Available Promise operations are:
66
+
67
+ - `imports.run()` (streamed portable import, `content:write`)
68
+ - `health()`
69
+ - `blogs.list()`
70
+ - `posts.list()`, `posts.get()`, and `posts.create()`
71
+ - `posts.update()` and `posts.archive()`
72
+ - `posts.revisions()` and `posts.restore()`
73
+ - `media.list()`, `media.get()`, and `media.startUpload()`
74
+ - `media.completeUpload()` and `media.delete()`
75
+
76
+ Management keys are publication-scoped. Post, revision, and media reads require `content:read`. Create, update, archive, revision restore, and media mutations require `content:write`. A restore saves the current post as a new revision before replacing it. Media deletion fails while a current post references the asset. Never expose a management key or signed upload reservation in untrusted code.
77
+
78
+ Start a managed upload with the publication UUID, filename, MIME type, and exact byte count. Send the bytes to the returned signed URL using its method and headers, then call `media.completeUpload()` with the reserved asset ID. Completion returns the sanitized variants and CDN URL.
79
+
80
+ The optional `blog` value on `posts.list()` is a safety assertion. It must match the API key's publication slug or UUID; it cannot select another publication.
81
+
42
82
  For Effect applications, `createEffectClient` exposes the same operations as
43
83
  typed Effects. `createClient` is the Promise-compatible facade for existing
44
- callers; both are generated from the shared Effect HttpApi contract.
84
+ callers; both share the Effect HttpApi contract; portable import uses its own streaming adapter and shared report/error schemas.
85
+
86
+ Non-success responses reject. Public-client errors include the HTTP status; management errors retain the typed Effect HTTP API failure when using `createEffectClient`.
87
+
88
+ ## Portable import
89
+
90
+ ```ts
91
+ import { openPortableImportFile } from "@prosewire/sdk/import-node";
92
+
93
+ const preview = await management.imports.run({
94
+ ...openPortableImportFile("export.json", "media.zip"),
95
+ mode: "preview",
96
+ });
97
+ ```
98
+
99
+ The main entrypoint accepts a `ReadableStream<Uint8Array>` and `application/json` or `application/zip`; the Node-only helper streams local export artifacts. Explicit `mode: "apply"` creates content, with posts defaulting to drafts unless `preserveStates: true` is supplied. Failures retain `status`, `item`, and `reason` in `ProsewireImportError`. See [portable import](https://prosewire.com/docs/operate/portable-import/) for supported formats, limits, and exact-artifact retry policy.
@@ -0,0 +1,6 @@
1
+ import { r as PortableImportInput } from "./index-BFz3f-oC.mjs";
2
+ //#region src/import-node.d.ts
3
+ /** Stream the existing two export artifacts into one envelope, without unpacking them. */
4
+ export declare function openPortableImportFile(file: string, mediaFile?: string): Pick<PortableImportInput, "body" | "contentType">;
5
+ //#endregion
6
+ //# sourceMappingURL=import-node.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-node.d.mts","names":[],"sources":["../src/import-node.ts"],"mappings":";;;wBAMgB,uBACd,cACA,qBACC,KAAK"}
@@ -0,0 +1,49 @@
1
+ import { createReadStream } from "node:fs";
2
+ import { Readable } from "node:stream";
3
+ import { Zip, ZipPassThrough } from "fflate";
4
+ //#region src/import-node.ts
5
+ /** Stream the existing two export artifacts into one envelope, without unpacking them. */
6
+ function openPortableImportFile(file, mediaFile) {
7
+ if (!mediaFile) return {
8
+ body: Readable.toWeb(createReadStream(file)),
9
+ contentType: file.toLowerCase().endsWith(".zip") ? "application/zip" : "application/json"
10
+ };
11
+ const mediaSource = mediaFile;
12
+ async function* chunks() {
13
+ const output = [];
14
+ let failure;
15
+ const archive = new Zip((error, chunk) => {
16
+ if (error) failure = error;
17
+ else output.push(chunk);
18
+ });
19
+ const drain = function* () {
20
+ if (failure) throw failure;
21
+ yield* output.splice(0);
22
+ };
23
+ try {
24
+ for (const [name, source] of [["export.json", file], ["media.zip", mediaSource]]) {
25
+ const entry = new ZipPassThrough(name);
26
+ archive.add(entry);
27
+ yield* drain();
28
+ for await (const chunk of createReadStream(source)) {
29
+ entry.push(chunk, false);
30
+ yield* drain();
31
+ }
32
+ entry.push(/* @__PURE__ */ new Uint8Array(), true);
33
+ yield* drain();
34
+ }
35
+ archive.end();
36
+ yield* drain();
37
+ } finally {
38
+ archive.terminate();
39
+ }
40
+ }
41
+ return {
42
+ contentType: "application/zip",
43
+ body: Readable.toWeb(Readable.from(chunks()))
44
+ };
45
+ }
46
+ //#endregion
47
+ export { openPortableImportFile };
48
+
49
+ //# sourceMappingURL=import-node.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-node.mjs","names":[],"sources":["../src/import-node.ts"],"sourcesContent":["import { createReadStream } from \"node:fs\";\nimport { Readable } from \"node:stream\";\nimport { Zip, ZipPassThrough } from \"fflate\";\nimport type { PortableImportInput } from \"./index.ts\";\n\n/** Stream the existing two export artifacts into one envelope, without unpacking them. */\nexport function openPortableImportFile(\n file: string,\n mediaFile?: string,\n): Pick<PortableImportInput, \"body\" | \"contentType\"> {\n if (!mediaFile)\n return {\n body: Readable.toWeb(\n createReadStream(file),\n ) as ReadableStream<Uint8Array>,\n contentType: file.toLowerCase().endsWith(\".zip\")\n ? \"application/zip\"\n : \"application/json\",\n };\n const mediaSource = mediaFile;\n async function* chunks() {\n const output: Uint8Array[] = [];\n let failure: Error | undefined;\n const archive = new Zip((error, chunk) => {\n if (error) failure = error;\n else output.push(chunk);\n });\n const drain = function* () {\n if (failure) throw failure;\n yield* output.splice(0);\n };\n try {\n for (const [name, source] of [\n [\"export.json\", file],\n [\"media.zip\", mediaSource],\n ] as const) {\n const entry = new ZipPassThrough(name);\n archive.add(entry);\n yield* drain();\n for await (const chunk of createReadStream(source)) {\n entry.push(chunk, false);\n yield* drain();\n }\n entry.push(new Uint8Array(), true);\n yield* drain();\n }\n archive.end();\n yield* drain();\n } finally {\n archive.terminate();\n }\n }\n return {\n contentType: \"application/zip\",\n body: Readable.toWeb(Readable.from(chunks())) as ReadableStream<Uint8Array>,\n };\n}\n"],"mappings":";;;;;AAMA,SAAgB,uBACd,MACA,WACmD;CACnD,IAAI,CAAC,WACH,OAAO;EACL,MAAM,SAAS,MACb,iBAAiB,IAAI,CACvB;EACA,aAAa,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,IAC3C,oBACA;CACN;CACF,MAAM,cAAc;CACpB,gBAAgB,SAAS;EACvB,MAAM,SAAuB,CAAC;EAC9B,IAAI;EACJ,MAAM,UAAU,IAAI,KAAK,OAAO,UAAU;GACxC,IAAI,OAAO,UAAU;QAChB,OAAO,KAAK,KAAK;EACxB,CAAC;EACD,MAAM,QAAQ,aAAa;GACzB,IAAI,SAAS,MAAM;GACnB,OAAO,OAAO,OAAO,CAAC;EACxB;EACA,IAAI;GACF,KAAK,MAAM,CAAC,MAAM,WAAW,CAC3B,CAAC,eAAe,IAAI,GACpB,CAAC,aAAa,WAAW,CAC3B,GAAY;IACV,MAAM,QAAQ,IAAI,eAAe,IAAI;IACrC,QAAQ,IAAI,KAAK;IACjB,OAAO,MAAM;IACb,WAAW,MAAM,SAAS,iBAAiB,MAAM,GAAG;KAClD,MAAM,KAAK,OAAO,KAAK;KACvB,OAAO,MAAM;IACf;IACA,MAAM,qBAAK,IAAI,WAAW,GAAG,IAAI;IACjC,OAAO,MAAM;GACf;GACA,QAAQ,IAAI;GACZ,OAAO,MAAM;EACf,UAAU;GACR,QAAQ,UAAU;EACpB;CACF;CACA,OAAO;EACL,aAAa;EACb,MAAM,SAAS,MAAM,SAAS,KAAK,OAAO,CAAC,CAAC;CAC9C;AACF"}