@iveri/contracts 0.5.0 → 0.6.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.
Files changed (57) hide show
  1. package/dist/cjs/api/conduit/connection.type.d.ts +100 -0
  2. package/dist/cjs/api/conduit/connection.type.d.ts.map +1 -0
  3. package/dist/cjs/api/conduit/connection.type.js +3 -0
  4. package/dist/cjs/api/conduit/connection.type.js.map +1 -0
  5. package/dist/cjs/api/conduit/connector-auth-type.enum.d.ts +42 -0
  6. package/dist/cjs/api/conduit/connector-auth-type.enum.d.ts.map +1 -0
  7. package/dist/cjs/api/conduit/connector-auth-type.enum.js +46 -0
  8. package/dist/cjs/api/conduit/connector-auth-type.enum.js.map +1 -0
  9. package/dist/cjs/api/conduit/connector.type.d.ts +179 -0
  10. package/dist/cjs/api/conduit/connector.type.d.ts.map +1 -0
  11. package/dist/cjs/api/conduit/connector.type.js +13 -0
  12. package/dist/cjs/api/conduit/connector.type.js.map +1 -0
  13. package/dist/cjs/api/conduit/dispatch-status.enum.d.ts +40 -0
  14. package/dist/cjs/api/conduit/dispatch-status.enum.d.ts.map +1 -0
  15. package/dist/cjs/api/conduit/dispatch-status.enum.js +44 -0
  16. package/dist/cjs/api/conduit/dispatch-status.enum.js.map +1 -0
  17. package/dist/cjs/api/conduit/dispatch.type.d.ts +143 -0
  18. package/dist/cjs/api/conduit/dispatch.type.d.ts.map +1 -0
  19. package/dist/cjs/api/conduit/dispatch.type.js +3 -0
  20. package/dist/cjs/api/conduit/dispatch.type.js.map +1 -0
  21. package/dist/cjs/api/conduit/index.d.ts +5 -0
  22. package/dist/cjs/api/conduit/index.d.ts.map +1 -1
  23. package/dist/cjs/api/conduit/index.js +5 -0
  24. package/dist/cjs/api/conduit/index.js.map +1 -1
  25. package/dist/cjs/enum/user-permission.enum.d.ts +40 -1
  26. package/dist/cjs/enum/user-permission.enum.d.ts.map +1 -1
  27. package/dist/cjs/enum/user-permission.enum.js +42 -0
  28. package/dist/cjs/enum/user-permission.enum.js.map +1 -1
  29. package/dist/esm/api/conduit/connection.type.d.ts +100 -0
  30. package/dist/esm/api/conduit/connection.type.d.ts.map +1 -0
  31. package/dist/esm/api/conduit/connection.type.js +2 -0
  32. package/dist/esm/api/conduit/connection.type.js.map +1 -0
  33. package/dist/esm/api/conduit/connector-auth-type.enum.d.ts +42 -0
  34. package/dist/esm/api/conduit/connector-auth-type.enum.d.ts.map +1 -0
  35. package/dist/esm/api/conduit/connector-auth-type.enum.js +43 -0
  36. package/dist/esm/api/conduit/connector-auth-type.enum.js.map +1 -0
  37. package/dist/esm/api/conduit/connector.type.d.ts +179 -0
  38. package/dist/esm/api/conduit/connector.type.d.ts.map +1 -0
  39. package/dist/esm/api/conduit/connector.type.js +10 -0
  40. package/dist/esm/api/conduit/connector.type.js.map +1 -0
  41. package/dist/esm/api/conduit/dispatch-status.enum.d.ts +40 -0
  42. package/dist/esm/api/conduit/dispatch-status.enum.d.ts.map +1 -0
  43. package/dist/esm/api/conduit/dispatch-status.enum.js +41 -0
  44. package/dist/esm/api/conduit/dispatch-status.enum.js.map +1 -0
  45. package/dist/esm/api/conduit/dispatch.type.d.ts +143 -0
  46. package/dist/esm/api/conduit/dispatch.type.d.ts.map +1 -0
  47. package/dist/esm/api/conduit/dispatch.type.js +2 -0
  48. package/dist/esm/api/conduit/dispatch.type.js.map +1 -0
  49. package/dist/esm/api/conduit/index.d.ts +5 -0
  50. package/dist/esm/api/conduit/index.d.ts.map +1 -1
  51. package/dist/esm/api/conduit/index.js +5 -0
  52. package/dist/esm/api/conduit/index.js.map +1 -1
  53. package/dist/esm/enum/user-permission.enum.d.ts +40 -1
  54. package/dist/esm/enum/user-permission.enum.d.ts.map +1 -1
  55. package/dist/esm/enum/user-permission.enum.js +42 -0
  56. package/dist/esm/enum/user-permission.enum.js.map +1 -1
  57. package/package.json +1 -1
@@ -0,0 +1,100 @@
1
+ import type { IsoDateTime } from '../../type/iso-date-time.type.js';
2
+ import type { Nullable } from '../../type/nil.type.js';
3
+ import type { UUID } from '../../type/uuid.type.js';
4
+ /**
5
+ * One tenant's configured instance of a connector: the credentials and the account details that
6
+ * turn a manifest into something that can actually send.
7
+ *
8
+ * The split is the reason a manifest can ship in the binary. A connector says "WhatsApp Cloud
9
+ * lives at graph.facebook.com and authenticates with a bearer token"; a connection says "and
10
+ * *this* is our token, for *this* phone number". One is ours and identical for everybody; the
11
+ * other is the customer's and is the single most sensitive thing this service holds.
12
+ *
13
+ * **No endpoint of this API returns a credential value**, in any form — not masked, not
14
+ * truncated, not a length. `credentialNames` answers the only question an operator has, which is
15
+ * whether something is configured. Same control as a destination's headers and an endpoint's
16
+ * signing secret, for the same reason: §11 says one leak ends the company.
17
+ */
18
+ export interface Connection {
19
+ id: UUID;
20
+ /** Stable identifier a dispatch names instead of an id, so callers can be configured by name. */
21
+ key: string;
22
+ name: string;
23
+ description: Nullable<string>;
24
+ /** The manifest this connection instantiates. Built-in or tenant-defined. */
25
+ connectorKey: string;
26
+ /** Sorted. **The values are never returned by any endpoint of this API.** */
27
+ credentialNames: string[];
28
+ /**
29
+ * Non-secret values filling `{placeholders}` in an operation path — a phone number id, an
30
+ * account id, an API version.
31
+ *
32
+ * Deliberately separate from credentials rather than one map with a secrecy flag: a flag is
33
+ * a thing somebody sets wrong once, and the failure mode is a token in a response body.
34
+ * Variables are returned in full because they are the part an operator needs to check.
35
+ */
36
+ variables: Record<string, string>;
37
+ /**
38
+ * When the cached OAuth access token stops being usable, or null when there is none —
39
+ * either because the connector authenticates statically or because nothing has fetched one
40
+ * yet.
41
+ *
42
+ * The expiry is returned, the token is not. It is what makes "why is this connection
43
+ * failing" answerable without exposing anything.
44
+ */
45
+ accessTokenExpiresAt: Nullable<IsoDateTime>;
46
+ /**
47
+ * Why the last authentication attempt failed, or null when the last one worked.
48
+ *
49
+ * A refresh token that a provider has revoked is the most common real failure on this
50
+ * aggregate, and it is invisible otherwise: dispatches simply dead-letter with a 401 and
51
+ * nothing points at the connection.
52
+ */
53
+ lastAuthError: Nullable<string>;
54
+ /** Stops *new* dispatches being accepted. Does not cancel ones already queued. */
55
+ isActive: boolean;
56
+ createdAt: IsoDateTime;
57
+ updatedAt: IsoDateTime;
58
+ }
59
+ /** `GET /api/v1/connections`. Offset-paginated — a small, slow-moving collection. */
60
+ export interface ConnectionsPage {
61
+ items: Connection[];
62
+ total: number;
63
+ limit: number;
64
+ offset: number;
65
+ }
66
+ /** `POST /api/v1/connections`. */
67
+ export interface CreateConnectionBody {
68
+ key: string;
69
+ name: string;
70
+ description?: Nullable<string>;
71
+ connectorKey: string;
72
+ /**
73
+ * Secrets this connection authenticates with. **Stored encrypted and never returned.**
74
+ *
75
+ * Which names are expected depends on the connector's auth type: an `API_KEY_HEADER`
76
+ * manifest names its own, an OAuth one wants `clientId`/`clientSecret` and, for the refresh
77
+ * grant, `refreshToken`.
78
+ */
79
+ credentials: Record<string, string>;
80
+ variables?: Nullable<Record<string, string>>;
81
+ }
82
+ /**
83
+ * `PATCH /api/v1/connections/:id`.
84
+ *
85
+ * **`credentials` has three states, not two.** Omitting it leaves the stored map untouched,
86
+ * `null` clears it, an object replaces it wholesale. A form that sends `{}` meaning "unchanged"
87
+ * deletes the customer's token, and the symptom is every dispatch dead-lettering on a 401.
88
+ *
89
+ * Replacing credentials also discards the cached access token: a token minted from the old
90
+ * client secret is not evidence that the new one works, and keeping it would hide a typo until
91
+ * the token happened to expire.
92
+ */
93
+ export interface UpdateConnectionBody {
94
+ name?: string;
95
+ description?: Nullable<string>;
96
+ credentials?: Nullable<Record<string, string>>;
97
+ variables?: Nullable<Record<string, string>>;
98
+ isActive?: boolean;
99
+ }
100
+ //# sourceMappingURL=connection.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.type.d.ts","sourceRoot":"","sources":["../../../../src/api/conduit/connection.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,IAAI,CAAC;IAET,iGAAiG;IACjG,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9B,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IAErB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,EAAE,CAAC;IAE1B;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC;;;;;;;OAOG;IACH,oBAAoB,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAE5C;;;;;;OAMG;IACH,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEhC,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAC;IAElB,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;CAC1B;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,kCAAkC;AAClC,MAAM,WAAW,oBAAoB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChD;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAoB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=connection.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.type.js","sourceRoot":"","sources":["../../../../src/api/conduit/connection.type.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * How a connector proves who we are to the third-party API it fronts.
3
+ *
4
+ * This is the field that decides whether Conduit has to hold a *renewable* credential or merely
5
+ * a static one, and the two are very different operationally: a static key works until someone
6
+ * rotates it, an OAuth token stops working on a schedule and something has to notice first.
7
+ *
8
+ * The list is deliberately short. Every member is a scheme a manifest can describe entirely as
9
+ * data — adding a provider that uses one of these is a manifest and a credential, never a code
10
+ * change, which is the whole promise of §11's connector manifest. A provider needing anything
11
+ * else (a request-signing scheme, mutual TLS) does not get a special case bolted on here; it
12
+ * gets a new member, designed once, with the same "no per-provider branches" rule applied to it.
13
+ */
14
+ export declare enum ConnectorAuthType {
15
+ /** The API is open, or the credential travels in the payload. Nothing is added to a request. */
16
+ NONE = "none",
17
+ /**
18
+ * A static secret sent in a header — `X-Api-Key: …`, `Authorization: Basic …`.
19
+ *
20
+ * The header name and any literal prefix come from the manifest; only the secret comes from
21
+ * the connection. That split is what lets one manifest serve every tenant.
22
+ */
23
+ API_KEY_HEADER = "api-key-header",
24
+ /** A static bearer token. Shorthand for {@link API_KEY_HEADER} with the header and prefix fixed. */
25
+ BEARER_STATIC = "bearer-static",
26
+ /**
27
+ * OAuth 2.0 client credentials (RFC 6749 §4.4): exchange a client id and secret for a
28
+ * short-lived access token, and do it again when it expires. Machine-to-machine, no user.
29
+ */
30
+ OAUTH2_CLIENT_CREDENTIALS = "oauth2-client-credentials",
31
+ /**
32
+ * OAuth 2.0 refresh token (RFC 6749 §6): a long-lived refresh token buys short-lived access
33
+ * tokens.
34
+ *
35
+ * **The refresh token may be rotated by the provider on every use**, and Conduit stores
36
+ * whichever one came back last. That single fact is why refreshing has to be serialised
37
+ * across replicas rather than left to race — two simultaneous refreshes rotate twice, one
38
+ * of the two results is stored, and the connection is dead until a human re-authorises it.
39
+ */
40
+ OAUTH2_REFRESH_TOKEN = "oauth2-refresh-token"
41
+ }
42
+ //# sourceMappingURL=connector-auth-type.enum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector-auth-type.enum.d.ts","sourceRoot":"","sources":["../../../../src/api/conduit/connector-auth-type.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,oBAAY,iBAAiB;IACzB,gGAAgG;IAChG,IAAI,SAAS;IAEb;;;;;OAKG;IACH,cAAc,mBAAmB;IAEjC,oGAAoG;IACpG,aAAa,kBAAkB;IAE/B;;;OAGG;IACH,yBAAyB,8BAA8B;IAEvD;;;;;;;;OAQG;IACH,oBAAoB,yBAAyB;CAChD"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectorAuthType = void 0;
4
+ /**
5
+ * How a connector proves who we are to the third-party API it fronts.
6
+ *
7
+ * This is the field that decides whether Conduit has to hold a *renewable* credential or merely
8
+ * a static one, and the two are very different operationally: a static key works until someone
9
+ * rotates it, an OAuth token stops working on a schedule and something has to notice first.
10
+ *
11
+ * The list is deliberately short. Every member is a scheme a manifest can describe entirely as
12
+ * data — adding a provider that uses one of these is a manifest and a credential, never a code
13
+ * change, which is the whole promise of §11's connector manifest. A provider needing anything
14
+ * else (a request-signing scheme, mutual TLS) does not get a special case bolted on here; it
15
+ * gets a new member, designed once, with the same "no per-provider branches" rule applied to it.
16
+ */
17
+ var ConnectorAuthType;
18
+ (function (ConnectorAuthType) {
19
+ /** The API is open, or the credential travels in the payload. Nothing is added to a request. */
20
+ ConnectorAuthType["NONE"] = "none";
21
+ /**
22
+ * A static secret sent in a header — `X-Api-Key: …`, `Authorization: Basic …`.
23
+ *
24
+ * The header name and any literal prefix come from the manifest; only the secret comes from
25
+ * the connection. That split is what lets one manifest serve every tenant.
26
+ */
27
+ ConnectorAuthType["API_KEY_HEADER"] = "api-key-header";
28
+ /** A static bearer token. Shorthand for {@link API_KEY_HEADER} with the header and prefix fixed. */
29
+ ConnectorAuthType["BEARER_STATIC"] = "bearer-static";
30
+ /**
31
+ * OAuth 2.0 client credentials (RFC 6749 §4.4): exchange a client id and secret for a
32
+ * short-lived access token, and do it again when it expires. Machine-to-machine, no user.
33
+ */
34
+ ConnectorAuthType["OAUTH2_CLIENT_CREDENTIALS"] = "oauth2-client-credentials";
35
+ /**
36
+ * OAuth 2.0 refresh token (RFC 6749 §6): a long-lived refresh token buys short-lived access
37
+ * tokens.
38
+ *
39
+ * **The refresh token may be rotated by the provider on every use**, and Conduit stores
40
+ * whichever one came back last. That single fact is why refreshing has to be serialised
41
+ * across replicas rather than left to race — two simultaneous refreshes rotate twice, one
42
+ * of the two results is stored, and the connection is dead until a human re-authorises it.
43
+ */
44
+ ConnectorAuthType["OAUTH2_REFRESH_TOKEN"] = "oauth2-refresh-token";
45
+ })(ConnectorAuthType || (exports.ConnectorAuthType = ConnectorAuthType = {}));
46
+ //# sourceMappingURL=connector-auth-type.enum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector-auth-type.enum.js","sourceRoot":"","sources":["../../../../src/api/conduit/connector-auth-type.enum.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,IAAY,iBA+BX;AA/BD,WAAY,iBAAiB;IACzB,gGAAgG;IAChG,kCAAa,CAAA;IAEb;;;;;OAKG;IACH,sDAAiC,CAAA;IAEjC,oGAAoG;IACpG,oDAA+B,CAAA;IAE/B;;;OAGG;IACH,4EAAuD,CAAA;IAEvD;;;;;;;;OAQG;IACH,kEAA6C,CAAA;AACjD,CAAC,EA/BW,iBAAiB,iCAAjB,iBAAiB,QA+B5B"}
@@ -0,0 +1,179 @@
1
+ import type { IsoDateTime } from '../../type/iso-date-time.type.js';
2
+ import type { Nullable } from '../../type/nil.type.js';
3
+ import type { UUID } from '../../type/uuid.type.js';
4
+ import type { ConnectorAuthType } from './connector-auth-type.enum.js';
5
+ /**
6
+ * Credential names an OAuth manifest expects to find on a connection.
7
+ *
8
+ * Fixed rather than manifest-configurable: these are the field names RFC 6749 gives the token
9
+ * request, so letting a manifest rename them would only allow it to be wrong.
10
+ */
11
+ export declare const CONNECTOR_CREDENTIAL_CLIENT_ID = "clientId";
12
+ export declare const CONNECTOR_CREDENTIAL_CLIENT_SECRET = "clientSecret";
13
+ export declare const CONNECTOR_CREDENTIAL_REFRESH_TOKEN = "refreshToken";
14
+ /**
15
+ * How a connector authenticates, as data.
16
+ *
17
+ * Flat with a discriminating {@link ConnectorAuthType} and nullable fields, matching
18
+ * `SignatureManifest` — a discriminated union reads better in TypeScript and validates far worse
19
+ * as a request body, and this shape has to survive `class-validator` on the way in.
20
+ *
21
+ * **A manifest holds no secret.** It names *where* the credential goes; the credential itself
22
+ * lives on a connection, encrypted. That is what makes a manifest safe to return in full to
23
+ * anyone with `conduit:connector:read` and safe to ship in the binary.
24
+ */
25
+ export interface ConnectorAuthManifest {
26
+ type: ConnectorAuthType;
27
+ /**
28
+ * Which credential on the connection holds the secret. `API_KEY_HEADER` only.
29
+ *
30
+ * OAuth manifests do not set it — they read the fixed names above, because those are defined
31
+ * by the grant rather than by us.
32
+ */
33
+ credentialKey: Nullable<string>;
34
+ /** Lowercase header the credential is sent in. `API_KEY_HEADER` only. */
35
+ headerName: Nullable<string>;
36
+ /** Literal text before the credential, e.g. `Bearer ` or `Basic `. Kept verbatim, spaces included. */
37
+ valuePrefix: Nullable<string>;
38
+ /** Token endpoint. `OAUTH2_*` only. Vetted against the SSRF blocklist like any other target. */
39
+ tokenUrl: Nullable<string>;
40
+ /** `scope` sent with the token request, when the provider wants one. */
41
+ scope: Nullable<string>;
42
+ /**
43
+ * Seconds shaved off a token's reported lifetime before it counts as expired.
44
+ *
45
+ * Not a nicety. A token that expires while a request is in flight fails with a 401 that looks
46
+ * exactly like a revoked credential, and the retry that would fix it is indistinguishable
47
+ * from one that never will. Refreshing slightly early makes that case rare instead of
48
+ * routine. Null falls back to the service default.
49
+ */
50
+ expirySkewSeconds: Nullable<number>;
51
+ }
52
+ /** {@link ConnectorAuthManifest} as sent on create/update: nullable fields may simply be omitted. */
53
+ export interface ConnectorAuthManifestInput {
54
+ type: ConnectorAuthType;
55
+ credentialKey?: Nullable<string>;
56
+ headerName?: Nullable<string>;
57
+ valuePrefix?: Nullable<string>;
58
+ tokenUrl?: Nullable<string>;
59
+ scope?: Nullable<string>;
60
+ expirySkewSeconds?: Nullable<number>;
61
+ }
62
+ /**
63
+ * One thing a connector can be asked to do.
64
+ *
65
+ * A dispatch names an operation by {@link key} rather than supplying a URL, and that is the
66
+ * whole security model of the outbound side: a caller chooses from a list somebody with
67
+ * `conduit:connector:manage` wrote down, instead of naming a host Conduit will then post to.
68
+ * Outbound would otherwise be an open proxy with authentication attached.
69
+ */
70
+ export interface ConnectorOperation {
71
+ /** Stable identifier a dispatch names. Lowercase kebab-case: `send-message`. */
72
+ key: string;
73
+ /** Human-readable label for the admin UI. */
74
+ name: string;
75
+ method: string;
76
+ /**
77
+ * Path appended to the connector's `baseUrl`.
78
+ *
79
+ * `{name}` placeholders are filled from the connection's variables first and the dispatch's
80
+ * `pathParams` second. Every placeholder must resolve, or the dispatch is refused at submit
81
+ * time rather than dead-lettering later — the caller is still on the phone at submit time.
82
+ */
83
+ path: string;
84
+ description: Nullable<string>;
85
+ }
86
+ /** Requests per minute, and how many may arrive at once. Applied per connection. */
87
+ export interface ConnectorRateLimit {
88
+ perMinute: number;
89
+ burst: number;
90
+ }
91
+ /**
92
+ * When to try again, and when to stop.
93
+ *
94
+ * Per connector rather than global because providers genuinely differ: one returns 429 with a
95
+ * generous window, another treats a burst as abuse and blocks for an hour.
96
+ */
97
+ export interface ConnectorRetryPolicy {
98
+ maxAttempts: number;
99
+ backoffBaseMs: number;
100
+ backoffMaxMs: number;
101
+ /**
102
+ * Statuses to retry **in addition** to the transient default set (5xx, 408, 425, 429).
103
+ *
104
+ * Additive, never subtractive: a manifest can say "this provider's 400 is transient", which
105
+ * is a fact about that provider, but it cannot claim a 502 is permanent — nothing about a
106
+ * gateway error is provider-specific and the ability to get it wrong buys nothing.
107
+ */
108
+ retryableStatuses: number[];
109
+ /**
110
+ * Statuses meaning the access token is stale.
111
+ *
112
+ * Handled once per attempt and unlike any other status: the token is discarded, refreshed,
113
+ * and the request is sent again immediately rather than after a backoff. A token that
114
+ * expired thirty seconds early is not a failing destination and should not be treated as one.
115
+ */
116
+ authFailureStatuses: number[];
117
+ }
118
+ /**
119
+ * A third-party API, described entirely as data. §11's "connector manifest".
120
+ *
121
+ * Conduit contains no `if (connector === 'whatsapp')` anywhere. Adding TikTok is a manifest and
122
+ * a credential; the code that sends the request never learns a provider's name.
123
+ */
124
+ export interface Connector {
125
+ /** Null for a built-in — it is not a row and cannot be edited or deleted. */
126
+ id: Nullable<UUID>;
127
+ /** Stable identifier a connection references. Lowercase kebab-case. */
128
+ key: string;
129
+ name: string;
130
+ description: Nullable<string>;
131
+ /** Absolute https base every operation path is appended to. */
132
+ baseUrl: string;
133
+ auth: ConnectorAuthManifest;
134
+ operations: ConnectorOperation[];
135
+ /** Null means only the service-wide default applies. */
136
+ rateLimit: Nullable<ConnectorRateLimit>;
137
+ retry: ConnectorRetryPolicy;
138
+ /**
139
+ * Ships with Conduit, in code rather than as a seeded row. Override it by defining your own
140
+ * under the same key — resolution checks a tenant's rows first.
141
+ */
142
+ isBuiltIn: boolean;
143
+ /** Null for a built-in. */
144
+ createdAt: Nullable<IsoDateTime>;
145
+ /** Null for a built-in. */
146
+ updatedAt: Nullable<IsoDateTime>;
147
+ }
148
+ /** Built-ins plus whatever one customer has defined. `GET /api/v1/connectors`. */
149
+ export interface ConnectorsList {
150
+ items: Connector[];
151
+ }
152
+ /** `POST /api/v1/connectors`. */
153
+ export interface CreateConnectorBody {
154
+ key: string;
155
+ name: string;
156
+ description?: Nullable<string>;
157
+ baseUrl: string;
158
+ auth: ConnectorAuthManifestInput;
159
+ operations: ConnectorOperation[];
160
+ rateLimit?: Nullable<ConnectorRateLimit>;
161
+ retry?: Nullable<ConnectorRetryPolicy>;
162
+ }
163
+ /**
164
+ * `PATCH /api/v1/connectors/:id`.
165
+ *
166
+ * `operations` replaces the list wholesale — there is no per-operation patch. Merging would make
167
+ * "remove an operation" unexpressible, and an operation nobody can remove is a URL Conduit will
168
+ * post to forever.
169
+ */
170
+ export interface UpdateConnectorBody {
171
+ name?: string;
172
+ description?: Nullable<string>;
173
+ baseUrl?: string;
174
+ auth?: ConnectorAuthManifestInput;
175
+ operations?: ConnectorOperation[];
176
+ rateLimit?: Nullable<ConnectorRateLimit>;
177
+ retry?: Nullable<ConnectorRetryPolicy>;
178
+ }
179
+ //# sourceMappingURL=connector.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.type.d.ts","sourceRoot":"","sources":["../../../../src/api/conduit/connector.type.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONNECTOR_CREDENTIAL_REFRESH_TOKEN = exports.CONNECTOR_CREDENTIAL_CLIENT_SECRET = exports.CONNECTOR_CREDENTIAL_CLIENT_ID = void 0;
4
+ /**
5
+ * Credential names an OAuth manifest expects to find on a connection.
6
+ *
7
+ * Fixed rather than manifest-configurable: these are the field names RFC 6749 gives the token
8
+ * request, so letting a manifest rename them would only allow it to be wrong.
9
+ */
10
+ exports.CONNECTOR_CREDENTIAL_CLIENT_ID = 'clientId';
11
+ exports.CONNECTOR_CREDENTIAL_CLIENT_SECRET = 'clientSecret';
12
+ exports.CONNECTOR_CREDENTIAL_REFRESH_TOKEN = 'refreshToken';
13
+ //# sourceMappingURL=connector.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.type.js","sourceRoot":"","sources":["../../../../src/api/conduit/connector.type.ts"],"names":[],"mappings":";;;AAMA;;;;;GAKG;AACU,QAAA,8BAA8B,GAAG,UAAU,CAAC;AAC5C,QAAA,kCAAkC,GAAG,cAAc,CAAC;AACpD,QAAA,kCAAkC,GAAG,cAAc,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Where one outbound dispatch is in its life.
3
+ *
4
+ * The members mirror `DeliveryStatus` exactly, and this is deliberately **not** the same enum.
5
+ * The two are separate wire contracts on separate resources, and sharing one would mean a state
6
+ * added for the inbound queue silently appears on the outbound one's documented API — a
7
+ * connector-specific state has no business showing up on a webhook delivery, and vice versa.
8
+ * Being identical today is not a reason to make them the same type; it is just what happens when
9
+ * two queues share a state machine.
10
+ *
11
+ * There is no `RATE_LIMITED` member on purpose. A dispatch held back by a connector's rate limit
12
+ * is simply `PENDING` with a later `nextAttemptAt`, because that is exactly what it is — waiting
13
+ * for its next attempt time, no differently from one waiting out a backoff. A separate state
14
+ * would suggest an operator has something to do about it.
15
+ */
16
+ export declare enum DispatchStatus {
17
+ /** Waiting for its next attempt time. The only state the processor claims from. */
18
+ PENDING = "pending",
19
+ /**
20
+ * Claimed by a replica and being attempted right now.
21
+ *
22
+ * A row stuck here past the claim timeout is assumed abandoned and reclaimed — and on this
23
+ * side that reclaim is genuinely dangerous, because the request may have already reached the
24
+ * provider. It is why every dispatch carries an idempotency key onward and why a caller must
25
+ * treat outbound as at-least-once too.
26
+ */
27
+ IN_FLIGHT = "in-flight",
28
+ /** The provider accepted it. Terminal. */
29
+ DELIVERED = "delivered",
30
+ /**
31
+ * Every attempt was used and none succeeded, or the failure was permanent. Terminal until an
32
+ * operator redrives it.
33
+ *
34
+ * **This is the dead-letter queue** — a status, not a separate resource.
35
+ */
36
+ DEAD_LETTERED = "dead-lettered",
37
+ /** An operator stopped it before it was sent. Terminal, and distinct from a failure. */
38
+ CANCELLED = "cancelled"
39
+ }
40
+ //# sourceMappingURL=dispatch-status.enum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-status.enum.d.ts","sourceRoot":"","sources":["../../../../src/api/conduit/dispatch-status.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,oBAAY,cAAc;IACtB,mFAAmF;IACnF,OAAO,YAAY;IAEnB;;;;;;;OAOG;IACH,SAAS,cAAc;IAEvB,0CAA0C;IAC1C,SAAS,cAAc;IAEvB;;;;;OAKG;IACH,aAAa,kBAAkB;IAE/B,wFAAwF;IACxF,SAAS,cAAc;CAC1B"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DispatchStatus = void 0;
4
+ /**
5
+ * Where one outbound dispatch is in its life.
6
+ *
7
+ * The members mirror `DeliveryStatus` exactly, and this is deliberately **not** the same enum.
8
+ * The two are separate wire contracts on separate resources, and sharing one would mean a state
9
+ * added for the inbound queue silently appears on the outbound one's documented API — a
10
+ * connector-specific state has no business showing up on a webhook delivery, and vice versa.
11
+ * Being identical today is not a reason to make them the same type; it is just what happens when
12
+ * two queues share a state machine.
13
+ *
14
+ * There is no `RATE_LIMITED` member on purpose. A dispatch held back by a connector's rate limit
15
+ * is simply `PENDING` with a later `nextAttemptAt`, because that is exactly what it is — waiting
16
+ * for its next attempt time, no differently from one waiting out a backoff. A separate state
17
+ * would suggest an operator has something to do about it.
18
+ */
19
+ var DispatchStatus;
20
+ (function (DispatchStatus) {
21
+ /** Waiting for its next attempt time. The only state the processor claims from. */
22
+ DispatchStatus["PENDING"] = "pending";
23
+ /**
24
+ * Claimed by a replica and being attempted right now.
25
+ *
26
+ * A row stuck here past the claim timeout is assumed abandoned and reclaimed — and on this
27
+ * side that reclaim is genuinely dangerous, because the request may have already reached the
28
+ * provider. It is why every dispatch carries an idempotency key onward and why a caller must
29
+ * treat outbound as at-least-once too.
30
+ */
31
+ DispatchStatus["IN_FLIGHT"] = "in-flight";
32
+ /** The provider accepted it. Terminal. */
33
+ DispatchStatus["DELIVERED"] = "delivered";
34
+ /**
35
+ * Every attempt was used and none succeeded, or the failure was permanent. Terminal until an
36
+ * operator redrives it.
37
+ *
38
+ * **This is the dead-letter queue** — a status, not a separate resource.
39
+ */
40
+ DispatchStatus["DEAD_LETTERED"] = "dead-lettered";
41
+ /** An operator stopped it before it was sent. Terminal, and distinct from a failure. */
42
+ DispatchStatus["CANCELLED"] = "cancelled";
43
+ })(DispatchStatus || (exports.DispatchStatus = DispatchStatus = {}));
44
+ //# sourceMappingURL=dispatch-status.enum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-status.enum.js","sourceRoot":"","sources":["../../../../src/api/conduit/dispatch-status.enum.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACH,IAAY,cA2BX;AA3BD,WAAY,cAAc;IACtB,mFAAmF;IACnF,qCAAmB,CAAA;IAEnB;;;;;;;OAOG;IACH,yCAAuB,CAAA;IAEvB,0CAA0C;IAC1C,yCAAuB,CAAA;IAEvB;;;;;OAKG;IACH,iDAA+B,CAAA;IAE/B,wFAAwF;IACxF,yCAAuB,CAAA;AAC3B,CAAC,EA3BW,cAAc,8BAAd,cAAc,QA2BzB"}
@@ -0,0 +1,143 @@
1
+ import type { CursorPage } from '../../type/cursor-page.type.js';
2
+ import type { IsoDateTime } from '../../type/iso-date-time.type.js';
3
+ import type { Nullable } from '../../type/nil.type.js';
4
+ import type { UUID } from '../../type/uuid.type.js';
5
+ import type { DispatchStatus } from './dispatch-status.enum.js';
6
+ /**
7
+ * One outbound request Conduit has accepted responsibility for.
8
+ *
9
+ * The inbound pipeline reversed: a vertical POSTs here, Conduit persists and answers `202`, and
10
+ * the provider is called afterwards with retries and a dead-letter queue. The caller is released
11
+ * from the third party's availability entirely — which is the point, because a CRM that awaits
12
+ * the Graph API inline is a CRM that is down whenever Meta is.
13
+ *
14
+ * **The payload is stored as sent.** Conduit does not reshape it into a provider's format; §14
15
+ * rules out transforms beyond header templating, and a gateway that rewrites bodies is a gateway
16
+ * that has opinions about domains it must not know exist.
17
+ */
18
+ export interface Dispatch {
19
+ id: UUID;
20
+ /** The connection this went out through. A loose reference — it may since have been deleted. */
21
+ connectionId: UUID;
22
+ /** Snapshotted, so the record still reads correctly after the connection is renamed. */
23
+ connectionKey: string;
24
+ connectorKey: string;
25
+ /** The operation named at submit time. */
26
+ operation: string;
27
+ /**
28
+ * Resolved when the dispatch was accepted, not read from the manifest now.
29
+ *
30
+ * Same rule as a delivery's target: editing an operation's path must not retroactively
31
+ * change where Conduit says it sent something.
32
+ */
33
+ targetUrl: string;
34
+ method: string;
35
+ /**
36
+ * The caller's own key, echoed back.
37
+ *
38
+ * Submitting twice with the same key returns the **first** dispatch rather than creating a
39
+ * second — which is what makes a vertical's own retry safe. Null when the caller supplied
40
+ * none, in which case a resubmit is a second send and that is the caller's choice.
41
+ */
42
+ idempotencyKey: Nullable<string>;
43
+ status: DispatchStatus;
44
+ attemptCount: number;
45
+ /** Snapshotted from the connector's retry policy when accepted. */
46
+ maxAttempts: number;
47
+ nextAttemptAt: IsoDateTime;
48
+ lastAttemptAt: Nullable<IsoDateTime>;
49
+ /** Null when no response arrived. See `lastError`. */
50
+ lastResponseStatus: Nullable<number>;
51
+ lastResponseBodyPreview: Nullable<string>;
52
+ lastError: Nullable<string>;
53
+ /** Set when an operator redrove this. */
54
+ redrivenByUserId: Nullable<UUID>;
55
+ completedAt: Nullable<IsoDateTime>;
56
+ createdAt: IsoDateTime;
57
+ }
58
+ /**
59
+ * One attempt at one dispatch.
60
+ *
61
+ * `wasRateLimited` is the field that stops an operator misreading the history. A dispatch held
62
+ * back by the connector's own limit did not fail, cost no attempt, and needs nothing done about
63
+ * it — but without a record it looks like a gap in the timeline, and the obvious conclusion is
64
+ * that the queue stalled.
65
+ */
66
+ export interface DispatchAttempt {
67
+ id: UUID;
68
+ /** 1-based. Equal for two rows only when one of them was a rate-limit deferral. */
69
+ attemptNumber: number;
70
+ /** Null when no response arrived at all. */
71
+ responseStatus: Nullable<number>;
72
+ responseBodyPreview: Nullable<string>;
73
+ errorMessage: Nullable<string>;
74
+ durationMs: number;
75
+ isSuccess: boolean;
76
+ /** Recorded but not charged against the attempt budget. */
77
+ wasRateLimited: boolean;
78
+ /** Whether the access token was refreshed and the request re-sent within this attempt. */
79
+ didRefreshCredentials: boolean;
80
+ createdAt: IsoDateTime;
81
+ }
82
+ /** `GET /api/v1/dispatches/:id/attempts`. Oldest first — an attempt history reads as a story. */
83
+ export interface DispatchAttemptsList {
84
+ items: DispatchAttempt[];
85
+ }
86
+ /** `GET /api/v1/dispatches`. Cursor-paginated: the collection is appended to while it is read. */
87
+ export type DispatchesPage = CursorPage<Dispatch>;
88
+ /** Query for `GET /api/v1/dispatches`. */
89
+ export interface GetDispatchesQuery {
90
+ limit?: number;
91
+ /** Opaque cursor from the previous page's `nextCursor`. Never parse it. */
92
+ after?: Nullable<string>;
93
+ /** `dead-lettered` is the DLQ view. The DLQ is a filter, not a separate resource. */
94
+ status?: Nullable<DispatchStatus>;
95
+ connectionId?: Nullable<UUID>;
96
+ operation?: Nullable<string>;
97
+ }
98
+ /**
99
+ * `POST /api/v1/dispatches` — the request a vertical makes to send something.
100
+ *
101
+ * Note what is **not** here: no URL, no host, no arbitrary headers. The caller picks a connection
102
+ * and an operation, both of which someone configured deliberately, and Conduit derives the
103
+ * target. Accepting a URL would make this an authenticated open proxy sitting inside our network.
104
+ */
105
+ export interface SubmitDispatchBody {
106
+ /** Which connection to send through, by its tenant-scoped key. */
107
+ connectionKey: string;
108
+ /** Which of the connector's operations to invoke, by key. */
109
+ operation: string;
110
+ /** The request body, sent to the provider as-is. */
111
+ payload: Record<string, unknown>;
112
+ /**
113
+ * Values for `{placeholders}` in the operation's path that the connection does not supply.
114
+ *
115
+ * Percent-encoded into the path, so a value cannot introduce a path segment or a query
116
+ * string and steer the request at a different endpoint of the same host.
117
+ */
118
+ pathParams?: Nullable<Record<string, string>>;
119
+ /** Appended to the resolved URL. Values are encoded. */
120
+ query?: Nullable<Record<string, string>>;
121
+ /**
122
+ * The caller's idempotency key, scoped to the tenant.
123
+ *
124
+ * **Strongly recommended.** Without one, a vertical that times out submitting and retries
125
+ * sends the customer two messages. With one, the second submit returns the first dispatch.
126
+ */
127
+ idempotencyKey?: Nullable<string>;
128
+ }
129
+ /**
130
+ * `202 Accepted` from `POST /api/v1/dispatches`.
131
+ *
132
+ * Deliberately not the full {@link Dispatch}: at this moment the only true facts are that it was
133
+ * accepted and where to look next. Returning a status the caller might treat as an outcome
134
+ * invites exactly the synchronous coupling this endpoint exists to remove.
135
+ */
136
+ export interface SubmitDispatchResponse {
137
+ id: UUID;
138
+ status: DispatchStatus;
139
+ /** True when this submit matched an existing idempotency key and created nothing. */
140
+ isDuplicate: boolean;
141
+ createdAt: IsoDateTime;
142
+ }
143
+ //# sourceMappingURL=dispatch.type.d.ts.map