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/dist/reference.js CHANGED
@@ -24,12 +24,12 @@ export function help(topic) {
24
24
  const section = REFERENCE.split(/\n(?=## )/).find((block) => firstLine(block) === `## ${wanted}`);
25
25
  if (section)
26
26
  return section.trim() + "\n";
27
- // A method: every entry whose heading names it.
28
- const method = new RegExp(`\`${escape(wanted)}\\(|[ /]${escape(wanted)}\\(`);
27
+ // A method: every entry whose heading names it (`token` finds `oidc.token(`).
28
+ const method = new RegExp(`\`${escape(wanted)}\\(|[ /.]${escape(wanted)}\\(`);
29
29
  const matches = REFERENCE.split(/\n(?=#{2,3} )/).filter((block) => block.startsWith("### ") && method.test(firstLine(block)));
30
30
  if (matches.length > 0)
31
31
  return matches.map((block) => block.trim()).join("\n\n") + "\n";
32
- const names = [...REFERENCE.matchAll(/^### `(\w+)\(/gm)].map((m) => m[1]);
32
+ const names = [...REFERENCE.matchAll(/^### `([\w.]+)\(/gm)].map((m) => m[1]);
33
33
  return `No entry for "${topic}". Methods: ${names.join(", ")}. help() returns the whole reference.\n`;
34
34
  }
35
35
  function escape(text) {
@@ -1 +1 @@
1
- {"version":3,"file":"reference.js","sourceRoot":"","sources":["../src/reference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAAC,KAAc;IACjC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/D,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;IAC5E,kDAAkD;IAClD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,MAAM,MAAM,EAAE,CAAC,CAAC;IAClG,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC;IAC1C,gDAAgD;IAChD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,CACrD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CACrE,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACxF,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,iBAAiB,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC,CAAC;AACxG,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import { REFERENCE } from \"./reference.generated.js\";\n\nexport { REFERENCE };\n\n/**\n * The SDK's reference, for a script (or a model) that needs a signature:\n * every method of {@link Unfazed} with its parameters, result and an\n * example, in one page (REFERENCE.md). It describes THIS version, the one\n * the script imported (`VERSION`): exactly what `help()` should answer.\n *\n * ```ts\n * import { help } from \"unfazed\";\n * export default () => help(\"writeFiles\"); // run_script returns the entry\n * ```\n *\n * `help()` is the whole page; `help(topic)` the entries whose heading names\n * it (a method like `\"restoreVersion\"`, or a section like `\"Events\"`), or the\n * list of methods when nothing matches.\n */\nexport function help(topic?: string): string {\n if (!topic) return REFERENCE;\n const wanted = topic.trim().replace(/\\(\\)$/, \"\").toLowerCase();\n const firstLine = (block: string) => block.split(\"\\n\", 1)[0]!.toLowerCase();\n // A section (\"Events\", \"Files\"): the whole of it.\n const section = REFERENCE.split(/\\n(?=## )/).find((block) => firstLine(block) === `## ${wanted}`);\n if (section) return section.trim() + \"\\n\";\n // A method: every entry whose heading names it.\n const method = new RegExp(`\\`${escape(wanted)}\\\\(|[ /]${escape(wanted)}\\\\(`);\n const matches = REFERENCE.split(/\\n(?=#{2,3} )/).filter(\n (block) => block.startsWith(\"### \") && method.test(firstLine(block)),\n );\n if (matches.length > 0) return matches.map((block) => block.trim()).join(\"\\n\\n\") + \"\\n\";\n const names = [...REFERENCE.matchAll(/^### `(\\w+)\\(/gm)].map((m) => m[1]);\n return `No entry for \"${topic}\". Methods: ${names.join(\", \")}. help() returns the whole reference.\\n`;\n}\n\nfunction escape(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"]}
1
+ {"version":3,"file":"reference.js","sourceRoot":"","sources":["../src/reference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAAC,KAAc;IACjC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/D,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;IAC5E,kDAAkD;IAClD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,MAAM,MAAM,EAAE,CAAC,CAAC;IAClG,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC;IAC1C,8EAA8E;IAC9E,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,YAAY,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,CACrD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CACrE,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACxF,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,OAAO,iBAAiB,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC,CAAC;AACxG,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import { REFERENCE } from \"./reference.generated.js\";\n\nexport { REFERENCE };\n\n/**\n * The SDK's reference, for a script (or a model) that needs a signature:\n * every method of {@link Unfazed} with its parameters, result and an\n * example, in one page (REFERENCE.md). It describes THIS version, the one\n * the script imported (`VERSION`): exactly what `help()` should answer.\n *\n * ```ts\n * import { help } from \"unfazed\";\n * export default () => help(\"writeFiles\"); // run_script returns the entry\n * ```\n *\n * `help()` is the whole page; `help(topic)` the entries whose heading names\n * it (a method like `\"restoreVersion\"`, or a section like `\"Events\"`), or the\n * list of methods when nothing matches.\n */\nexport function help(topic?: string): string {\n if (!topic) return REFERENCE;\n const wanted = topic.trim().replace(/\\(\\)$/, \"\").toLowerCase();\n const firstLine = (block: string) => block.split(\"\\n\", 1)[0]!.toLowerCase();\n // A section (\"Events\", \"Files\"): the whole of it.\n const section = REFERENCE.split(/\\n(?=## )/).find((block) => firstLine(block) === `## ${wanted}`);\n if (section) return section.trim() + \"\\n\";\n // A method: every entry whose heading names it (`token` finds `oidc.token(`).\n const method = new RegExp(`\\`${escape(wanted)}\\\\(|[ /.]${escape(wanted)}\\\\(`);\n const matches = REFERENCE.split(/\\n(?=#{2,3} )/).filter(\n (block) => block.startsWith(\"### \") && method.test(firstLine(block)),\n );\n if (matches.length > 0) return matches.map((block) => block.trim()).join(\"\\n\\n\") + \"\\n\";\n const names = [...REFERENCE.matchAll(/^### `([\\w.]+)\\(/gm)].map((m) => m[1]);\n return `No entry for \"${topic}\". Methods: ${names.join(\", \")}. help() returns the whole reference.\\n`;\n}\n\nfunction escape(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"]}
package/dist/unfazed.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { UnfazedClient, type ClientOptions, type RequestOptions } from "./client.js";
2
+ import { UnfazedOIDC } from "./oidc.js";
2
3
  import type { AppErrorGroupFieldsFragment, CreateDeploymentInput, DeploymentEventType, DeploymentFieldsFragment, FileCommitFieldsFragment, ListDeploymentsInput, RunFieldsFragment, RunStatus, ServiceMode, ServiceRuntime, SdkAppCommitQuery, SdkAppCommitsQuery, SdkAppErrorGroupQuery, SdkAppErrorsQuery, SdkCreateWorkspaceMutation, SdkDeleteAppMutation, SdkDeploymentEventsQuery, SdkEventTypesQuery, SdkPollEventsQuery, SdkRestoreVersionMutation, SdkRunHarnessesQuery, SdkWorkspaceLiveAppsQuery } from "./generated/graphql.js";
3
4
  import { type WaitOptions } from "./wait.js";
4
5
  /** A deployment as the workflow layer returns it. */
@@ -577,9 +578,19 @@ export interface UnfazedOptions extends ClientOptions {
577
578
  */
578
579
  export declare class Unfazed {
579
580
  #private;
580
- /** The underlying typed GraphQL client. Use it for un-wrapped operations. */
581
- readonly client: UnfazedClient;
581
+ /**
582
+ * Workload identity tokens for the app this code runs in
583
+ * (`oidc.token(audience)`, `oidc.fetchWithToken(audience)`). They need only
584
+ * the app's `UNFZ_STATE_TOKEN`, so they work in an `Unfazed` made without
585
+ * an API token or a login.
586
+ */
587
+ readonly oidc: UnfazedOIDC;
582
588
  constructor(options?: UnfazedOptions);
589
+ /**
590
+ * The underlying typed GraphQL client. Use it for un-wrapped operations.
591
+ * Throws {@link UnfazedAuthError} when there is no token and no login.
592
+ */
593
+ get client(): UnfazedClient;
583
594
  /** Who this token authenticates as, plus usage and resource limits. */
584
595
  whoami(options?: RequestOptions): Promise<{
585
596
  kind: import("./index.js").CurrentActorKind;
package/dist/unfazed.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { UnfazedClient } from "./client.js";
2
- import { DEPLOYMENT_NOT_FOUND, UnfazedConflictError, UnfazedDeployError, UnfazedError, UnfazedGraphQLError, UnfazedNotFoundError, UnfazedRunError, UnfazedTimeoutError, UnfazedUnconfirmedAppsError, UnfazedWriteError, WORKSPACE_NOT_FOUND, isAppNotFound, isDuplicateWorkspaceName, isNotFoundSentinel as isNotFound, isUnconfirmedAppsRefusal, isTooManyWaits, isUnsupportedField, looksLikeCASConflict, } from "./errors.js";
2
+ import { DEPLOYMENT_NOT_FOUND, UnfazedAuthError, UnfazedConflictError, UnfazedDeployError, UnfazedError, UnfazedGraphQLError, UnfazedNotFoundError, UnfazedRunError, UnfazedTimeoutError, UnfazedUnconfirmedAppsError, UnfazedWriteError, WORKSPACE_NOT_FOUND, isAppNotFound, isDuplicateWorkspaceName, isNotFoundSentinel as isNotFound, isUnconfirmedAppsRefusal, isTooManyWaits, isUnsupportedField, looksLikeCASConflict, } from "./errors.js";
3
+ import { UnfazedOIDC } from "./oidc.js";
3
4
  import * as ops from "./operations.js";
4
5
  import { pollWithBackoff, resolveWaitOptions } from "./wait.js";
5
6
  /**
@@ -20,11 +21,51 @@ import { pollWithBackoff, resolveWaitOptions } from "./wait.js";
20
21
  * ```
21
22
  */
22
23
  export class Unfazed {
23
- /** The underlying typed GraphQL client. Use it for un-wrapped operations. */
24
- client;
24
+ #client;
25
+ /** Why there is no client: no token and no stored login. */
26
+ #noClient;
27
+ /**
28
+ * Workload identity tokens for the app this code runs in
29
+ * (`oidc.token(audience)`, `oidc.fetchWithToken(audience)`). They need only
30
+ * the app's `UNFZ_STATE_TOKEN`, so they work in an `Unfazed` made without
31
+ * an API token or a login.
32
+ */
33
+ oidc;
25
34
  constructor(options = {}) {
26
35
  const { client, ...clientOptions } = options;
27
- this.client = client ?? new UnfazedClient(clientOptions);
36
+ let made = client;
37
+ if (!made) {
38
+ try {
39
+ made = new UnfazedClient(clientOptions);
40
+ }
41
+ catch (error) {
42
+ // An app has its state token but no API token: `oidc` still works,
43
+ // and the first call that needs the API says how to sign in.
44
+ if (!(error instanceof UnfazedAuthError))
45
+ throw error;
46
+ this.#noClient = error;
47
+ }
48
+ }
49
+ this.#client = made;
50
+ // Tokens come from the platform the client talks to: its endpoint already
51
+ // resolved `endpoint`, `baseUrl`, `UNFZ_API_URL` and the stored login.
52
+ const endpoint = made?.endpoint ?? options.endpoint;
53
+ this.oidc = new UnfazedOIDC({
54
+ ...(endpoint ? { endpoint } : {}),
55
+ ...(!made && options.baseUrl ? { baseUrl: options.baseUrl } : {}),
56
+ ...(options.env ? { env: options.env } : {}),
57
+ ...(options.fetch ? { fetch: options.fetch } : {}),
58
+ ...(options.userAgent !== undefined ? { userAgent: options.userAgent } : {}),
59
+ });
60
+ }
61
+ /**
62
+ * The underlying typed GraphQL client. Use it for un-wrapped operations.
63
+ * Throws {@link UnfazedAuthError} when there is no token and no login.
64
+ */
65
+ get client() {
66
+ if (this.#client)
67
+ return this.#client;
68
+ throw new UnfazedAuthError(this.#noClient?.message ?? "unfazed: no API client", { cause: this.#noClient });
28
69
  }
29
70
  /* ---------------------------------------------------------------- identity */
30
71
  /** Who this token authenticates as, plus usage and resource limits. */