@atlasauth/backend 0.14.1 → 0.16.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 +42 -0
- package/dist/access-control.d.ts +53 -0
- package/dist/access-control.js +46 -0
- package/dist/access-control.js.map +1 -0
- package/dist/algs.d.ts +7 -0
- package/dist/algs.js +11 -0
- package/dist/algs.js.map +1 -0
- package/dist/botid.d.ts +70 -0
- package/dist/botid.js +88 -0
- package/dist/botid.js.map +1 -0
- package/dist/client/action-gallery.d.ts +215 -0
- package/dist/client/action-gallery.js +66 -0
- package/dist/client/action-gallery.js.map +1 -0
- package/dist/client/actions.d.ts +21 -0
- package/dist/client/actions.js.map +1 -1
- package/dist/client/api-keys.d.ts +49 -4
- package/dist/client/api-keys.js +6 -2
- package/dist/client/api-keys.js.map +1 -1
- package/dist/client/app-roles.d.ts +76 -0
- package/dist/client/app-roles.js +39 -0
- package/dist/client/app-roles.js.map +1 -0
- package/dist/client/attack-protection.d.ts +33 -1
- package/dist/client/attack-protection.js.map +1 -1
- package/dist/client/billing-plans.d.ts +93 -0
- package/dist/client/billing-plans.js +31 -0
- package/dist/client/billing-plans.js.map +1 -1
- package/dist/client/custom-oauth-providers.d.ts +243 -0
- package/dist/client/custom-oauth-providers.js +60 -0
- package/dist/client/custom-oauth-providers.js.map +1 -0
- package/dist/client/email-templates.d.ts +1 -1
- package/dist/client/email-templates.js.map +1 -1
- package/dist/client/index.d.ts +157 -3
- package/dist/client/index.js +11 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/instance-security.d.ts +104 -0
- package/dist/client/instance-security.js +28 -0
- package/dist/client/instance-security.js.map +1 -1
- package/dist/client/notifications.d.ts +1 -1
- package/dist/client/notifications.js.map +1 -1
- package/dist/client/oauth-clients.d.ts +84 -2
- package/dist/client/oauth-clients.js +13 -0
- package/dist/client/oauth-clients.js.map +1 -1
- package/dist/client/referrals.d.ts +1 -1
- package/dist/client/resource-servers.d.ts +34 -5
- package/dist/client/resource-servers.js.map +1 -1
- package/dist/client/risk-based-mfa.d.ts +3 -0
- package/dist/client/risk-based-mfa.js.map +1 -1
- package/dist/client/scim-tokens.d.ts +18 -0
- package/dist/client/scim-tokens.js +4 -0
- package/dist/client/scim-tokens.js.map +1 -1
- package/dist/client/sessions.d.ts +16 -0
- package/dist/client/sessions.js +15 -0
- package/dist/client/sessions.js.map +1 -1
- package/dist/client/sso-connections.d.ts +105 -0
- package/dist/client/sso-connections.js +29 -0
- package/dist/client/sso-connections.js.map +1 -1
- package/dist/client/users.d.ts +40 -4
- package/dist/client/users.js +8 -3
- package/dist/client/users.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/logout-token.d.ts +1 -1
- package/dist/logout-token.js +3 -2
- package/dist/logout-token.js.map +1 -1
- package/dist/oauth-resource.d.ts +225 -0
- package/dist/oauth-resource.js +469 -0
- package/dist/oauth-resource.js.map +1 -0
- package/dist/oidc.d.ts +1 -1
- package/dist/oidc.js +5 -3
- package/dist/oidc.js.map +1 -1
- package/dist/referral-test-calls.d.ts +7 -7
- package/dist/referral-test-calls.js +2 -2
- package/dist/testing.d.ts +6 -4
- package/dist/testing.js +7 -6
- package/dist/testing.js.map +1 -1
- package/dist/verify.d.ts +11 -0
- package/dist/verify.js +15 -1
- package/dist/verify.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @atlasauth/backend changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0 — 2026-10-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `actionGallery` (`list`, `get`, `install`, `installs`, `update`) and `actionTemplates` (`list`, `create`, `get`, `update`, `delete`, `addVersion`, `submitVersion`, `deleteVersion`, `publish`, `unlist`) for the Actions gallery.
|
|
8
|
+
- `ActionTestResult.http_requests` (what `api.http` queued in a dry run) and `organizations` / `country` on the sample event.
|
|
9
|
+
- DPoP server nonces (RFC 9449 §8) in `createAccessTokenVerifier`: `dpopNonces` requires every DPoP proof to carry a nonce the API issued, answering `use_dpop_nonce` with a fresh nonce otherwise; every result carries `dpopNonce` for the `DPoP-Nonce` response header. `memoryDpopNonceSource()` (one process) and `hmacDpopNonceSource(secret)` (stateless, shared by every process — and every Atlas server SDK — holding the same secret).
|
|
10
|
+
- Opaque access tokens: `createAccessTokenVerifier({ introspection: { clientId, clientSecret } })` validates a token that is not a JWT at the instance's introspection endpoint (RFC 7662), caching each answer (60 seconds by default, never past `exp`; inactive answers 10 seconds). `introspectJwts: true` checks JWTs live too, so a revoked grant is refused before the token expires. DPoP-bound opaque tokens keep their sender constraint (`cnf.jkt` from introspection). `createIntrospector` is the cache on its own.
|
|
11
|
+
- OAuth clients: `access_token_format` (`jwt` | `opaque`) and `token_lifetimes` (`OAuthTokenLifetimes`) on create, update and reads. Resource servers: `access_token_format`, `refresh_token_absolute_ttl_seconds`, `refresh_token_idle_ttl_seconds`, `introspection_client_ids`.
|
|
12
|
+
- `RotateSigningKeyBody.key` (`main` | `secondary`) and `SigningKeyRotation.secondary_kid` for KMS setups with one key per algorithm (`config.secondary`).
|
|
13
|
+
- KMS-backed signing keys: `instanceSecurity.getSigningKeyProvider()` / `setSigningKeyProvider({ provider, config, credentials })` / `removeSigningKeyProvider()` for AWS KMS, Google Cloud KMS and Azure Key Vault; `SigningKey.provider`.
|
|
14
|
+
- SSO connections: SAML Single Logout fields (`saml_single_logout`, `saml_idp_slo_url`, `saml_idp_slo_response_url`, `saml_idp_slo_binding`, `saml_idp_slo_soap_url` for back-channel logout) and `saml_require_encrypted_assertions`. Reads add `has_saml_encryption_key`, `has_previous_saml_encryption_key`, `saml_encryption_rotated_at`; `SamlMetadata.slo_url`. New `getSamlEncryptionKey()`, `createSamlEncryptionKey()` (generate or rotate), `retirePreviousSamlEncryptionKey()` and `deleteSamlEncryptionKey()` (`SamlEncryptionKey`).
|
|
15
|
+
- Custom OAuth 2.0 / OpenID Connect login providers: `customOAuthProviders` (`list`, `get`, `create`, `update`, `delete`, `discover`, `previewProfileMapping`, and `signingKeys(id)` with `list` / `rotate` / `retire` for `private_key_jwt`), with `ProfileMapping`, `TokenEndpointAuthMethod`, upstream (RP-initiated and front-channel) logout and third-party initiated login settings. Enterprise OIDC connections take the same `profile_mapping`, `token_endpoint_auth_method`, `client_assertion_audience`, `logout` and `idp_initiated_login`, and report `upstream_urls`.
|
|
16
|
+
- Attack protection: `dormant_accounts` (`DormantAccountsSettings`), readable and writable; `country_restrictions` / `device_accounts` typed. Risk-based MFA weights and signals gain `dormantAccount`. Email template key `account_reactivated`.
|
|
17
|
+
- Sessions: `custom_data` on reads and on create; `sessions.setCustomData(id, data)` (replace, `null` clears) and `sessions.updateCustomData(id, patch)` (merge).
|
|
18
|
+
- Billing: add-on plans (`kind: 'base' | 'addon'`) and several subscriptions per subject (`billing.getSubscription(id)`, and `changeSubscription` / `cancelSubscription` / `restoreSubscription` by subscription id); providers `chargebee`, `autumn` and `commet` (`BillingProviderName`); plan `limits` and Autumn `seat_feature_id`; `syncPlanEntitlements(id)`; `getSettings()` / `updateSettings()` for how limits merge across subscriptions (`max` | `sum`); `checkFeature({ subject_type, subject_id, feature, required })`; metered usage takes `feature` and `subscription_id`.
|
|
19
|
+
- Vercel BotID: `botIdAttestationHandler({ secret, checkBotId })` (a fetch-style route handler for your Vercel deployment) and `createBotIdAttestation()`, which sign a BotID verdict for Atlas's `vercel_botid` captcha provider.
|
|
20
|
+
- `instanceSecurity.setWalletRpcSecret({ chain_id, url })`: the wallet RPC endpoint for an EIP-155 chain (token gating and the Sign in with Ethereum ENS lookup); captcha secrets take `provider`.
|
|
21
|
+
|
|
22
|
+
## 0.15.0 — 2026-10-23
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `createAccessTokenVerifier` / `verifyAccessToken`: verify Atlas-issued OAuth access tokens in your own API or MCP server — signature against the instance JWKS, issuer, expiry, access-token type, audience (RFC 8707), required scopes (scope + permissions) and DPoP sender constraint (RFC 9449, with a replay store). Failures return the status and the RFC 6750 / RFC 9728 `WWW-Authenticate` challenge (`resource_metadata`, `insufficient_scope`).
|
|
27
|
+
- `protectedResourceMetadata` / `protectedResourceMetadataPath`: build and locate your API's RFC 9728 protected-resource metadata document.
|
|
28
|
+
- `buildWwwAuthenticate`, `memoryDpopReplayStore`.
|
|
29
|
+
- `createTestIssuer({ alg: 'ES256' })`: an ES256 test key, like an instance switched to ES256.
|
|
30
|
+
- Backend API client: `instanceSecurity.listSigningKeys()` / `rotateSigningKey({ alg, grace_period_seconds, emergency })`; `oauthClients.initialAccessTokens.list()` / `create()` / `revoke()` (RFC 7591 §3); OAuth client fields `subject_type`, `sector_identifier_uri`, `client_secret_expires_at`, `disabled`, `require_pkce` and `secret_expires_in_seconds` on create / rotate; resource server `publish_resource_metadata` and a typed `signing_alg`.
|
|
31
|
+
- `users.list({ external_id })`: find a user by the id it had in the system it was migrated from.
|
|
32
|
+
- User search: `users.list()` takes `query`, `email_address`, `username`, `phone_number`, `banned`, `email_verified`, `organization_id`, `created_after` / `created_before`, `order_by`, `offset` and `include_total_count` (the page then carries `total_count`).
|
|
33
|
+
- Timed bans: `users.ban(id, { reason, note, expires_in_seconds, expires_at })`; `User.ban_expires_at`.
|
|
34
|
+
- App roles (instance-wide, no active organization needed): `appRoles.list()` / `create()` / `get()` / `update()` / `delete()` / `listUsers()` / `getForUser()` / `setForUser()` / `removeFromUser()`; `SessionClaims.app_roles` / `app_permissions` with `hasAppRole()` / `hasAppPermission()`.
|
|
35
|
+
- `createAccessControl(statements)`: typed resource/action statements for app roles — `permission()`, `permissions()`, `allPermissions()`, `role()` (a definition ready for `appRoles.create`), and `can()` / `canAny()` against session claims.
|
|
36
|
+
- API key controls: create / update bodies take `enabled`, `rate_limit` (`ApiKeyRateLimit`), `remaining`, and create also `prefix` and `expires_in_seconds`; `apiKeys.verify(secret, { required_scopes })` and a coded refusal of a live key (`insufficient_scope`, `rate_limited`, `usage_exhausted`, with `missing_scopes`, `retry_after_ms`, `remaining`).
|
|
37
|
+
- SCIM tokens: `scopes` and `role_mappings` on create, and `scimTokens.update()`.
|
|
38
|
+
- SSO connections: `attribute_mapping` (`SsoAttributeMapping`) and `oidc_scopes`; `domain_warnings` on the connection.
|
|
39
|
+
- Email template keys `email_changed`, `verification_link`, `password_reset_link`; notification `email_changed`.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Every verifier (`AtlasBackend.verify`, the OIDC relying-party helpers, `verifyLogoutToken`, `createAccessTokenVerifier`) accepts ES256 as well as RS256, since an Atlas instance — or a single API — can now sign with ES256. Verification stays pinned to those two algorithms; `none`, HMAC and anything else are refused.
|
|
44
|
+
|
|
3
45
|
## 0.14.1
|
|
4
46
|
|
|
5
47
|
### Added
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { SessionClaims } from './verify';
|
|
2
|
+
/**
|
|
3
|
+
* Typed access-control definitions for app-level roles.
|
|
4
|
+
*
|
|
5
|
+
* Declare your resources and their actions once; the result gives you
|
|
6
|
+
* type-checked permission keys (`tickets:read`), role definitions you can send
|
|
7
|
+
* to `atlas.appRoles.create`, and checks against a verified session's
|
|
8
|
+
* `app_permissions` claim — so a typo in a permission is a compile error, not a
|
|
9
|
+
* silent deny.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const ac = createAccessControl({
|
|
13
|
+
* tickets: ['read', 'write', 'close'],
|
|
14
|
+
* billing: ['read', 'refund'],
|
|
15
|
+
* } as const);
|
|
16
|
+
*
|
|
17
|
+
* const support = ac.role('support', 'Support agent', { tickets: ['read', 'write'] });
|
|
18
|
+
* await atlas.appRoles.create(support); // { key, name, permissions: [...] }
|
|
19
|
+
*
|
|
20
|
+
* if (!ac.can(claims, { tickets: ['close'] })) return forbidden();
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export type AccessStatements = Readonly<Record<string, readonly string[]>>;
|
|
24
|
+
/** Every `resource:action` key a statement set allows. */
|
|
25
|
+
export type PermissionOf<S extends AccessStatements> = {
|
|
26
|
+
[R in keyof S & string]: `${R}:${S[R][number]}`;
|
|
27
|
+
}[keyof S & string];
|
|
28
|
+
/** A partial grant: some actions on some resources. */
|
|
29
|
+
export type AccessGrant<S extends AccessStatements> = {
|
|
30
|
+
[R in keyof S]?: ReadonlyArray<S[R][number]>;
|
|
31
|
+
};
|
|
32
|
+
export interface AppRoleDefinition<S extends AccessStatements> {
|
|
33
|
+
key: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
permissions: Array<PermissionOf<S>>;
|
|
37
|
+
}
|
|
38
|
+
export interface AccessControl<S extends AccessStatements> {
|
|
39
|
+
readonly statements: S;
|
|
40
|
+
/** One typed permission key. */
|
|
41
|
+
permission<R extends keyof S & string>(resource: R, action: S[R][number]): PermissionOf<S>;
|
|
42
|
+
/** Flatten a grant into sorted permission keys. */
|
|
43
|
+
permissions(grant: AccessGrant<S>): Array<PermissionOf<S>>;
|
|
44
|
+
/** Every permission the statements define. */
|
|
45
|
+
allPermissions(): Array<PermissionOf<S>>;
|
|
46
|
+
/** An app role definition (the body `appRoles.create` takes). */
|
|
47
|
+
role(key: string, name: string, grant: AccessGrant<S>, description?: string): AppRoleDefinition<S>;
|
|
48
|
+
/** Whether the verified claims hold EVERY permission in the grant (from `app_permissions`). */
|
|
49
|
+
can(claims: SessionClaims | null | undefined, grant: AccessGrant<S>): boolean;
|
|
50
|
+
/** Whether the verified claims hold AT LEAST ONE permission in the grant. */
|
|
51
|
+
canAny(claims: SessionClaims | null | undefined, grant: AccessGrant<S>): boolean;
|
|
52
|
+
}
|
|
53
|
+
export declare function createAccessControl<const S extends AccessStatements>(statements: S): AccessControl<S>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAccessControl = createAccessControl;
|
|
4
|
+
const authz_1 = require("@atlasauth/authz");
|
|
5
|
+
function createAccessControl(statements) {
|
|
6
|
+
for (const [resource, actions] of Object.entries(statements)) {
|
|
7
|
+
if (!/^[a-z][a-z0-9_]{0,63}$/.test(resource) || resource === 'org') {
|
|
8
|
+
throw new Error(`Invalid resource "${resource}": lowercase letters, digits and _, not "org".`);
|
|
9
|
+
}
|
|
10
|
+
for (const action of actions) {
|
|
11
|
+
if (!/^[a-z*][a-z0-9_*]{0,63}$/.test(action))
|
|
12
|
+
throw new Error(`Invalid action "${action}" on "${resource}".`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const permissions = (grant) => {
|
|
16
|
+
const out = new Set();
|
|
17
|
+
for (const [resource, actions] of Object.entries(grant)) {
|
|
18
|
+
const allowed = statements[resource];
|
|
19
|
+
if (!allowed)
|
|
20
|
+
throw new Error(`Unknown resource "${resource}".`);
|
|
21
|
+
for (const action of actions ?? []) {
|
|
22
|
+
if (!allowed.includes(action))
|
|
23
|
+
throw new Error(`Unknown action "${action}" on "${resource}".`);
|
|
24
|
+
out.add(`${resource}:${action}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [...out].sort();
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
statements,
|
|
31
|
+
permission: (resource, action) => `${resource}:${String(action)}`,
|
|
32
|
+
permissions,
|
|
33
|
+
allPermissions: () => Object.entries(statements)
|
|
34
|
+
.flatMap(([r, actions]) => actions.map((a) => `${r}:${a}`))
|
|
35
|
+
.sort(),
|
|
36
|
+
role: (key, name, grant, description) => ({
|
|
37
|
+
key,
|
|
38
|
+
name,
|
|
39
|
+
...(description !== undefined ? { description } : {}),
|
|
40
|
+
permissions: permissions(grant),
|
|
41
|
+
}),
|
|
42
|
+
can: (claims, grant) => (0, authz_1.hasFromClaims)(claims, { allAppPermissions: permissions(grant) }),
|
|
43
|
+
canAny: (claims, grant) => (0, authz_1.hasFromClaims)(claims, { anyAppPermission: permissions(grant) }),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=access-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-control.js","sourceRoot":"","sources":["../src/access-control.ts"],"names":[],"mappings":";;AAyDA,kDAwCC;AAjGD,4CAAiD;AAyDjD,SAAgB,mBAAmB,CAAmC,UAAa;IACjF,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACnE,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,gDAAgD,CAAC,CAAC;QACjG,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,SAAS,QAAQ,IAAI,CAAC,CAAC;QAChH,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,KAAqB,EAA0B,EAAE;QACpE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;QAC9B,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAuD,EAAE,CAAC;YAC9G,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrC,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,IAAI,CAAC,CAAC;YACjE,KAAK,MAAM,MAAM,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,SAAS,QAAQ,IAAI,CAAC,CAAC;gBAC/F,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAA4B,CAAC;IACnD,CAAC,CAAC;IAEF,OAAO;QACL,UAAU;QACV,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,EAAqB;QACpF,WAAW;QACX,cAAc,EAAE,GAAG,EAAE,CACnB,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;aACvB,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC1D,IAAI,EAA4B;QACrC,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;YACxC,GAAG;YACH,IAAI;YACJ,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC;SAChC,CAAC;QACF,GAAG,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,iBAAiB,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACxF,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,gBAAgB,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;KAC3F,CAAC;AACJ,CAAC"}
|
package/dist/algs.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JWS algorithms Atlas signs tokens with. An instance key is RS256 (the
|
|
3
|
+
* default) or ES256; APIs may be set to either. Verification is pinned to this
|
|
4
|
+
* list — `none` and HMAC never verify — and the token's `kid` selects exactly
|
|
5
|
+
* one published key, whose type fixes which of the two applies.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ATLAS_SIGNING_ALGS: string[];
|
package/dist/algs.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ATLAS_SIGNING_ALGS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The JWS algorithms Atlas signs tokens with. An instance key is RS256 (the
|
|
6
|
+
* default) or ES256; APIs may be set to either. Verification is pinned to this
|
|
7
|
+
* list — `none` and HMAC never verify — and the token's `kid` selects exactly
|
|
8
|
+
* one published key, whose type fixes which of the two applies.
|
|
9
|
+
*/
|
|
10
|
+
exports.ATLAS_SIGNING_ALGS = ['RS256', 'ES256'];
|
|
11
|
+
//# sourceMappingURL=algs.js.map
|
package/dist/algs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"algs.js","sourceRoot":"","sources":["../src/algs.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACU,QAAA,kBAAkB,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC"}
|
package/dist/botid.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel BotID for Atlas — the server half that runs on YOUR Vercel deployment.
|
|
3
|
+
*
|
|
4
|
+
* BotID's verdict only exists inside a Vercel-hosted request (`checkBotId()`
|
|
5
|
+
* from `botid/server` reads signals Vercel's edge attaches). Atlas is not that
|
|
6
|
+
* request, so your route runs `checkBotId()` and turns the verdict into a short
|
|
7
|
+
* signed attestation; the browser sends it to Atlas as `captcha_token`, and
|
|
8
|
+
* Atlas checks the signature, freshness and `isBot === false`, and spends it
|
|
9
|
+
* (each attestation passes once).
|
|
10
|
+
*
|
|
11
|
+
* // app/api/atlas/botid/route.ts (Next.js on Vercel)
|
|
12
|
+
* import { checkBotId } from 'botid/server';
|
|
13
|
+
* import { botIdAttestationHandler } from '@atlasauth/backend';
|
|
14
|
+
* export const POST = botIdAttestationHandler({
|
|
15
|
+
* secret: process.env.ATLAS_BOTID_SECRET!,
|
|
16
|
+
* checkBotId,
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* The same secret is stored in Atlas for the `vercel_botid` captcha provider.
|
|
20
|
+
* Protect the route with BotID on the client (`initBotId({ protect: [{ path:
|
|
21
|
+
* '/api/atlas/botid', method: 'POST' }] })`) and set the captcha's
|
|
22
|
+
* `attestationUrl` to it, so the prebuilt components fetch a verdict on submit.
|
|
23
|
+
*
|
|
24
|
+
* Format (version 1): `atlas_botid.v1.<payload>.<sig>` — `payload` is base64url
|
|
25
|
+
* JSON `{ v: 1, is_bot, verified_bot, iat, exp, jti }`, `sig` is base64url
|
|
26
|
+
* HMAC-SHA256 over `atlas_botid.v1.<payload>` keyed by the shared secret.
|
|
27
|
+
*/
|
|
28
|
+
/** The prefix every attestation carries. */
|
|
29
|
+
export declare const BOTID_ATTESTATION_PREFIX = "atlas_botid.v1.";
|
|
30
|
+
/** Atlas refuses attestations that live longer than this, in seconds. */
|
|
31
|
+
export declare const BOTID_ATTESTATION_MAX_TTL_SECONDS = 300;
|
|
32
|
+
/** Atlas refuses secrets shorter than this. */
|
|
33
|
+
export declare const BOTID_SECRET_MIN_LENGTH = 32;
|
|
34
|
+
/** The fields of BotID's `checkBotId()` result this helper reads. */
|
|
35
|
+
export interface BotIdVerdict {
|
|
36
|
+
isBot: boolean;
|
|
37
|
+
/** True for a known good bot (a search engine crawler). Recorded, still refused. */
|
|
38
|
+
isVerifiedBot?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface CreateBotIdAttestationOptions {
|
|
41
|
+
/** The attestation secret shared with Atlas (at least 32 characters). */
|
|
42
|
+
secret: string;
|
|
43
|
+
/** The verdict `checkBotId()` returned for this request. */
|
|
44
|
+
verdict: BotIdVerdict;
|
|
45
|
+
/** Lifetime in seconds. Default 60, at most 300. */
|
|
46
|
+
ttlSeconds?: number;
|
|
47
|
+
/** Clock override, epoch ms (tests). */
|
|
48
|
+
now?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sign a BotID verdict for Atlas. Throws when the secret is too short (Atlas
|
|
52
|
+
* would refuse every attestation it signs). A bot verdict is still signed — it
|
|
53
|
+
* is Atlas that refuses it — so your route needs no branching.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createBotIdAttestation(options: CreateBotIdAttestationOptions): string;
|
|
56
|
+
export interface BotIdAttestationHandlerOptions {
|
|
57
|
+
/** The attestation secret shared with Atlas. */
|
|
58
|
+
secret: string;
|
|
59
|
+
/** `checkBotId` from `botid/server`. */
|
|
60
|
+
checkBotId: () => Promise<BotIdVerdict>;
|
|
61
|
+
/** Lifetime of each attestation in seconds. Default 60. */
|
|
62
|
+
ttlSeconds?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A Web-standard `(request) => Response` route handler (Next.js App Router,
|
|
66
|
+
* Hono, any fetch-style runtime): runs `checkBotId()` and answers
|
|
67
|
+
* `{ token }` with the signed attestation, `no-store`. A BotID failure answers
|
|
68
|
+
* 503 so the client never sends a token that was never checked.
|
|
69
|
+
*/
|
|
70
|
+
export declare function botIdAttestationHandler(options: BotIdAttestationHandlerOptions): (request?: Request) => Promise<Response>;
|
package/dist/botid.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BOTID_SECRET_MIN_LENGTH = exports.BOTID_ATTESTATION_MAX_TTL_SECONDS = exports.BOTID_ATTESTATION_PREFIX = void 0;
|
|
4
|
+
exports.createBotIdAttestation = createBotIdAttestation;
|
|
5
|
+
exports.botIdAttestationHandler = botIdAttestationHandler;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
/**
|
|
8
|
+
* Vercel BotID for Atlas — the server half that runs on YOUR Vercel deployment.
|
|
9
|
+
*
|
|
10
|
+
* BotID's verdict only exists inside a Vercel-hosted request (`checkBotId()`
|
|
11
|
+
* from `botid/server` reads signals Vercel's edge attaches). Atlas is not that
|
|
12
|
+
* request, so your route runs `checkBotId()` and turns the verdict into a short
|
|
13
|
+
* signed attestation; the browser sends it to Atlas as `captcha_token`, and
|
|
14
|
+
* Atlas checks the signature, freshness and `isBot === false`, and spends it
|
|
15
|
+
* (each attestation passes once).
|
|
16
|
+
*
|
|
17
|
+
* // app/api/atlas/botid/route.ts (Next.js on Vercel)
|
|
18
|
+
* import { checkBotId } from 'botid/server';
|
|
19
|
+
* import { botIdAttestationHandler } from '@atlasauth/backend';
|
|
20
|
+
* export const POST = botIdAttestationHandler({
|
|
21
|
+
* secret: process.env.ATLAS_BOTID_SECRET!,
|
|
22
|
+
* checkBotId,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* The same secret is stored in Atlas for the `vercel_botid` captcha provider.
|
|
26
|
+
* Protect the route with BotID on the client (`initBotId({ protect: [{ path:
|
|
27
|
+
* '/api/atlas/botid', method: 'POST' }] })`) and set the captcha's
|
|
28
|
+
* `attestationUrl` to it, so the prebuilt components fetch a verdict on submit.
|
|
29
|
+
*
|
|
30
|
+
* Format (version 1): `atlas_botid.v1.<payload>.<sig>` — `payload` is base64url
|
|
31
|
+
* JSON `{ v: 1, is_bot, verified_bot, iat, exp, jti }`, `sig` is base64url
|
|
32
|
+
* HMAC-SHA256 over `atlas_botid.v1.<payload>` keyed by the shared secret.
|
|
33
|
+
*/
|
|
34
|
+
/** The prefix every attestation carries. */
|
|
35
|
+
exports.BOTID_ATTESTATION_PREFIX = 'atlas_botid.v1.';
|
|
36
|
+
/** Atlas refuses attestations that live longer than this, in seconds. */
|
|
37
|
+
exports.BOTID_ATTESTATION_MAX_TTL_SECONDS = 300;
|
|
38
|
+
/** Atlas refuses secrets shorter than this. */
|
|
39
|
+
exports.BOTID_SECRET_MIN_LENGTH = 32;
|
|
40
|
+
/**
|
|
41
|
+
* Sign a BotID verdict for Atlas. Throws when the secret is too short (Atlas
|
|
42
|
+
* would refuse every attestation it signs). A bot verdict is still signed — it
|
|
43
|
+
* is Atlas that refuses it — so your route needs no branching.
|
|
44
|
+
*/
|
|
45
|
+
function createBotIdAttestation(options) {
|
|
46
|
+
const { secret, verdict } = options;
|
|
47
|
+
if (typeof secret !== 'string' || secret.length < exports.BOTID_SECRET_MIN_LENGTH) {
|
|
48
|
+
throw new Error(`The BotID attestation secret must be at least ${exports.BOTID_SECRET_MIN_LENGTH} characters.`);
|
|
49
|
+
}
|
|
50
|
+
const ttl = Math.min(Math.max(Math.floor(options.ttlSeconds ?? 60), 1), exports.BOTID_ATTESTATION_MAX_TTL_SECONDS);
|
|
51
|
+
const iat = Math.floor((options.now ?? Date.now()) / 1000);
|
|
52
|
+
const payload = Buffer.from(JSON.stringify({
|
|
53
|
+
v: 1,
|
|
54
|
+
is_bot: verdict.isBot !== false,
|
|
55
|
+
verified_bot: verdict.isVerifiedBot === true,
|
|
56
|
+
iat,
|
|
57
|
+
exp: iat + ttl,
|
|
58
|
+
jti: (0, node_crypto_1.randomBytes)(18).toString('base64url'),
|
|
59
|
+
}), 'utf8').toString('base64url');
|
|
60
|
+
const sig = (0, node_crypto_1.createHmac)('sha256', secret).update(exports.BOTID_ATTESTATION_PREFIX + payload).digest('base64url');
|
|
61
|
+
return `${exports.BOTID_ATTESTATION_PREFIX}${payload}.${sig}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A Web-standard `(request) => Response` route handler (Next.js App Router,
|
|
65
|
+
* Hono, any fetch-style runtime): runs `checkBotId()` and answers
|
|
66
|
+
* `{ token }` with the signed attestation, `no-store`. A BotID failure answers
|
|
67
|
+
* 503 so the client never sends a token that was never checked.
|
|
68
|
+
*/
|
|
69
|
+
function botIdAttestationHandler(options) {
|
|
70
|
+
return async () => {
|
|
71
|
+
let verdict;
|
|
72
|
+
try {
|
|
73
|
+
verdict = await options.checkBotId();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return new Response(JSON.stringify({ error: 'botid_unavailable' }), {
|
|
77
|
+
status: 503,
|
|
78
|
+
headers: { 'content-type': 'application/json', 'cache-control': 'no-store' },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const token = createBotIdAttestation({ secret: options.secret, verdict, ttlSeconds: options.ttlSeconds });
|
|
82
|
+
return new Response(JSON.stringify({ token }), {
|
|
83
|
+
status: 200,
|
|
84
|
+
headers: { 'content-type': 'application/json', 'cache-control': 'no-store' },
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=botid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"botid.js","sourceRoot":"","sources":["../src/botid.ts"],"names":[],"mappings":";;;AA8DA,wDAoBC;AAiBD,0DAiBC;AApHD,6CAAsD;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,4CAA4C;AAC/B,QAAA,wBAAwB,GAAG,iBAAiB,CAAC;AAE1D,yEAAyE;AAC5D,QAAA,iCAAiC,GAAG,GAAG,CAAC;AAErD,+CAA+C;AAClC,QAAA,uBAAuB,GAAG,EAAE,CAAC;AAoB1C;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,OAAsC;IAC3E,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACpC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,+BAAuB,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,iDAAiD,+BAAuB,cAAc,CAAC,CAAC;IAC1G,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,yCAAiC,CAAC,CAAC;IAC3G,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CACzB,IAAI,CAAC,SAAS,CAAC;QACb,CAAC,EAAE,CAAC;QACJ,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK,KAAK;QAC/B,YAAY,EAAE,OAAO,CAAC,aAAa,KAAK,IAAI;QAC5C,GAAG;QACH,GAAG,EAAE,GAAG,GAAG,GAAG;QACd,GAAG,EAAE,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;KAC3C,CAAC,EACF,MAAM,CACP,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxB,MAAM,GAAG,GAAG,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,gCAAwB,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxG,OAAO,GAAG,gCAAwB,GAAG,OAAO,IAAI,GAAG,EAAE,CAAC;AACxD,CAAC;AAWD;;;;;GAKG;AACH,SAAgB,uBAAuB,CAAC,OAAuC;IAC7E,OAAO,KAAK,IAAI,EAAE;QAChB,IAAI,OAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAE;gBAClE,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE;aAC7E,CAAC,CAAC;QACL,CAAC;QACD,MAAM,KAAK,GAAG,sBAAsB,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QAC1G,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;YAC7C,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE;SAC7E,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { type RequestFn } from './request';
|
|
2
|
+
import type { Action, ActionTrigger } from './actions';
|
|
3
|
+
import type { ListPage } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* §actions gallery — browse reusable Action templates (first-party and
|
|
6
|
+
* community), install one into the instance as a DISABLED action, keep installed
|
|
7
|
+
* actions up to date, and publish your own templates for review.
|
|
8
|
+
*/
|
|
9
|
+
/** Every trigger a template can target (a superset of the classic three). */
|
|
10
|
+
export type GalleryTrigger = ActionTrigger | 'pre_organization_create' | 'pre_member_add' | 'pre_invitation_create';
|
|
11
|
+
/** A secret a template reads, declared by NAME; values are never part of a template. */
|
|
12
|
+
export interface ActionTemplateSecret {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
required: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ActionGalleryEntry {
|
|
18
|
+
object: 'action_template';
|
|
19
|
+
source: 'builtin' | 'community';
|
|
20
|
+
slug: string;
|
|
21
|
+
name: string;
|
|
22
|
+
summary: string;
|
|
23
|
+
trigger: GalleryTrigger;
|
|
24
|
+
category: string;
|
|
25
|
+
tags: string[];
|
|
26
|
+
/** SPDX licence id. */
|
|
27
|
+
license: string;
|
|
28
|
+
author: {
|
|
29
|
+
name: string;
|
|
30
|
+
first_party: boolean;
|
|
31
|
+
};
|
|
32
|
+
verified: boolean;
|
|
33
|
+
featured: boolean;
|
|
34
|
+
install_count: number;
|
|
35
|
+
latest_version: string;
|
|
36
|
+
/** The code sends outbound requests with `api.http`. */
|
|
37
|
+
uses_http: boolean;
|
|
38
|
+
secrets: ActionTemplateSecret[];
|
|
39
|
+
updated_at: number | null;
|
|
40
|
+
}
|
|
41
|
+
export interface ActionGalleryDetail extends ActionGalleryEntry {
|
|
42
|
+
/** Markdown. */
|
|
43
|
+
description: string;
|
|
44
|
+
code: string;
|
|
45
|
+
changelog: string | null;
|
|
46
|
+
repository_url: string | null;
|
|
47
|
+
versions: Array<{
|
|
48
|
+
version: string;
|
|
49
|
+
changelog: string | null;
|
|
50
|
+
released_at: number | null;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
export interface ActionGalleryFilter {
|
|
54
|
+
q?: string;
|
|
55
|
+
category?: string;
|
|
56
|
+
trigger?: GalleryTrigger;
|
|
57
|
+
source?: 'builtin' | 'community';
|
|
58
|
+
}
|
|
59
|
+
export interface InstallTemplateBody {
|
|
60
|
+
/** A specific approved version (default: the latest). */
|
|
61
|
+
version?: string;
|
|
62
|
+
/** The action's name (default: the template's). */
|
|
63
|
+
name?: string;
|
|
64
|
+
/** Values for the secrets the template declares. Write-only. */
|
|
65
|
+
secrets?: Record<string, string>;
|
|
66
|
+
}
|
|
67
|
+
export interface ActionGalleryInstallResult {
|
|
68
|
+
object: 'action_gallery_install';
|
|
69
|
+
/** The new action: disabled and not bound to any flow yet. */
|
|
70
|
+
action: Action;
|
|
71
|
+
template: {
|
|
72
|
+
slug: string;
|
|
73
|
+
source: 'builtin' | 'community';
|
|
74
|
+
version: string;
|
|
75
|
+
};
|
|
76
|
+
install_id: string;
|
|
77
|
+
/** Required secrets you have not supplied yet. */
|
|
78
|
+
missing_secrets: string[];
|
|
79
|
+
}
|
|
80
|
+
export interface ActionGalleryInstallation {
|
|
81
|
+
object: 'action_gallery_installation';
|
|
82
|
+
action_id: string;
|
|
83
|
+
action_name: string;
|
|
84
|
+
enabled: boolean;
|
|
85
|
+
trigger: string;
|
|
86
|
+
source: 'builtin' | 'community';
|
|
87
|
+
template_slug: string;
|
|
88
|
+
installed_version: string;
|
|
89
|
+
latest_version: string | null;
|
|
90
|
+
update_available: boolean;
|
|
91
|
+
missing_secrets: string[];
|
|
92
|
+
update_requires_secrets: string[];
|
|
93
|
+
installed_at: number;
|
|
94
|
+
updated_at: number;
|
|
95
|
+
}
|
|
96
|
+
export interface ActionGalleryUpdateResult {
|
|
97
|
+
object: 'action_gallery_update';
|
|
98
|
+
/** false when the action already runs the latest version. */
|
|
99
|
+
updated: boolean;
|
|
100
|
+
action: Action;
|
|
101
|
+
from_version?: string;
|
|
102
|
+
version: string;
|
|
103
|
+
}
|
|
104
|
+
export type ActionTemplateStatus = 'draft' | 'in_review' | 'published' | 'unlisted' | 'rejected' | 'banned';
|
|
105
|
+
export interface ActionTemplateVersion {
|
|
106
|
+
object: 'action_template_version';
|
|
107
|
+
version: string;
|
|
108
|
+
status: 'draft' | 'pending_review' | 'approved' | 'rejected';
|
|
109
|
+
trigger: GalleryTrigger;
|
|
110
|
+
secrets: ActionTemplateSecret[];
|
|
111
|
+
changelog: string | null;
|
|
112
|
+
/** Why staff rejected it, when they did. */
|
|
113
|
+
review_note: string | null;
|
|
114
|
+
code: string;
|
|
115
|
+
submitted_at: number | null;
|
|
116
|
+
reviewed_at: number | null;
|
|
117
|
+
created_at: number;
|
|
118
|
+
}
|
|
119
|
+
/** A template this instance publishes. */
|
|
120
|
+
export interface ActionTemplate {
|
|
121
|
+
object: 'action_template';
|
|
122
|
+
id: string;
|
|
123
|
+
slug: string;
|
|
124
|
+
name: string;
|
|
125
|
+
author_name: string;
|
|
126
|
+
summary: string;
|
|
127
|
+
description: string | null;
|
|
128
|
+
category: string;
|
|
129
|
+
tags: string[];
|
|
130
|
+
trigger: GalleryTrigger;
|
|
131
|
+
license: string;
|
|
132
|
+
repository_url: string | null;
|
|
133
|
+
status: ActionTemplateStatus;
|
|
134
|
+
latest_version: string | null;
|
|
135
|
+
verified: boolean;
|
|
136
|
+
featured: boolean;
|
|
137
|
+
install_count: number;
|
|
138
|
+
view_count: number;
|
|
139
|
+
published_at: number | null;
|
|
140
|
+
created_at: number;
|
|
141
|
+
updated_at: number;
|
|
142
|
+
versions: ActionTemplateVersion[];
|
|
143
|
+
}
|
|
144
|
+
export interface ActionTemplateListing {
|
|
145
|
+
slug?: string;
|
|
146
|
+
name?: string;
|
|
147
|
+
/** Shown as the author on the gallery. */
|
|
148
|
+
author_name?: string;
|
|
149
|
+
summary?: string;
|
|
150
|
+
/** Markdown. */
|
|
151
|
+
description?: string;
|
|
152
|
+
category?: string;
|
|
153
|
+
tags?: string[];
|
|
154
|
+
trigger?: GalleryTrigger;
|
|
155
|
+
/** SPDX id (MIT, Apache-2.0, BSD-3-Clause, ISC, MPL-2.0, …). */
|
|
156
|
+
license?: string;
|
|
157
|
+
repository_url?: string | null;
|
|
158
|
+
}
|
|
159
|
+
export interface NewTemplateVersion {
|
|
160
|
+
/** MAJOR.MINOR.PATCH, higher than every existing version. */
|
|
161
|
+
version: string;
|
|
162
|
+
code: string;
|
|
163
|
+
secrets?: ActionTemplateSecret[];
|
|
164
|
+
changelog?: string;
|
|
165
|
+
/** Send to review right away. */
|
|
166
|
+
submit?: boolean;
|
|
167
|
+
}
|
|
168
|
+
export interface CreateActionTemplateBody extends ActionTemplateListing {
|
|
169
|
+
slug: string;
|
|
170
|
+
name: string;
|
|
171
|
+
author_name: string;
|
|
172
|
+
trigger: GalleryTrigger;
|
|
173
|
+
/** Optionally the first version in the same call. */
|
|
174
|
+
version?: string;
|
|
175
|
+
code?: string;
|
|
176
|
+
secrets?: ActionTemplateSecret[];
|
|
177
|
+
changelog?: string;
|
|
178
|
+
submit?: boolean;
|
|
179
|
+
}
|
|
180
|
+
export declare function actionGalleryResource(request: RequestFn): {
|
|
181
|
+
/** The gallery, best first. */
|
|
182
|
+
list(filter?: ActionGalleryFilter): Promise<ListPage<ActionGalleryEntry>>;
|
|
183
|
+
/** One template with its code, secret names and versions. */
|
|
184
|
+
get(slug: string): Promise<ActionGalleryDetail>;
|
|
185
|
+
/** Install as a new, disabled action. Supply secrets, bind it, then enable it. */
|
|
186
|
+
install(slug: string, body?: InstallTemplateBody, idempotencyKey?: string): Promise<ActionGalleryInstallResult>;
|
|
187
|
+
/** Actions installed from the gallery, with update status. */
|
|
188
|
+
installs(): Promise<ListPage<ActionGalleryInstallation>>;
|
|
189
|
+
/** Move an installed action to its template's latest version. */
|
|
190
|
+
update(actionId: string, body?: {
|
|
191
|
+
secrets?: Record<string, string>;
|
|
192
|
+
}): Promise<ActionGalleryUpdateResult>;
|
|
193
|
+
};
|
|
194
|
+
export declare function actionTemplatesResource(request: RequestFn): {
|
|
195
|
+
list(): Promise<ListPage<ActionTemplate>>;
|
|
196
|
+
create(body: CreateActionTemplateBody, idempotencyKey?: string): Promise<ActionTemplate>;
|
|
197
|
+
get(id: string): Promise<ActionTemplate>;
|
|
198
|
+
update(id: string, body: ActionTemplateListing): Promise<ActionTemplate>;
|
|
199
|
+
delete(id: string): Promise<{
|
|
200
|
+
object: "action_template";
|
|
201
|
+
id: string;
|
|
202
|
+
deleted: true;
|
|
203
|
+
}>;
|
|
204
|
+
addVersion(id: string, body: NewTemplateVersion): Promise<ActionTemplateVersion>;
|
|
205
|
+
submitVersion(id: string, version: string): Promise<ActionTemplateVersion>;
|
|
206
|
+
deleteVersion(id: string, version: string): Promise<{
|
|
207
|
+
object: "action_template_version";
|
|
208
|
+
version: string;
|
|
209
|
+
deleted: true;
|
|
210
|
+
}>;
|
|
211
|
+
/** List an approved template again after unlisting it. */
|
|
212
|
+
publish(id: string): Promise<ActionTemplate>;
|
|
213
|
+
/** Hide a template from the gallery (installs keep working). */
|
|
214
|
+
unlist(id: string): Promise<ActionTemplate>;
|
|
215
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.actionGalleryResource = actionGalleryResource;
|
|
4
|
+
exports.actionTemplatesResource = actionTemplatesResource;
|
|
5
|
+
const request_1 = require("./request");
|
|
6
|
+
function actionGalleryResource(request) {
|
|
7
|
+
return {
|
|
8
|
+
/** The gallery, best first. */
|
|
9
|
+
list(filter = {}) {
|
|
10
|
+
return request({ method: 'GET', path: '/v1/action_gallery', query: { ...filter } });
|
|
11
|
+
},
|
|
12
|
+
/** One template with its code, secret names and versions. */
|
|
13
|
+
get(slug) {
|
|
14
|
+
return request({ method: 'GET', path: `/v1/action_gallery/${(0, request_1.enc)(slug)}` });
|
|
15
|
+
},
|
|
16
|
+
/** Install as a new, disabled action. Supply secrets, bind it, then enable it. */
|
|
17
|
+
install(slug, body = {}, idempotencyKey) {
|
|
18
|
+
return request({ method: 'POST', path: `/v1/action_gallery/${(0, request_1.enc)(slug)}/install`, body, idempotencyKey });
|
|
19
|
+
},
|
|
20
|
+
/** Actions installed from the gallery, with update status. */
|
|
21
|
+
installs() {
|
|
22
|
+
return request({ method: 'GET', path: '/v1/action_gallery_installs' });
|
|
23
|
+
},
|
|
24
|
+
/** Move an installed action to its template's latest version. */
|
|
25
|
+
update(actionId, body = {}) {
|
|
26
|
+
return request({ method: 'POST', path: `/v1/actions/${(0, request_1.enc)(actionId)}/gallery_update`, body });
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function actionTemplatesResource(request) {
|
|
31
|
+
return {
|
|
32
|
+
list() {
|
|
33
|
+
return request({ method: 'GET', path: '/v1/action_templates' });
|
|
34
|
+
},
|
|
35
|
+
create(body, idempotencyKey) {
|
|
36
|
+
return request({ method: 'POST', path: '/v1/action_templates', body, idempotencyKey });
|
|
37
|
+
},
|
|
38
|
+
get(id) {
|
|
39
|
+
return request({ method: 'GET', path: `/v1/action_templates/${(0, request_1.enc)(id)}` });
|
|
40
|
+
},
|
|
41
|
+
update(id, body) {
|
|
42
|
+
return request({ method: 'PATCH', path: `/v1/action_templates/${(0, request_1.enc)(id)}`, body });
|
|
43
|
+
},
|
|
44
|
+
delete(id) {
|
|
45
|
+
return request({ method: 'DELETE', path: `/v1/action_templates/${(0, request_1.enc)(id)}` });
|
|
46
|
+
},
|
|
47
|
+
addVersion(id, body) {
|
|
48
|
+
return request({ method: 'POST', path: `/v1/action_templates/${(0, request_1.enc)(id)}/versions`, body });
|
|
49
|
+
},
|
|
50
|
+
submitVersion(id, version) {
|
|
51
|
+
return request({ method: 'POST', path: `/v1/action_templates/${(0, request_1.enc)(id)}/versions/${(0, request_1.enc)(version)}/submit` });
|
|
52
|
+
},
|
|
53
|
+
deleteVersion(id, version) {
|
|
54
|
+
return request({ method: 'DELETE', path: `/v1/action_templates/${(0, request_1.enc)(id)}/versions/${(0, request_1.enc)(version)}` });
|
|
55
|
+
},
|
|
56
|
+
/** List an approved template again after unlisting it. */
|
|
57
|
+
publish(id) {
|
|
58
|
+
return request({ method: 'POST', path: `/v1/action_templates/${(0, request_1.enc)(id)}/publish` });
|
|
59
|
+
},
|
|
60
|
+
/** Hide a template from the gallery (installs keep working). */
|
|
61
|
+
unlist(id) {
|
|
62
|
+
return request({ method: 'POST', path: `/v1/action_templates/${(0, request_1.enc)(id)}/unlist` });
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=action-gallery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-gallery.js","sourceRoot":"","sources":["../../src/client/action-gallery.ts"],"names":[],"mappings":"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"}
|