@backmatter/core 0.0.6 → 0.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backmatter/core",
3
- "version": "0.0.6",
3
+ "version": "0.0.9",
4
4
  "description": "Experimental Backmatter SDK. APIs will break. Not ready for production.",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/src/config.ts CHANGED
@@ -15,9 +15,9 @@ export type BackmatterDirs = {
15
15
  };
16
16
 
17
17
  export type BackmatterConfig = {
18
- /** Required only when talking to the BACKMATTER API. Env: BACKMATTER_WORKSPACE_ID. */
18
+ /** Workspace id (`ws_…`), not the dashboard slug. Config first; env BACKMATTER_WORKSPACE_ID overrides gap. */
19
19
  workspaceId?: string;
20
- /** Env: BACKMATTER_API_URL. When set with workspaceId, published docs load from the API. */
20
+ /** CMS API origin. Config / BACKMATTER_API_URL, else cloud API on a deployed host when workspaceId is set. */
21
21
  apiUrl?: string;
22
22
  /** Env: BACKMATTER_CONTENT_TOKEN. Required only to read non-default branches. */
23
23
  contentToken?: string;
@@ -53,6 +53,7 @@ export const DEFAULT_DIRS: Required<BackmatterDirs> = {
53
53
  layouts: "layouts",
54
54
  };
55
55
  export const DEFAULT_LOCALE = "en";
56
+ export const CLOUD_API_URL = "https://api.backmatter.app";
56
57
 
57
58
  function withHttps(url: string): string {
58
59
  if (url.startsWith("http://") || url.startsWith("https://")) return url;
@@ -63,6 +64,46 @@ function stripTrailingSlash(url: string): string {
63
64
  return url.endsWith("/") ? url.slice(0, -1) : url;
64
65
  }
65
66
 
67
+ function envOf(): Record<string, string | undefined> {
68
+ return typeof process === "undefined" ? {} : process.env;
69
+ }
70
+
71
+ /** Vercel, Netlify, Cloudflare Pages, Railway, or NODE_ENV=production. */
72
+ export function isDeployedHost(env: Record<string, string | undefined>): boolean {
73
+ return Boolean(
74
+ env.VERCEL ||
75
+ env.NETLIFY ||
76
+ env.CF_PAGES ||
77
+ env.RAILWAY_ENVIRONMENT ||
78
+ env.NODE_ENV === "production",
79
+ );
80
+ }
81
+
82
+ /** DevBar /me origin: explicit, else env, else cloud API on a deployed host. */
83
+ export function resolveDevBarApiUrl(
84
+ explicit: string | undefined,
85
+ env: Record<string, string | undefined> = envOf(),
86
+ ): string {
87
+ const fromProp = explicit?.trim();
88
+ if (fromProp) return stripTrailingSlash(fromProp);
89
+ const fromEnv = env.BACKMATTER_API_URL?.trim();
90
+ if (fromEnv) return stripTrailingSlash(fromEnv);
91
+ if (isDeployedHost(env)) return CLOUD_API_URL;
92
+ return "";
93
+ }
94
+
95
+ function detectApiUrl(
96
+ config: BackmatterConfig,
97
+ env: Record<string, string | undefined>,
98
+ workspaceId: string | undefined,
99
+ ): string | undefined {
100
+ const explicit = config.apiUrl !== undefined ? config.apiUrl : env.BACKMATTER_API_URL;
101
+ const trimmed = explicit?.trim();
102
+ if (trimmed) return stripTrailingSlash(trimmed);
103
+ if (workspaceId && isDeployedHost(env)) return CLOUD_API_URL;
104
+ return undefined;
105
+ }
106
+
66
107
  /** Public origin of the host that should receive `content.committed`. */
67
108
  export function detectSiteUrl(
68
109
  config: { siteUrl?: string } = {},
@@ -90,10 +131,6 @@ export function defineConfig(config: BackmatterConfig): BackmatterConfig {
90
131
  return config;
91
132
  }
92
133
 
93
- function envOf(): Record<string, string | undefined> {
94
- return typeof process === "undefined" ? {} : process.env;
95
- }
96
-
97
134
  /** Explicit config wins; env vars fill the gaps; folder defaults last. */
98
135
  export function resolveConfig(
99
136
  config: BackmatterConfig = {},
@@ -104,9 +141,10 @@ export function resolveConfig(
104
141
  const fromEnv = env[key]?.trim();
105
142
  return fromEnv ? fromEnv : undefined;
106
143
  };
144
+ const workspaceId = pick(config.workspaceId, "BACKMATTER_WORKSPACE_ID");
107
145
  return {
108
- workspaceId: pick(config.workspaceId, "BACKMATTER_WORKSPACE_ID"),
109
- apiUrl: pick(config.apiUrl, "BACKMATTER_API_URL"),
146
+ workspaceId,
147
+ apiUrl: detectApiUrl(config, env, workspaceId),
110
148
  contentToken: pick(config.contentToken, "BACKMATTER_CONTENT_TOKEN"),
111
149
  previewToken: pick(config.previewToken, "BACKMATTER_PREVIEW_TOKEN"),
112
150
  hostToken: pick(config.hostToken, "BACKMATTER_HOST_TOKEN"),
package/src/index.ts CHANGED
@@ -42,7 +42,16 @@ export type { DocFormat } from "./doc-format";
42
42
  export { modelFormat, parseDoc, serializeDoc } from "./doc-format";
43
43
 
44
44
  export type { BackmatterConfig, BackmatterDirs, ResolvedConfig } from "./config";
45
- export { defineConfig, resolveConfig, detectSiteUrl, DEFAULT_DIRS, DEFAULT_LOCALE } from "./config";
45
+ export {
46
+ defineConfig,
47
+ resolveConfig,
48
+ detectSiteUrl,
49
+ isDeployedHost,
50
+ resolveDevBarApiUrl,
51
+ DEFAULT_DIRS,
52
+ DEFAULT_LOCALE,
53
+ CLOUD_API_URL,
54
+ } from "./config";
46
55
 
47
56
  export type { RouteMatch, RouteModel, MatchRouteOptions } from "./routes";
48
57
  export {
@@ -106,6 +115,8 @@ export {
106
115
  PREVIEW_COOKIE,
107
116
  PREVIEW_ENABLE_PATH,
108
117
  PREVIEW_QUERY,
118
+ PREVIEW_CHECKOUT_QUERY,
119
+ previewLocationWithCheckout,
109
120
  isSafeRedirectPath,
110
121
  mintPreviewTicket,
111
122
  previewMiddlewareDecision,
package/src/node/cli.ts CHANGED
@@ -249,8 +249,10 @@ import { defineConfig } from "${from}";
249
249
  // General settings only. Models live in models/*.ts, composer blocks in
250
250
  // blocks/*.tsx, MDX tags in mdx/*.tsx, and content in
251
251
  // content/{locale}/{type}/{slug}.{mdx,json} — all discovered automatically.
252
- // workspaceId / apiUrl / tokens fall back to BACKMATTER_* env vars when omitted.
252
+ // Set workspaceId from General settings (not an env var).
253
+ // API URL defaults to https://api.backmatter.app in production; tokens stay env.
253
254
  export default defineConfig({
255
+ // workspaceId: "ws_…",
254
256
  defaultLocale: "en",
255
257
  });
256
258
  `;
@@ -3,6 +3,7 @@ import { signPayload, verifyPayload } from "./signed-token";
3
3
 
4
4
  export const PREVIEW_COOKIE = "backmatter_preview";
5
5
  export const PREVIEW_QUERY = "backmatter_preview";
6
+ export const PREVIEW_CHECKOUT_QUERY = "backmatter_checkout";
6
7
  export const PREVIEW_ENABLE_PATH = "/api/backmatter/preview";
7
8
  export const PREVIEW_TICKET_TTL_MS = 10 * 60 * 1000;
8
9
  export const PREVIEW_COOKIE_TTL_MS = 12 * 60 * 60 * 1000;
@@ -80,6 +81,16 @@ export async function readPreviewTicket(secret: string, token: string): Promise<
80
81
  return asTicket(await verifyPayload(secret, token));
81
82
  }
82
83
 
84
+ export function previewLocationWithCheckout(
85
+ origin: string,
86
+ redirect: string,
87
+ checkout: string,
88
+ ): string {
89
+ const location = new URL(redirect, origin);
90
+ location.searchParams.set(PREVIEW_CHECKOUT_QUERY, checkout);
91
+ return location.toString();
92
+ }
93
+
83
94
  export async function writePreviewCookie(
84
95
  secret: string,
85
96
  input: PreviewCheckout & { exp: number },
@@ -124,7 +135,7 @@ export function previewMiddlewareDecision(url: URL, draftEnabled: boolean): Prev
124
135
  if (ticket) {
125
136
  return { type: "redirect", location: `${PREVIEW_ENABLE_PATH}?ticket=${encodeURIComponent(ticket)}` };
126
137
  }
127
- if (draftEnabled) {
138
+ if (draftEnabled || url.searchParams.get(PREVIEW_CHECKOUT_QUERY)?.trim()) {
128
139
  return { type: "headers", headers: { ...PREVIEW_CACHE_HEADERS } };
129
140
  }
130
141
  return { type: "next" };