unfazed 0.8.1 → 0.9.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 +44 -0
- package/README.md +90 -2
- package/REFERENCE.md +47 -2
- package/dist/cli/login.d.ts +1 -1
- package/dist/client.d.ts +21 -1
- package/dist/client.js +12 -4
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +49 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/oidc.d.ts +161 -0
- package/dist/oidc.js +391 -0
- package/dist/oidc.js.map +1 -0
- package/dist/reference.generated.js +1 -1
- package/dist/reference.generated.js.map +1 -1
- package/dist/reference.js +3 -3
- package/dist/reference.js.map +1 -1
- package/dist/unfazed.d.ts +13 -2
- package/dist/unfazed.js +45 -4
- package/dist/unfazed.js.map +1 -1
- package/package.json +1 -1
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Error taxonomy.\n *\n * Everything the SDK throws is an {@link UnfazedError}, so a code-mode script\n * can `catch (e) { if (e instanceof UnfazedError) ... }` and still\n * discriminate on the subclass to decide whether to retry.\n */\n\n/** A single entry from a GraphQL response's `errors` array. */\nexport interface GraphQLResponseError {\n message: string;\n path?: (string | number)[];\n extensions?: Record<string, unknown>;\n}\n\n/** Base class for everything this SDK throws. */\nexport class UnfazedError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/** No token was passed and no token environment variable was set. */\nexport class UnfazedAuthError extends UnfazedError {}\n\n/** The runtime has no global `fetch` and none was injected. */\nexport class UnfazedConfigError extends UnfazedError {}\n\n/**\n * The endpoint answered with a non-2xx status. `status` is the HTTP status and\n * `body` the response text, which is where the platform reports non-GraphQL\n * failures such as an expired or wrong-scope bearer token.\n */\nexport class UnfazedHTTPError extends UnfazedError {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string, url: string) {\n super(`unfazed: HTTP ${status} from ${url}${body ? `: ${truncate(body)}` : \"\"}`);\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * The endpoint answered 200 with a GraphQL `errors` array.\n *\n * Partial `data` is preserved deliberately: the batched writeFiles document\n * resolves each aliased field independently, so one path can fail while the\n * others commit, and the caller needs to see which landed.\n */\nexport class UnfazedGraphQLError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n readonly data: unknown;\n\n constructor(errors: GraphQLResponseError[], data: unknown) {\n const summary = errors.map((e) => e.message).join(\"; \") || \"unknown GraphQL error\";\n super(`unfazed: ${summary}`);\n this.errors = errors;\n this.data = data;\n }\n}\n\n/**\n * The server refused a delete that would take apps offline without the call\n * naming exactly those apps, and deleted nothing.\n *\n * `deleteWorkspace` with `withDeployments` takes down every app the workspace\n * serves, so its `confirmAliases` must equal the workspace's live apps — no\n * more, no fewer. A missing or extra alias, or an app that went live after the\n * check, is refused. The message is the server's, verbatim: it lists each live\n * app with its custom domains and says how to name them. Look the apps up\n * deliberately with `unfazed.liveApps(workspaceId)`, decide, and delete again,\n * or delete one app with `unfazed.deleteApp`.\n *\n * Still an {@link UnfazedGraphQLError}, so existing `catch` sites keep matching.\n */\nexport class UnfazedUnconfirmedAppsError extends UnfazedGraphQLError {\n /** The workspace whose delete was refused. */\n readonly workspaceId: string;\n /** The aliases the refused call named. */\n readonly confirmAliases: readonly string[];\n\n constructor(\n workspaceId: string,\n confirmAliases: readonly string[],\n errors: GraphQLResponseError[],\n data: unknown,\n ) {\n super(errors, data);\n this.workspaceId = workspaceId;\n this.confirmAliases = confirmAliases;\n }\n}\n\n/**\n * How the server opens every refusal of a delete whose named apps do not match\n * the live ones (usecases.UnconfirmedAppsError, and the race where an app went\n * live after the check). Anchored at the start, so a message that merely\n * mentions the phrase is not mistaken for one.\n */\nconst NOTHING_WAS_DELETED = /^nothing was deleted:/i;\n\n/** Did the server refuse a delete because the named apps are not exactly the live ones? */\nexport function isUnconfirmedAppsRefusal(error: unknown): error is UnfazedGraphQLError {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => NOTHING_WAS_DELETED.test(e.message.trim()))\n );\n}\n\n/** A request exceeded `timeoutMs`, or the caller's AbortSignal fired. */\nexport class UnfazedTimeoutError extends UnfazedError {}\n\n/**\n * `writeFiles` wrote some files and not others.\n *\n * Writes are not transactional, exactly as MCP `write_workspace_files`: the\n * files listed in `result.files` with `ok: true` ARE written (and, on an app\n * with platform git, committed), the others are not. `result` is the whole\n * answer, `failed` names the paths that did not land and why. Retry only\n * those paths. Pass `allowPartial: true` to get the result instead of this\n * error.\n */\nexport class UnfazedWriteError extends UnfazedError {\n /** What the call wrote: every file's outcome and the commit. */\n readonly result: unknown;\n /** The paths that were not written, with the server's reason. */\n readonly failed: { path: string; error: string }[];\n\n constructor(result: unknown, failed: { path: string; error: string }[]) {\n super(\n `unfazed: ${failed.length} file(s) were not written (the others were): ` +\n failed.map((f) => `${f.path}: ${f.error}`).join(\"; \"),\n );\n this.result = result;\n this.failed = failed;\n }\n}\n\n/**\n * Did the platform refuse a long wait because the caller (or the server) has\n * too many in progress? Nothing was waited on; polling is the way on.\n */\nexport function isTooManyWaits(error: unknown): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => /waits in progress|long wait per operation/i.test(e.message))\n );\n}\n\n/**\n * Did the server refuse the document because it does not know `field` (a\n * platform older than this SDK)? The SDK uses it to fall back to the older\n * way of doing the same thing, never to hide a real error.\n */\nexport function isUnsupportedField(error: unknown, field: string): boolean {\n if (!(error instanceof UnfazedGraphQLError)) return false;\n const quoted = `\"${field}\"`;\n return error.errors.some(\n (e) =>\n e.message.includes(quoted) &&\n /Cannot query field|is not defined by type|Unknown argument|Unknown type|not defined on/i.test(e.message),\n );\n}\n\n/**\n * Waiting on a deploy ended badly.\n *\n * `reason` separates the two outcomes because they need different handling:\n * FAILED means the build finished and is broken (read `deployment.error` and\n * fix the source — retrying will not help), TIMEOUT means it is still BUILDING\n * and polling longer may still succeed.\n */\nexport class UnfazedDeployError extends UnfazedError {\n readonly reason: \"FAILED\" | \"TIMEOUT\";\n readonly deploymentId: string;\n /** Last deployment snapshot observed before giving up. */\n readonly deployment: unknown;\n\n constructor(\n reason: \"FAILED\" | \"TIMEOUT\",\n deploymentId: string,\n deployment: unknown,\n message: string,\n ) {\n super(message);\n this.reason = reason;\n this.deploymentId = deploymentId;\n this.deployment = deployment;\n }\n}\n\n/**\n * Waiting on a run timed out.\n *\n * Unlike {@link UnfazedDeployError}, a run's FAILED status is never thrown\n * here: {@link import(\"./unfazed.js\").Unfazed.waitForRun} resolves on\n * SUCCEEDED, FAILED and CANCELLED alike, because a run that executed and\n * exited non-zero is a completed answer — read `run.error`/`run.exitCode` —\n * not a broken build the way a FAILED deployment is. TIMEOUT is the only\n * outcome still worth an exception: the run is, as far as the last poll\n * showed, still QUEUED or RUNNING, and waiting longer may yet succeed.\n */\nexport class UnfazedRunError extends UnfazedError {\n readonly reason: \"TIMEOUT\";\n readonly runId: string;\n /** Last run snapshot observed before giving up. */\n readonly run: unknown;\n\n constructor(reason: \"TIMEOUT\", runId: string, run: unknown, message: string) {\n super(message);\n this.reason = reason;\n this.runId = runId;\n this.run = run;\n }\n}\n\n/**\n * A rollback's compare-and-swap guard was rejected: the alias moved between\n * reading the current release and swapping it.\n *\n * Recovery is always \"re-list releases and rebuild the guard\" — never \"reuse\n * the old tuple\", because the server checks the holder id, epoch AND opaque\n * revision to close same-id ABA. `Unfazed.rollback` does that re-list for you\n * once by default.\n */\nexport class UnfazedConflictError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n\n constructor(message: string, errors: GraphQLResponseError[]) {\n super(message);\n this.errors = errors;\n }\n}\n\n/** The requested alias, deployment or release does not exist (or was pruned). */\nexport class UnfazedNotFoundError extends UnfazedError {}\n\n/**\n * What the `unfazed` CLI backend's World (`createWorld()`) throws.\n *\n * Its `name` is the Workflow SDK error a World would throw in its place\n * (`WorkflowWorldError`, `WorkflowRunNotFoundError`, `HookNotFoundError`),\n * because Workflow SDK's CLI and web UI pick their message by `name`,\n * `status` and `code`: a missing run reads as a 404 there, an argument the\n * backend cannot serve (`code: \"INVALID_ARGUMENT\"`) prints as given.\n * `instanceof UnfazedError` still holds.\n */\nexport class UnfazedInspectError extends UnfazedError {\n /** HTTP-like status: 404 for a missing run, step, event, hook or app; 401/403 for a refused token. */\n readonly status: number | undefined;\n /** `INVALID_ARGUMENT`, `READ_ONLY`, or undefined. */\n readonly code: string | undefined;\n /** The run, for `WorkflowRunNotFoundError`. */\n readonly runId: string | undefined;\n\n constructor(\n message: string,\n options: {\n name?: \"WorkflowWorldError\" | \"WorkflowRunNotFoundError\" | \"HookNotFoundError\";\n status?: number;\n code?: string;\n runId?: string;\n cause?: unknown;\n } = {},\n ) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = options.name ?? \"WorkflowWorldError\";\n this.status = options.status;\n this.code = options.code;\n this.runId = options.runId;\n }\n}\n\n/**\n * The server's exact \"no such workspace\" sentinel.\n *\n * A missing workspace and a workspace belonging to another tenant return the\n * SAME message on purpose, so the API cannot be used to probe for someone\n * else's ids. Treat it as \"not yours to see\", not strictly as \"absent\".\n */\nexport const WORKSPACE_NOT_FOUND = \"workspace was not found\";\n\n/** The server's exact \"no such deployment\" sentinel. See {@link WORKSPACE_NOT_FOUND}. */\nexport const DEPLOYMENT_NOT_FOUND = \"deployment was not found\";\n\n/**\n * Does this error carry EXACTLY the given not-found sentinel?\n *\n * Exact equality, deliberately. The tempting version — \"does any message\n * contain 'not found'\" — also swallows `release not found in history`,\n * `integration not found` and every future message that happens to use the\n * phrase, turning real failures into a silent `false` from a delete. These two\n * strings are stable authz sentinels with test coverage on the server side;\n * anything else is a genuine error and must keep throwing.\n */\nexport function isNotFoundSentinel(error: unknown, sentinel: string): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => e.message.trim() === sentinel)\n );\n}\n\n/**\n * The server's \"no such app here\" refusal of `deleteApp`:\n * `app \"<alias>\" was not found in workspace \"<workspace name>\"`. Anchored at\n * both ends, like the exact sentinels above.\n */\nconst APP_NOT_FOUND = /^app \"(.+)\" was not found in workspace \".*\"$/;\n\n/**\n * Does this error say that exactly `alias` is not an app of the workspace?\n *\n * The workspace name in the message varies, so this matches the shape, then\n * the alias itself: a refusal about a different alias still throws.\n */\nexport function isAppNotFound(error: unknown, alias: string): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => APP_NOT_FOUND.exec(e.message.trim())?.[1] === alias.trim())\n );\n}\n\n/**\n * Message shape of the server's per-owner duplicate workspace name error:\n * `a workspace named \"x\" already exists; reuse it (id <uuid>) or pick a\n * different name`.\n *\n * Used only to turn a lost `ensureWorkspace` race into a re-read. Requiring\n * both \"workspace\" and \"already exists\" in one clause keeps it off unrelated\n * conflicts (a duplicate ALIAS, say), and a false negative is safe: the\n * caller just gets the server's error verbatim.\n */\nconst DUPLICATE_WORKSPACE_NAME = /\\bworkspace\\b[^.;]{0,80}\\balready exists\\b/i;\n\n/** Did `createWorkspace` fail because the name is already taken by this owner? */\nexport function isDuplicateWorkspaceName(error: unknown): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => DUPLICATE_WORKSPACE_NAME.test(e.message))\n );\n}\n\nfunction truncate(s: string, max = 500): string {\n return s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n\n/**\n * Message shapes that mean \"the alias moved under you\".\n *\n * The first two are the platform's ACTUAL strings. A rejected rollback guard\n * returns `store.ErrAliasMoved` verbatim — every guard point in the rollback\n * and adopt paths returns that one sentinel unwrapped — and the staged-service\n * rollback wraps the same guard with its own prefix. The rest are defensive:\n * the guard ARGUMENTS named back at you, or the alias-revision concept in any\n * spelling, or `alias` sitting in the same clause as a conflict verb, so a\n * rewording on the server side does not silently disable the retry.\n *\n * Deliberately NOT matched: a bare \"conflict\", \"changed\" or \"stale\" anywhere in\n * any message. Those fire on errors that have nothing to do with the alias\n * compare-and-swap, and a false positive is not free — it spends a pointless\n * re-list AND rewraps an unrelated failure as {@link UnfazedConflictError},\n * whose message would then blame concurrent deploys for, say, a permission\n * problem.\n */\nconst CAS_PATTERNS: RegExp[] = [\n /alias was repointed by another change/i,\n /became stale before alias change/i,\n /expected[\\s_-]*current/i,\n /alias[\\s_-]*revision/i,\n /\\balias\\b[^.;]{0,80}\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b/i,\n /\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b[^.;]{0,80}\\balias\\b/i,\n];\n\n/**\n * Heuristic: does this GraphQL error set look like a rejected compare-and-swap?\n *\n * The server carries no machine-readable code in `extensions` — its error\n * presenter returns gqlgen's default untouched — so this has to match message\n * text. It is used ONLY to decide whether to re-list and retry once: a false\n * negative costs a surfaced error the caller can retry by hand, a false\n * positive costs an extra read and a misleading error class. Neither can\n * promote the wrong release, because the retry rebuilds the guard from a fresh\n * read and the server re-validates it.\n */\nexport function looksLikeCASConflict(errors: GraphQLResponseError[]): boolean {\n return errors.some((e) => CAS_PATTERNS.some((pattern) => pattern.test(e.message)));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Error taxonomy.\n *\n * Everything the SDK throws is an {@link UnfazedError}, so a code-mode script\n * can `catch (e) { if (e instanceof UnfazedError) ... }` and still\n * discriminate on the subclass to decide whether to retry.\n */\n\n/** A single entry from a GraphQL response's `errors` array. */\nexport interface GraphQLResponseError {\n message: string;\n path?: (string | number)[];\n extensions?: Record<string, unknown>;\n}\n\n/** Base class for everything this SDK throws. */\nexport class UnfazedError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/** No token was passed and no token environment variable was set. */\nexport class UnfazedAuthError extends UnfazedError {}\n\n/** The runtime has no global `fetch` and none was injected. */\nexport class UnfazedConfigError extends UnfazedError {}\n\n/**\n * The endpoint answered with a non-2xx status. `status` is the HTTP status and\n * `body` the response text, which is where the platform reports non-GraphQL\n * failures such as an expired or wrong-scope bearer token.\n */\nexport class UnfazedHTTPError extends UnfazedError {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string, url: string) {\n super(`unfazed: HTTP ${status} from ${url}${body ? `: ${truncate(body)}` : \"\"}`);\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * The endpoint answered 200 with a GraphQL `errors` array.\n *\n * Partial `data` is preserved deliberately: the batched writeFiles document\n * resolves each aliased field independently, so one path can fail while the\n * others commit, and the caller needs to see which landed.\n */\nexport class UnfazedGraphQLError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n readonly data: unknown;\n\n constructor(errors: GraphQLResponseError[], data: unknown) {\n const summary = errors.map((e) => e.message).join(\"; \") || \"unknown GraphQL error\";\n super(`unfazed: ${summary}`);\n this.errors = errors;\n this.data = data;\n }\n}\n\n/**\n * The server refused a delete that would take apps offline without the call\n * naming exactly those apps, and deleted nothing.\n *\n * `deleteWorkspace` with `withDeployments` takes down every app the workspace\n * serves, so its `confirmAliases` must equal the workspace's live apps — no\n * more, no fewer. A missing or extra alias, or an app that went live after the\n * check, is refused. The message is the server's, verbatim: it lists each live\n * app with its custom domains and says how to name them. Look the apps up\n * deliberately with `unfazed.liveApps(workspaceId)`, decide, and delete again,\n * or delete one app with `unfazed.deleteApp`.\n *\n * Still an {@link UnfazedGraphQLError}, so existing `catch` sites keep matching.\n */\nexport class UnfazedUnconfirmedAppsError extends UnfazedGraphQLError {\n /** The workspace whose delete was refused. */\n readonly workspaceId: string;\n /** The aliases the refused call named. */\n readonly confirmAliases: readonly string[];\n\n constructor(\n workspaceId: string,\n confirmAliases: readonly string[],\n errors: GraphQLResponseError[],\n data: unknown,\n ) {\n super(errors, data);\n this.workspaceId = workspaceId;\n this.confirmAliases = confirmAliases;\n }\n}\n\n/**\n * How the server opens every refusal of a delete whose named apps do not match\n * the live ones (usecases.UnconfirmedAppsError, and the race where an app went\n * live after the check). Anchored at the start, so a message that merely\n * mentions the phrase is not mistaken for one.\n */\nconst NOTHING_WAS_DELETED = /^nothing was deleted:/i;\n\n/** Did the server refuse a delete because the named apps are not exactly the live ones? */\nexport function isUnconfirmedAppsRefusal(error: unknown): error is UnfazedGraphQLError {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => NOTHING_WAS_DELETED.test(e.message.trim()))\n );\n}\n\n/** A request exceeded `timeoutMs`, or the caller's AbortSignal fired. */\nexport class UnfazedTimeoutError extends UnfazedError {}\n\n/**\n * `writeFiles` wrote some files and not others.\n *\n * Writes are not transactional, exactly as MCP `write_workspace_files`: the\n * files listed in `result.files` with `ok: true` ARE written (and, on an app\n * with platform git, committed), the others are not. `result` is the whole\n * answer, `failed` names the paths that did not land and why. Retry only\n * those paths. Pass `allowPartial: true` to get the result instead of this\n * error.\n */\nexport class UnfazedWriteError extends UnfazedError {\n /** What the call wrote: every file's outcome and the commit. */\n readonly result: unknown;\n /** The paths that were not written, with the server's reason. */\n readonly failed: { path: string; error: string }[];\n\n constructor(result: unknown, failed: { path: string; error: string }[]) {\n super(\n `unfazed: ${failed.length} file(s) were not written (the others were): ` +\n failed.map((f) => `${f.path}: ${f.error}`).join(\"; \"),\n );\n this.result = result;\n this.failed = failed;\n }\n}\n\n/**\n * Did the platform refuse a long wait because the caller (or the server) has\n * too many in progress? Nothing was waited on; polling is the way on.\n */\nexport function isTooManyWaits(error: unknown): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => /waits in progress|long wait per operation/i.test(e.message))\n );\n}\n\n/**\n * Did the server refuse the document because it does not know `field` (a\n * platform older than this SDK)? The SDK uses it to fall back to the older\n * way of doing the same thing, never to hide a real error.\n */\nexport function isUnsupportedField(error: unknown, field: string): boolean {\n if (!(error instanceof UnfazedGraphQLError)) return false;\n const quoted = `\"${field}\"`;\n return error.errors.some(\n (e) =>\n e.message.includes(quoted) &&\n /Cannot query field|is not defined by type|Unknown argument|Unknown type|not defined on/i.test(e.message),\n );\n}\n\n/**\n * Waiting on a deploy ended badly.\n *\n * `reason` separates the two outcomes because they need different handling:\n * FAILED means the build finished and is broken (read `deployment.error` and\n * fix the source — retrying will not help), TIMEOUT means it is still BUILDING\n * and polling longer may still succeed.\n */\nexport class UnfazedDeployError extends UnfazedError {\n readonly reason: \"FAILED\" | \"TIMEOUT\";\n readonly deploymentId: string;\n /** Last deployment snapshot observed before giving up. */\n readonly deployment: unknown;\n\n constructor(\n reason: \"FAILED\" | \"TIMEOUT\",\n deploymentId: string,\n deployment: unknown,\n message: string,\n ) {\n super(message);\n this.reason = reason;\n this.deploymentId = deploymentId;\n this.deployment = deployment;\n }\n}\n\n/**\n * Waiting on a run timed out.\n *\n * Unlike {@link UnfazedDeployError}, a run's FAILED status is never thrown\n * here: {@link import(\"./unfazed.js\").Unfazed.waitForRun} resolves on\n * SUCCEEDED, FAILED and CANCELLED alike, because a run that executed and\n * exited non-zero is a completed answer — read `run.error`/`run.exitCode` —\n * not a broken build the way a FAILED deployment is. TIMEOUT is the only\n * outcome still worth an exception: the run is, as far as the last poll\n * showed, still QUEUED or RUNNING, and waiting longer may yet succeed.\n */\nexport class UnfazedRunError extends UnfazedError {\n readonly reason: \"TIMEOUT\";\n readonly runId: string;\n /** Last run snapshot observed before giving up. */\n readonly run: unknown;\n\n constructor(reason: \"TIMEOUT\", runId: string, run: unknown, message: string) {\n super(message);\n this.reason = reason;\n this.runId = runId;\n this.run = run;\n }\n}\n\n/**\n * A rollback's compare-and-swap guard was rejected: the alias moved between\n * reading the current release and swapping it.\n *\n * Recovery is always \"re-list releases and rebuild the guard\" — never \"reuse\n * the old tuple\", because the server checks the holder id, epoch AND opaque\n * revision to close same-id ABA. `Unfazed.rollback` does that re-list for you\n * once by default.\n */\nexport class UnfazedConflictError extends UnfazedError {\n readonly errors: GraphQLResponseError[];\n\n constructor(message: string, errors: GraphQLResponseError[]) {\n super(message);\n this.errors = errors;\n }\n}\n\n/** The requested alias, deployment or release does not exist (or was pruned). */\nexport class UnfazedNotFoundError extends UnfazedError {}\n\n/**\n * Why `oidc.token()` gave no token. The first six are the platform's own\n * `error` codes (`POST /oidc/token`); the rest are the SDK's.\n */\nexport type OIDCErrorCode =\n /** 401: the platform refused the app's `UNFZ_STATE_TOKEN`. */\n | \"invalid_token\"\n /** 400, or refused locally: not 1–256 printable ASCII characters without whitespace. */\n | \"invalid_audience\"\n /** 400, or refused locally: `ttlSeconds` is not a whole number from 60 to 3600. */\n | \"invalid_request\"\n /** 429, after the retries (or with a `Retry-After` longer than the SDK waits). */\n | \"rate_limited\"\n /** 404: the platform issues no OIDC tokens to this app. */\n | \"oidc_disabled\"\n /** 503 (or a gateway's 502/504), after the retries. */\n | \"temporarily_unavailable\"\n /** `UNFZ_STATE_TOKEN` is not set: not running inside an unfazed app. */\n | \"not_in_app\"\n /** Network failures (or attempts past `timeoutMs`) outlasted the retries. */\n | \"unreachable\"\n /** A 200 without a usable `token` and `expires_at`. */\n | \"invalid_response\"\n /** Any other status, without a code. */\n | \"http_error\";\n\n/**\n * `oidc.token()` (or `tokenResponse`, or a `fetchWithToken` fetch) got no\n * token. `code` is the platform's `error` (`invalid_audience`, `rate_limited`,\n * …), passed through as sent, or one of the SDK's own ({@link OIDCErrorCode});\n * `description` is the platform's `error_description`.\n */\nexport class UnfazedOIDCError extends UnfazedError {\n readonly code: OIDCErrorCode | (string & {});\n /** HTTP status from the platform; undefined for a refusal made locally. */\n readonly status: number | undefined;\n /** The platform's `error_description`. */\n readonly description: string | undefined;\n /** Asking again later may succeed (`rate_limited`, `temporarily_unavailable`, `unreachable`). */\n readonly retryable: boolean;\n /** The platform's `Retry-After`, in ms, when it sent one. */\n readonly retryAfterMs: number | undefined;\n\n constructor(\n code: OIDCErrorCode | (string & {}),\n message: string,\n options: {\n status?: number;\n description?: string;\n retryable?: boolean;\n retryAfterMs?: number;\n cause?: unknown;\n } = {},\n ) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.code = code;\n this.status = options.status;\n this.description = options.description;\n this.retryable = options.retryable ?? false;\n this.retryAfterMs = options.retryAfterMs;\n }\n}\n\n/**\n * What the `unfazed` CLI backend's World (`createWorld()`) throws.\n *\n * Its `name` is the Workflow SDK error a World would throw in its place\n * (`WorkflowWorldError`, `WorkflowRunNotFoundError`, `HookNotFoundError`),\n * because Workflow SDK's CLI and web UI pick their message by `name`,\n * `status` and `code`: a missing run reads as a 404 there, an argument the\n * backend cannot serve (`code: \"INVALID_ARGUMENT\"`) prints as given.\n * `instanceof UnfazedError` still holds.\n */\nexport class UnfazedInspectError extends UnfazedError {\n /** HTTP-like status: 404 for a missing run, step, event, hook or app; 401/403 for a refused token. */\n readonly status: number | undefined;\n /** `INVALID_ARGUMENT`, `READ_ONLY`, or undefined. */\n readonly code: string | undefined;\n /** The run, for `WorkflowRunNotFoundError`. */\n readonly runId: string | undefined;\n\n constructor(\n message: string,\n options: {\n name?: \"WorkflowWorldError\" | \"WorkflowRunNotFoundError\" | \"HookNotFoundError\";\n status?: number;\n code?: string;\n runId?: string;\n cause?: unknown;\n } = {},\n ) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = options.name ?? \"WorkflowWorldError\";\n this.status = options.status;\n this.code = options.code;\n this.runId = options.runId;\n }\n}\n\n/**\n * The server's exact \"no such workspace\" sentinel.\n *\n * A missing workspace and a workspace belonging to another tenant return the\n * SAME message on purpose, so the API cannot be used to probe for someone\n * else's ids. Treat it as \"not yours to see\", not strictly as \"absent\".\n */\nexport const WORKSPACE_NOT_FOUND = \"workspace was not found\";\n\n/** The server's exact \"no such deployment\" sentinel. See {@link WORKSPACE_NOT_FOUND}. */\nexport const DEPLOYMENT_NOT_FOUND = \"deployment was not found\";\n\n/**\n * Does this error carry EXACTLY the given not-found sentinel?\n *\n * Exact equality, deliberately. The tempting version — \"does any message\n * contain 'not found'\" — also swallows `release not found in history`,\n * `integration not found` and every future message that happens to use the\n * phrase, turning real failures into a silent `false` from a delete. These two\n * strings are stable authz sentinels with test coverage on the server side;\n * anything else is a genuine error and must keep throwing.\n */\nexport function isNotFoundSentinel(error: unknown, sentinel: string): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => e.message.trim() === sentinel)\n );\n}\n\n/**\n * The server's \"no such app here\" refusal of `deleteApp`:\n * `app \"<alias>\" was not found in workspace \"<workspace name>\"`. Anchored at\n * both ends, like the exact sentinels above.\n */\nconst APP_NOT_FOUND = /^app \"(.+)\" was not found in workspace \".*\"$/;\n\n/**\n * Does this error say that exactly `alias` is not an app of the workspace?\n *\n * The workspace name in the message varies, so this matches the shape, then\n * the alias itself: a refusal about a different alias still throws.\n */\nexport function isAppNotFound(error: unknown, alias: string): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => APP_NOT_FOUND.exec(e.message.trim())?.[1] === alias.trim())\n );\n}\n\n/**\n * Message shape of the server's per-owner duplicate workspace name error:\n * `a workspace named \"x\" already exists; reuse it (id <uuid>) or pick a\n * different name`.\n *\n * Used only to turn a lost `ensureWorkspace` race into a re-read. Requiring\n * both \"workspace\" and \"already exists\" in one clause keeps it off unrelated\n * conflicts (a duplicate ALIAS, say), and a false negative is safe: the\n * caller just gets the server's error verbatim.\n */\nconst DUPLICATE_WORKSPACE_NAME = /\\bworkspace\\b[^.;]{0,80}\\balready exists\\b/i;\n\n/** Did `createWorkspace` fail because the name is already taken by this owner? */\nexport function isDuplicateWorkspaceName(error: unknown): boolean {\n return (\n error instanceof UnfazedGraphQLError &&\n error.errors.some((e) => DUPLICATE_WORKSPACE_NAME.test(e.message))\n );\n}\n\nfunction truncate(s: string, max = 500): string {\n return s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n\n/**\n * Message shapes that mean \"the alias moved under you\".\n *\n * The first two are the platform's ACTUAL strings. A rejected rollback guard\n * returns `store.ErrAliasMoved` verbatim — every guard point in the rollback\n * and adopt paths returns that one sentinel unwrapped — and the staged-service\n * rollback wraps the same guard with its own prefix. The rest are defensive:\n * the guard ARGUMENTS named back at you, or the alias-revision concept in any\n * spelling, or `alias` sitting in the same clause as a conflict verb, so a\n * rewording on the server side does not silently disable the retry.\n *\n * Deliberately NOT matched: a bare \"conflict\", \"changed\" or \"stale\" anywhere in\n * any message. Those fire on errors that have nothing to do with the alias\n * compare-and-swap, and a false positive is not free — it spends a pointless\n * re-list AND rewraps an unrelated failure as {@link UnfazedConflictError},\n * whose message would then blame concurrent deploys for, say, a permission\n * problem.\n */\nconst CAS_PATTERNS: RegExp[] = [\n /alias was repointed by another change/i,\n /became stale before alias change/i,\n /expected[\\s_-]*current/i,\n /alias[\\s_-]*revision/i,\n /\\balias\\b[^.;]{0,80}\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b/i,\n /\\b(?:moved|repointed|changed|conflicts?|stale|mismatch)\\b[^.;]{0,80}\\balias\\b/i,\n];\n\n/**\n * Heuristic: does this GraphQL error set look like a rejected compare-and-swap?\n *\n * The server carries no machine-readable code in `extensions` — its error\n * presenter returns gqlgen's default untouched — so this has to match message\n * text. It is used ONLY to decide whether to re-list and retry once: a false\n * negative costs a surfaced error the caller can retry by hand, a false\n * positive costs an extra read and a misleading error class. Neither can\n * promote the wrong release, because the retry rebuilds the guard from a fresh\n * read and the server re-validates it.\n */\nexport function looksLikeCASConflict(errors: GraphQLResponseError[]): boolean {\n return errors.some((e) => CAS_PATTERNS.some((pattern) => pattern.test(e.message)));\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,8 +35,22 @@ export type { AppRef, ClientTimings, CommitDeploymentTarget, CommitDetail, Commi
|
|
|
35
35
|
export { help, REFERENCE } from "./reference.js";
|
|
36
36
|
export { UnfazedClient, DEFAULT_BASE_URL, DEFAULT_ENDPOINT, ENDPOINT_ENV_VAR, GRAPHQL_PATH, TOKEN_ENV_VAR, VERSION, } from "./client.js";
|
|
37
37
|
export type { ClientOptions, FetchLike, GraphQLDocument, RequestOptions, ResultOf, VariablesOf, } from "./client.js";
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Workload identity: OIDC tokens the platform issues to the app this code
|
|
40
|
+
* runs in, with only its `UNFZ_STATE_TOKEN`. `oidc` is the process's own
|
|
41
|
+
* {@link UnfazedOIDC}; `new Unfazed().oidc` is the same with the instance's
|
|
42
|
+
* endpoint and fetch.
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { oidc, oidcToken } from "unfazed";
|
|
46
|
+
* const jwt = await oidcToken("https://api.example.com");
|
|
47
|
+
* const fetch = oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export { OIDC_TOKEN_PATH, STATE_TOKEN_ENV_VAR, UnfazedOIDC, oidc, oidcToken } from "./oidc.js";
|
|
51
|
+
export type { FetchWithTokenOptions, OIDCOptions, OIDCToken, OIDCTokenOptions, OIDCTransport, TokenFetch, } from "./oidc.js";
|
|
52
|
+
export { DEPLOYMENT_NOT_FOUND, UnfazedAuthError, UnfazedConfigError, UnfazedConflictError, UnfazedDeployError, UnfazedError, UnfazedGraphQLError, UnfazedHTTPError, UnfazedNotFoundError, UnfazedOIDCError, UnfazedRunError, UnfazedTimeoutError, UnfazedUnconfirmedAppsError, UnfazedWriteError, WORKSPACE_NOT_FOUND, isDuplicateWorkspaceName, isNotFoundSentinel, isTooManyWaits, } from "./errors.js";
|
|
53
|
+
export type { GraphQLResponseError, OIDCErrorCode } from "./errors.js";
|
|
40
54
|
export type { WaitOptions } from "./wait.js";
|
|
41
55
|
/**
|
|
42
56
|
* The stored login of `npx unfazed login`, which {@link UnfazedClient} uses when
|
package/dist/index.js
CHANGED
|
@@ -32,7 +32,20 @@ export { Unfazed, parseDeploymentTimings } from "./unfazed.js";
|
|
|
32
32
|
*/
|
|
33
33
|
export { help, REFERENCE } from "./reference.js";
|
|
34
34
|
export { UnfazedClient, DEFAULT_BASE_URL, DEFAULT_ENDPOINT, ENDPOINT_ENV_VAR, GRAPHQL_PATH, TOKEN_ENV_VAR, VERSION, } from "./client.js";
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Workload identity: OIDC tokens the platform issues to the app this code
|
|
37
|
+
* runs in, with only its `UNFZ_STATE_TOKEN`. `oidc` is the process's own
|
|
38
|
+
* {@link UnfazedOIDC}; `new Unfazed().oidc` is the same with the instance's
|
|
39
|
+
* endpoint and fetch.
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { oidc, oidcToken } from "unfazed";
|
|
43
|
+
* const jwt = await oidcToken("https://api.example.com");
|
|
44
|
+
* const fetch = oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export { OIDC_TOKEN_PATH, STATE_TOKEN_ENV_VAR, UnfazedOIDC, oidc, oidcToken } from "./oidc.js";
|
|
48
|
+
export { DEPLOYMENT_NOT_FOUND, UnfazedAuthError, UnfazedConfigError, UnfazedConflictError, UnfazedDeployError, UnfazedError, UnfazedGraphQLError, UnfazedHTTPError, UnfazedNotFoundError, UnfazedOIDCError, UnfazedRunError, UnfazedTimeoutError, UnfazedUnconfirmedAppsError, UnfazedWriteError, WORKSPACE_NOT_FOUND, isDuplicateWorkspaceName, isNotFoundSentinel, isTooManyWaits, } from "./errors.js";
|
|
36
49
|
/**
|
|
37
50
|
* The stored login of `npx unfazed login`, which {@link UnfazedClient} uses when
|
|
38
51
|
* no token is passed and `UNFZ_API_TOKEN` is unset. The helpers that read,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAoD/D;;;;GAIG;AACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,OAAO,GACR,MAAM,aAAa,CAAC;AAUrB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AAKrB;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA4B;IAC5D,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAChE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * unfazed — official TypeScript SDK.\n *\n * The code-mode companion to the unfazed MCP server: write one script that\n * deploys, waits, reads logs and filters them locally, instead of chaining a\n * dozen tool calls through a model's context.\n *\n * ```ts\n * import { Unfazed } from \"unfazed\";\n *\n * const unfazed = new Unfazed(); // UNFZ_API_TOKEN, else your `npx unfazed login`\n * const ws = await unfazed.createWorkspace(\"hello\");\n * await unfazed.writeFiles(ws.id, {\n * \"server.ts\": `export default { fetch: () => new Response(\"hi\") }`,\n * });\n * const app = await unfazed.deployService({ workspaceId: ws.id, alias: \"hello\", wait: true });\n * console.log(app.url);\n * ```\n *\n * `help()` returns the reference: every method with its signature.\n *\n * Two layers, both public:\n * - {@link Unfazed} — the workflow layer (deploy + wait, one-commit batch\n * writes, history and restore, errors, events, one-call rollback).\n * - {@link UnfazedClient} — the typed GraphQL transport, for anything not wrapped.\n */\n\nimport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\nexport { Unfazed, parseDeploymentTimings } from \"./unfazed.js\";\nexport type {\n AppRef,\n ClientTimings,\n CommitDeploymentTarget,\n CommitDetail,\n CommitPage,\n CommitsLanded,\n CreateRunOptions,\n FollowUpRunOptions,\n DeleteAppOptions,\n DeleteAppResult,\n DeleteDeploymentOptions,\n DeleteWorkspaceOptions,\n Deployment,\n DeploymentEvent,\n DeployBoxOptions,\n DeployOptions,\n DeployResult,\n DeployServiceOptions,\n DeployStaticOptions,\n DeployTimings,\n EnvVar,\n ErrorGroup,\n ErrorGroupDetail,\n ErrorGroupPage,\n ErrorGroupRef,\n EventPage,\n EventsInput,\n EventType,\n FileCommit,\n FileContent,\n FilesMap,\n FileWriteOutcome,\n ListErrorsOptions,\n ListRunsFilter,\n LiveApp,\n PlatformEvent,\n PlatformTimings,\n RestoreVersionOptions,\n RestoreVersionResult,\n RollbackOptions,\n Run,\n TimingEvent,\n UnfazedOptions,\n Workspace,\n WorkspaceFile,\n WriteFilesOptions,\n WriteFilesResult,\n WrittenFile,\n} from \"./unfazed.js\";\n\n/**\n * The SDK's reference — every method with its signature — as a string:\n * `help()` for the page, `help(\"writeFiles\")` for one entry. The same page\n * ships as REFERENCE.md and is served at https://unfazed.dev/sdk.md.\n */\nexport { help, REFERENCE } from \"./reference.js\";\n\nexport {\n UnfazedClient,\n DEFAULT_BASE_URL,\n DEFAULT_ENDPOINT,\n ENDPOINT_ENV_VAR,\n GRAPHQL_PATH,\n TOKEN_ENV_VAR,\n VERSION,\n} from \"./client.js\";\nexport type {\n ClientOptions,\n FetchLike,\n GraphQLDocument,\n RequestOptions,\n ResultOf,\n VariablesOf,\n} from \"./client.js\";\n\nexport {\n DEPLOYMENT_NOT_FOUND,\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedConflictError,\n UnfazedDeployError,\n UnfazedError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedNotFoundError,\n UnfazedRunError,\n UnfazedTimeoutError,\n UnfazedUnconfirmedAppsError,\n UnfazedWriteError,\n WORKSPACE_NOT_FOUND,\n isDuplicateWorkspaceName,\n isNotFoundSentinel,\n isTooManyWaits,\n} from \"./errors.js\";\nexport type { GraphQLResponseError } from \"./errors.js\";\n\nexport type { WaitOptions } from \"./wait.js\";\n\n/**\n * The stored login of `npx unfazed login`, which {@link UnfazedClient} uses when\n * no token is passed and `UNFZ_API_TOKEN` is unset. The helpers that read,\n * write and refresh it are in `unfazed/credentials`.\n */\nexport { CREDENTIALS_FILE, LOGIN_COMMAND, credentialsPath } from \"./credentials.js\";\nexport type { StoredCredentials } from \"./credentials.js\";\n\nexport { UnfazedInspectError } from \"./errors.js\";\nexport { APP_ENV_VAR } from \"./remote/app.js\";\nexport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\n/**\n * The `unfazed` backend of Workflow SDK's CLI: a read-mostly World over the\n * platform's workflow inspection API, for one app.\n *\n * ```sh\n * npx unfazed login # once: signs you in, in the browser\n * UNFZ_APP=birthday-card npx workflow inspect runs --backend unfazed\n * ```\n *\n * The token is `UNFZ_API_TOKEN` when set, else the stored login of\n * `npx unfazed login`, refreshed as it expires.\n *\n * The CLI (and its `--web` UI) resolve `unfazed` from the project and call\n * this with no arguments. The app is `UNFZ_APP`, else the `alias` (or the\n * single `apps[]` entry) of `unfazed.json` in the current directory. Reads\n * and `workflow cancel` work; every other write throws, since an app's own\n * World is `unfazed/workflow`. The module behind it loads on first call, so\n * `import \"unfazed\"` stays as light as before.\n */\nexport async function createWorld(options?: RemoteWorldOptions): Promise<RemoteWorld> {\n const { createRemoteWorld } = await import(\"./remote/world.js\");\n return createRemoteWorld(options);\n}\n\n/**\n * Every operation document the workflow layer uses, for callers that want the\n * typed documents but not the wrapper methods.\n */\nexport * as operations from \"./operations.js\";\n\n/**\n * Schema types generated from the platform's `graph/schema.graphqls`: enums\n * (`DeploymentStatus`, `ServiceMode`, ...), input objects\n * (`CreateDeploymentInput`, ...) and every operation's result type.\n */\nexport type * from \"./generated/graphql.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAoD/D;;;;GAIG;AACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,OAAO,GACR,MAAM,aAAa,CAAC;AAUrB;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAU/F,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC;AAKrB;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA4B;IAC5D,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAChE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * unfazed — official TypeScript SDK.\n *\n * The code-mode companion to the unfazed MCP server: write one script that\n * deploys, waits, reads logs and filters them locally, instead of chaining a\n * dozen tool calls through a model's context.\n *\n * ```ts\n * import { Unfazed } from \"unfazed\";\n *\n * const unfazed = new Unfazed(); // UNFZ_API_TOKEN, else your `npx unfazed login`\n * const ws = await unfazed.createWorkspace(\"hello\");\n * await unfazed.writeFiles(ws.id, {\n * \"server.ts\": `export default { fetch: () => new Response(\"hi\") }`,\n * });\n * const app = await unfazed.deployService({ workspaceId: ws.id, alias: \"hello\", wait: true });\n * console.log(app.url);\n * ```\n *\n * `help()` returns the reference: every method with its signature.\n *\n * Two layers, both public:\n * - {@link Unfazed} — the workflow layer (deploy + wait, one-commit batch\n * writes, history and restore, errors, events, one-call rollback).\n * - {@link UnfazedClient} — the typed GraphQL transport, for anything not wrapped.\n */\n\nimport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\nexport { Unfazed, parseDeploymentTimings } from \"./unfazed.js\";\nexport type {\n AppRef,\n ClientTimings,\n CommitDeploymentTarget,\n CommitDetail,\n CommitPage,\n CommitsLanded,\n CreateRunOptions,\n FollowUpRunOptions,\n DeleteAppOptions,\n DeleteAppResult,\n DeleteDeploymentOptions,\n DeleteWorkspaceOptions,\n Deployment,\n DeploymentEvent,\n DeployBoxOptions,\n DeployOptions,\n DeployResult,\n DeployServiceOptions,\n DeployStaticOptions,\n DeployTimings,\n EnvVar,\n ErrorGroup,\n ErrorGroupDetail,\n ErrorGroupPage,\n ErrorGroupRef,\n EventPage,\n EventsInput,\n EventType,\n FileCommit,\n FileContent,\n FilesMap,\n FileWriteOutcome,\n ListErrorsOptions,\n ListRunsFilter,\n LiveApp,\n PlatformEvent,\n PlatformTimings,\n RestoreVersionOptions,\n RestoreVersionResult,\n RollbackOptions,\n Run,\n TimingEvent,\n UnfazedOptions,\n Workspace,\n WorkspaceFile,\n WriteFilesOptions,\n WriteFilesResult,\n WrittenFile,\n} from \"./unfazed.js\";\n\n/**\n * The SDK's reference — every method with its signature — as a string:\n * `help()` for the page, `help(\"writeFiles\")` for one entry. The same page\n * ships as REFERENCE.md and is served at https://unfazed.dev/sdk.md.\n */\nexport { help, REFERENCE } from \"./reference.js\";\n\nexport {\n UnfazedClient,\n DEFAULT_BASE_URL,\n DEFAULT_ENDPOINT,\n ENDPOINT_ENV_VAR,\n GRAPHQL_PATH,\n TOKEN_ENV_VAR,\n VERSION,\n} from \"./client.js\";\nexport type {\n ClientOptions,\n FetchLike,\n GraphQLDocument,\n RequestOptions,\n ResultOf,\n VariablesOf,\n} from \"./client.js\";\n\n/**\n * Workload identity: OIDC tokens the platform issues to the app this code\n * runs in, with only its `UNFZ_STATE_TOKEN`. `oidc` is the process's own\n * {@link UnfazedOIDC}; `new Unfazed().oidc` is the same with the instance's\n * endpoint and fetch.\n *\n * ```ts\n * import { oidc, oidcToken } from \"unfazed\";\n * const jwt = await oidcToken(\"https://api.example.com\");\n * const fetch = oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE);\n * ```\n */\nexport { OIDC_TOKEN_PATH, STATE_TOKEN_ENV_VAR, UnfazedOIDC, oidc, oidcToken } from \"./oidc.js\";\nexport type {\n FetchWithTokenOptions,\n OIDCOptions,\n OIDCToken,\n OIDCTokenOptions,\n OIDCTransport,\n TokenFetch,\n} from \"./oidc.js\";\n\nexport {\n DEPLOYMENT_NOT_FOUND,\n UnfazedAuthError,\n UnfazedConfigError,\n UnfazedConflictError,\n UnfazedDeployError,\n UnfazedError,\n UnfazedGraphQLError,\n UnfazedHTTPError,\n UnfazedNotFoundError,\n UnfazedOIDCError,\n UnfazedRunError,\n UnfazedTimeoutError,\n UnfazedUnconfirmedAppsError,\n UnfazedWriteError,\n WORKSPACE_NOT_FOUND,\n isDuplicateWorkspaceName,\n isNotFoundSentinel,\n isTooManyWaits,\n} from \"./errors.js\";\nexport type { GraphQLResponseError, OIDCErrorCode } from \"./errors.js\";\n\nexport type { WaitOptions } from \"./wait.js\";\n\n/**\n * The stored login of `npx unfazed login`, which {@link UnfazedClient} uses when\n * no token is passed and `UNFZ_API_TOKEN` is unset. The helpers that read,\n * write and refresh it are in `unfazed/credentials`.\n */\nexport { CREDENTIALS_FILE, LOGIN_COMMAND, credentialsPath } from \"./credentials.js\";\nexport type { StoredCredentials } from \"./credentials.js\";\n\nexport { UnfazedInspectError } from \"./errors.js\";\nexport { APP_ENV_VAR } from \"./remote/app.js\";\nexport type { RemoteWorld, RemoteWorldOptions } from \"./remote/world.js\";\n\n/**\n * The `unfazed` backend of Workflow SDK's CLI: a read-mostly World over the\n * platform's workflow inspection API, for one app.\n *\n * ```sh\n * npx unfazed login # once: signs you in, in the browser\n * UNFZ_APP=birthday-card npx workflow inspect runs --backend unfazed\n * ```\n *\n * The token is `UNFZ_API_TOKEN` when set, else the stored login of\n * `npx unfazed login`, refreshed as it expires.\n *\n * The CLI (and its `--web` UI) resolve `unfazed` from the project and call\n * this with no arguments. The app is `UNFZ_APP`, else the `alias` (or the\n * single `apps[]` entry) of `unfazed.json` in the current directory. Reads\n * and `workflow cancel` work; every other write throws, since an app's own\n * World is `unfazed/workflow`. The module behind it loads on first call, so\n * `import \"unfazed\"` stays as light as before.\n */\nexport async function createWorld(options?: RemoteWorldOptions): Promise<RemoteWorld> {\n const { createRemoteWorld } = await import(\"./remote/world.js\");\n return createRemoteWorld(options);\n}\n\n/**\n * Every operation document the workflow layer uses, for callers that want the\n * typed documents but not the wrapper methods.\n */\nexport * as operations from \"./operations.js\";\n\n/**\n * Schema types generated from the platform's `graph/schema.graphqls`: enums\n * (`DeploymentStatus`, `ServiceMode`, ...), input objects\n * (`CreateDeploymentInput`, ...) and every operation's result type.\n */\nexport type * from \"./generated/graphql.js\";\n"]}
|
package/dist/oidc.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workload identity: OIDC tokens the platform issues to an app.
|
|
3
|
+
*
|
|
4
|
+
* An app running on unfazed (a service or a cron job) asks the control plane
|
|
5
|
+
* for a short-lived JWT that names it, for one audience, and presents it to a
|
|
6
|
+
* service that trusts the issuer `https://unfazed.dev/oidc` instead of a
|
|
7
|
+
* static API key. The only credential is the app's own `UNFZ_STATE_TOKEN`,
|
|
8
|
+
* which the platform puts into each of its VMs; no `UNFZ_API_TOKEN` is
|
|
9
|
+
* involved.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { oidcToken } from "unfazed";
|
|
13
|
+
* const jwt = await oidcToken("https://api.example.com");
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Wire contract: `POST <platform>/oidc/token` with
|
|
17
|
+
* `Authorization: Bearer $UNFZ_STATE_TOKEN` and `{ audience, ttl_seconds? }`;
|
|
18
|
+
* 200 is `{ token, expires_at, token_type: "Bearer" }`, an error
|
|
19
|
+
* `{ error, error_description }`.
|
|
20
|
+
*/
|
|
21
|
+
/** Environment variable holding the app's state token, set in every unfazed service and cron VM. */
|
|
22
|
+
export declare const STATE_TOKEN_ENV_VAR = "UNFZ_STATE_TOKEN";
|
|
23
|
+
/** Path of the token endpoint under the platform origin. */
|
|
24
|
+
export declare const OIDC_TOKEN_PATH = "/oidc/token";
|
|
25
|
+
/**
|
|
26
|
+
* The `fetch` the token request goes through: the global `fetch`, or anything
|
|
27
|
+
* shaped like it (the client's `FetchLike` fits). `headers` on the response
|
|
28
|
+
* is read for `Retry-After` when present.
|
|
29
|
+
*/
|
|
30
|
+
export type OIDCTransport = (input: string, init: {
|
|
31
|
+
method: string;
|
|
32
|
+
headers: Record<string, string>;
|
|
33
|
+
body: string;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}) => Promise<{
|
|
36
|
+
status: number;
|
|
37
|
+
headers?: {
|
|
38
|
+
get(name: string): string | null;
|
|
39
|
+
};
|
|
40
|
+
text(): Promise<string>;
|
|
41
|
+
}>;
|
|
42
|
+
/** A `fetch` as the OpenAI SDK (and most HTTP clients) take one. */
|
|
43
|
+
export type TokenFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
44
|
+
export interface OIDCOptions {
|
|
45
|
+
/** The app's state token. Default: `UNFZ_STATE_TOKEN`, read on every call. */
|
|
46
|
+
stateToken?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Full GraphQL endpoint; tokens come from its origin. Wins over `baseUrl`.
|
|
49
|
+
* Falls back to `UNFZ_API_URL`, then `https://unfazed.dev` (as the client).
|
|
50
|
+
*/
|
|
51
|
+
endpoint?: string;
|
|
52
|
+
/** Platform origin; `/oidc/token` is appended. */
|
|
53
|
+
baseUrl?: string;
|
|
54
|
+
/** Where `UNFZ_STATE_TOKEN` and `UNFZ_API_URL` are read from. Default: `process.env`. */
|
|
55
|
+
env?: Record<string, string | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* The transport of the token request. Default: the global `fetch`. An
|
|
58
|
+
* instance with its own `fetch` (or `now`) keeps its own cache; every other
|
|
59
|
+
* one shares the process's.
|
|
60
|
+
*/
|
|
61
|
+
fetch?: OIDCTransport;
|
|
62
|
+
/** Overrides the default `unfazed-sdk/<version>` User-Agent. */
|
|
63
|
+
userAgent?: string;
|
|
64
|
+
/** Timeout of one attempt, in ms. Default 10_000; 0 disables it. */
|
|
65
|
+
timeoutMs?: number;
|
|
66
|
+
/** Injected wall clock, ms (tests). */
|
|
67
|
+
now?: () => number;
|
|
68
|
+
/** Injected sleep between retries (tests). */
|
|
69
|
+
sleep?: (ms: number) => Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export interface OIDCTokenOptions {
|
|
72
|
+
/** Token lifetime, a whole number of seconds from 60 to 3600. Default: the platform's. */
|
|
73
|
+
ttlSeconds?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Stops waiting, with {@link UnfazedTimeoutError}. A request other callers
|
|
76
|
+
* wait on too keeps going (and fills the cache).
|
|
77
|
+
*/
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
}
|
|
80
|
+
/** A token with its expiry, from {@link UnfazedOIDC.tokenResponse}. */
|
|
81
|
+
export interface OIDCToken {
|
|
82
|
+
/** The JWT. */
|
|
83
|
+
token: string;
|
|
84
|
+
/** When it expires (the platform's `expires_at`). */
|
|
85
|
+
expiresAt: Date;
|
|
86
|
+
}
|
|
87
|
+
export interface FetchWithTokenOptions {
|
|
88
|
+
/** Token lifetime, a whole number of seconds from 60 to 3600. Default: the platform's. */
|
|
89
|
+
ttlSeconds?: number;
|
|
90
|
+
/** The `fetch` that sends the request. Default: the global `fetch`, at call time. */
|
|
91
|
+
fetch?: TokenFetch;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Workload identity tokens for the app this code runs in: `unfazed.oidc` on an
|
|
95
|
+
* {@link import("./unfazed.js").Unfazed} instance, or the standalone `oidc`.
|
|
96
|
+
*
|
|
97
|
+
* Tokens are cached per (audience, ttl) and reused until 80% of their
|
|
98
|
+
* lifetime has passed; concurrent calls for the same pair share one request.
|
|
99
|
+
* 503 (and a gateway's 502/504), 429 and network failures are retried up to 3
|
|
100
|
+
* times (200, 400, 800 ms; a 429's or 503's `Retry-After` up to 10 s is waited
|
|
101
|
+
* out instead); no other 4xx is retried. When a refresh fails for one of
|
|
102
|
+
* those passing reasons, the token it was replacing is returned while it is
|
|
103
|
+
* still valid.
|
|
104
|
+
*/
|
|
105
|
+
export declare class UnfazedOIDC {
|
|
106
|
+
#private;
|
|
107
|
+
constructor(options?: OIDCOptions);
|
|
108
|
+
/**
|
|
109
|
+
* A JWT for `audience`, from the cache while it is fresh.
|
|
110
|
+
*
|
|
111
|
+
* ```ts
|
|
112
|
+
* const jwt = await unfazed.oidc.token("https://api.example.com", { ttlSeconds: 300 });
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* Throws {@link UnfazedOIDCError}: `not_in_app` without `UNFZ_STATE_TOKEN`,
|
|
116
|
+
* `invalid_audience`, `invalid_request` (`ttlSeconds`), or the platform's
|
|
117
|
+
* code.
|
|
118
|
+
*/
|
|
119
|
+
token(audience: string | undefined, options?: OIDCTokenOptions): Promise<string>;
|
|
120
|
+
/** {@link token}, with the token's expiry: `{ token, expiresAt }`. */
|
|
121
|
+
tokenResponse(audience: string | undefined, options?: OIDCTokenOptions): Promise<OIDCToken>;
|
|
122
|
+
/**
|
|
123
|
+
* A `fetch` that sends every request with `Authorization: Bearer <token>`,
|
|
124
|
+
* a fresh token for `audience` each time (from the cache while it lasts).
|
|
125
|
+
* Every other header, the body and the signal pass through; an
|
|
126
|
+
* `Authorization` already there is replaced.
|
|
127
|
+
*
|
|
128
|
+
* ```ts
|
|
129
|
+
* const openai = new OpenAI({
|
|
130
|
+
* baseURL: process.env.PRIVATEINFERENCE_BASE_URL,
|
|
131
|
+
* apiKey: "unused",
|
|
132
|
+
* fetch: unfazed.oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE),
|
|
133
|
+
* });
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* An invalid `audience` (an unset variable among them) throws here, at
|
|
137
|
+
* once; a missing `UNFZ_STATE_TOKEN` on the first request.
|
|
138
|
+
*/
|
|
139
|
+
fetchWithToken(audience: string | undefined, options?: FetchWithTokenOptions): TokenFetch;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The process's own {@link UnfazedOIDC}: `UNFZ_STATE_TOKEN` and `UNFZ_API_URL`
|
|
143
|
+
* read on every call, the global `fetch`, one cache.
|
|
144
|
+
*
|
|
145
|
+
* ```ts
|
|
146
|
+
* import { oidc } from "unfazed";
|
|
147
|
+
* const fetchWithToken = oidc.fetchWithToken(process.env.PRIVATEINFERENCE_AUDIENCE);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare const oidc: UnfazedOIDC;
|
|
151
|
+
/**
|
|
152
|
+
* A JWT for `audience`, issued to the app this code runs in:
|
|
153
|
+
* {@link UnfazedOIDC.token} on the process's {@link oidc}. Needs only
|
|
154
|
+
* `UNFZ_STATE_TOKEN`.
|
|
155
|
+
*
|
|
156
|
+
* ```ts
|
|
157
|
+
* import { oidcToken } from "unfazed";
|
|
158
|
+
* const res = await fetch(url, { headers: { Authorization: `Bearer ${await oidcToken(url)}` } });
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare function oidcToken(audience: string | undefined, options?: OIDCTokenOptions): Promise<string>;
|