@iveri/contracts 0.6.0 → 0.8.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/README.md +30 -6
- package/dist/cjs/api/identity/access-token-payload.interface.d.ts +53 -0
- package/dist/cjs/api/identity/access-token-payload.interface.d.ts.map +1 -0
- package/dist/cjs/api/identity/access-token-payload.interface.js +3 -0
- package/dist/cjs/api/identity/access-token-payload.interface.js.map +1 -0
- package/dist/cjs/api/identity/auth.type.d.ts +21 -0
- package/dist/cjs/api/identity/auth.type.d.ts.map +1 -1
- package/dist/cjs/api/identity/index.d.ts +2 -0
- package/dist/cjs/api/identity/index.d.ts.map +1 -1
- package/dist/cjs/api/identity/index.js +2 -0
- package/dist/cjs/api/identity/index.js.map +1 -1
- package/dist/cjs/api/identity/principal-type.enum.d.ts +19 -0
- package/dist/cjs/api/identity/principal-type.enum.d.ts.map +1 -0
- package/dist/cjs/api/identity/principal-type.enum.js +23 -0
- package/dist/cjs/api/identity/principal-type.enum.js.map +1 -0
- package/dist/cjs/api/unibox/canned-reply.type.d.ts +49 -0
- package/dist/cjs/api/unibox/canned-reply.type.d.ts.map +1 -0
- package/dist/cjs/api/unibox/canned-reply.type.js +3 -0
- package/dist/cjs/api/unibox/canned-reply.type.js.map +1 -0
- package/dist/cjs/api/unibox/channel-platform.enum.d.ts +32 -0
- package/dist/cjs/api/unibox/channel-platform.enum.d.ts.map +1 -0
- package/dist/cjs/api/unibox/channel-platform.enum.js +36 -0
- package/dist/cjs/api/unibox/channel-platform.enum.js.map +1 -0
- package/dist/cjs/api/unibox/channel.type.d.ts +110 -0
- package/dist/cjs/api/unibox/channel.type.d.ts.map +1 -0
- package/dist/cjs/api/unibox/channel.type.js +3 -0
- package/dist/cjs/api/unibox/channel.type.js.map +1 -0
- package/dist/cjs/api/unibox/contact.type.d.ts +102 -0
- package/dist/cjs/api/unibox/contact.type.d.ts.map +1 -0
- package/dist/cjs/api/unibox/contact.type.js +3 -0
- package/dist/cjs/api/unibox/contact.type.js.map +1 -0
- package/dist/cjs/api/unibox/conversation-status.enum.d.ts +39 -0
- package/dist/cjs/api/unibox/conversation-status.enum.d.ts.map +1 -0
- package/dist/cjs/api/unibox/conversation-status.enum.js +43 -0
- package/dist/cjs/api/unibox/conversation-status.enum.js.map +1 -0
- package/dist/cjs/api/unibox/conversation.type.d.ts +134 -0
- package/dist/cjs/api/unibox/conversation.type.d.ts.map +1 -0
- package/dist/cjs/api/unibox/conversation.type.js +3 -0
- package/dist/cjs/api/unibox/conversation.type.js.map +1 -0
- package/dist/cjs/api/unibox/index.d.ts +13 -0
- package/dist/cjs/api/unibox/index.d.ts.map +1 -0
- package/dist/cjs/api/unibox/index.js +29 -0
- package/dist/cjs/api/unibox/index.js.map +1 -0
- package/dist/cjs/api/unibox/message-author-type.enum.d.ts +26 -0
- package/dist/cjs/api/unibox/message-author-type.enum.d.ts.map +1 -0
- package/dist/cjs/api/unibox/message-author-type.enum.js +30 -0
- package/dist/cjs/api/unibox/message-author-type.enum.js.map +1 -0
- package/dist/cjs/api/unibox/message-content-type.enum.d.ts +29 -0
- package/dist/cjs/api/unibox/message-content-type.enum.d.ts.map +1 -0
- package/dist/cjs/api/unibox/message-content-type.enum.js +33 -0
- package/dist/cjs/api/unibox/message-content-type.enum.js.map +1 -0
- package/dist/cjs/api/unibox/message-direction.enum.d.ts +23 -0
- package/dist/cjs/api/unibox/message-direction.enum.d.ts.map +1 -0
- package/dist/cjs/api/unibox/message-direction.enum.js +27 -0
- package/dist/cjs/api/unibox/message-direction.enum.js.map +1 -0
- package/dist/cjs/api/unibox/message-status.enum.d.ts +40 -0
- package/dist/cjs/api/unibox/message-status.enum.d.ts.map +1 -0
- package/dist/cjs/api/unibox/message-status.enum.js +44 -0
- package/dist/cjs/api/unibox/message-status.enum.js.map +1 -0
- package/dist/cjs/api/unibox/message.type.d.ts +120 -0
- package/dist/cjs/api/unibox/message.type.d.ts.map +1 -0
- package/dist/cjs/api/unibox/message.type.js +3 -0
- package/dist/cjs/api/unibox/message.type.js.map +1 -0
- package/dist/cjs/api/unibox/team.type.d.ts +50 -0
- package/dist/cjs/api/unibox/team.type.d.ts.map +1 -0
- package/dist/cjs/api/unibox/team.type.js +3 -0
- package/dist/cjs/api/unibox/team.type.js.map +1 -0
- package/dist/cjs/enum/user-permission.enum.d.ts +48 -1
- package/dist/cjs/enum/user-permission.enum.d.ts.map +1 -1
- package/dist/cjs/enum/user-permission.enum.js +54 -0
- package/dist/cjs/enum/user-permission.enum.js.map +1 -1
- package/dist/esm/api/identity/access-token-payload.interface.d.ts +53 -0
- package/dist/esm/api/identity/access-token-payload.interface.d.ts.map +1 -0
- package/dist/esm/api/identity/access-token-payload.interface.js +2 -0
- package/dist/esm/api/identity/access-token-payload.interface.js.map +1 -0
- package/dist/esm/api/identity/auth.type.d.ts +21 -0
- package/dist/esm/api/identity/auth.type.d.ts.map +1 -1
- package/dist/esm/api/identity/index.d.ts +2 -0
- package/dist/esm/api/identity/index.d.ts.map +1 -1
- package/dist/esm/api/identity/index.js +2 -0
- package/dist/esm/api/identity/index.js.map +1 -1
- package/dist/esm/api/identity/principal-type.enum.d.ts +19 -0
- package/dist/esm/api/identity/principal-type.enum.d.ts.map +1 -0
- package/dist/esm/api/identity/principal-type.enum.js +20 -0
- package/dist/esm/api/identity/principal-type.enum.js.map +1 -0
- package/dist/esm/api/unibox/canned-reply.type.d.ts +49 -0
- package/dist/esm/api/unibox/canned-reply.type.d.ts.map +1 -0
- package/dist/esm/api/unibox/canned-reply.type.js +2 -0
- package/dist/esm/api/unibox/canned-reply.type.js.map +1 -0
- package/dist/esm/api/unibox/channel-platform.enum.d.ts +32 -0
- package/dist/esm/api/unibox/channel-platform.enum.d.ts.map +1 -0
- package/dist/esm/api/unibox/channel-platform.enum.js +33 -0
- package/dist/esm/api/unibox/channel-platform.enum.js.map +1 -0
- package/dist/esm/api/unibox/channel.type.d.ts +110 -0
- package/dist/esm/api/unibox/channel.type.d.ts.map +1 -0
- package/dist/esm/api/unibox/channel.type.js +2 -0
- package/dist/esm/api/unibox/channel.type.js.map +1 -0
- package/dist/esm/api/unibox/contact.type.d.ts +102 -0
- package/dist/esm/api/unibox/contact.type.d.ts.map +1 -0
- package/dist/esm/api/unibox/contact.type.js +2 -0
- package/dist/esm/api/unibox/contact.type.js.map +1 -0
- package/dist/esm/api/unibox/conversation-status.enum.d.ts +39 -0
- package/dist/esm/api/unibox/conversation-status.enum.d.ts.map +1 -0
- package/dist/esm/api/unibox/conversation-status.enum.js +40 -0
- package/dist/esm/api/unibox/conversation-status.enum.js.map +1 -0
- package/dist/esm/api/unibox/conversation.type.d.ts +134 -0
- package/dist/esm/api/unibox/conversation.type.d.ts.map +1 -0
- package/dist/esm/api/unibox/conversation.type.js +2 -0
- package/dist/esm/api/unibox/conversation.type.js.map +1 -0
- package/dist/esm/api/unibox/index.d.ts +13 -0
- package/dist/esm/api/unibox/index.d.ts.map +1 -0
- package/dist/esm/api/unibox/index.js +13 -0
- package/dist/esm/api/unibox/index.js.map +1 -0
- package/dist/esm/api/unibox/message-author-type.enum.d.ts +26 -0
- package/dist/esm/api/unibox/message-author-type.enum.d.ts.map +1 -0
- package/dist/esm/api/unibox/message-author-type.enum.js +27 -0
- package/dist/esm/api/unibox/message-author-type.enum.js.map +1 -0
- package/dist/esm/api/unibox/message-content-type.enum.d.ts +29 -0
- package/dist/esm/api/unibox/message-content-type.enum.d.ts.map +1 -0
- package/dist/esm/api/unibox/message-content-type.enum.js +30 -0
- package/dist/esm/api/unibox/message-content-type.enum.js.map +1 -0
- package/dist/esm/api/unibox/message-direction.enum.d.ts +23 -0
- package/dist/esm/api/unibox/message-direction.enum.d.ts.map +1 -0
- package/dist/esm/api/unibox/message-direction.enum.js +24 -0
- package/dist/esm/api/unibox/message-direction.enum.js.map +1 -0
- package/dist/esm/api/unibox/message-status.enum.d.ts +40 -0
- package/dist/esm/api/unibox/message-status.enum.d.ts.map +1 -0
- package/dist/esm/api/unibox/message-status.enum.js +41 -0
- package/dist/esm/api/unibox/message-status.enum.js.map +1 -0
- package/dist/esm/api/unibox/message.type.d.ts +120 -0
- package/dist/esm/api/unibox/message.type.d.ts.map +1 -0
- package/dist/esm/api/unibox/message.type.js +2 -0
- package/dist/esm/api/unibox/message.type.js.map +1 -0
- package/dist/esm/api/unibox/team.type.d.ts +50 -0
- package/dist/esm/api/unibox/team.type.d.ts.map +1 -0
- package/dist/esm/api/unibox/team.type.js +2 -0
- package/dist/esm/api/unibox/team.type.js.map +1 -0
- package/dist/esm/enum/user-permission.enum.d.ts +48 -1
- package/dist/esm/enum/user-permission.enum.d.ts.map +1 -1
- package/dist/esm/enum/user-permission.enum.js +54 -0
- package/dist/esm/enum/user-permission.enum.js.map +1 -1
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -14,14 +14,16 @@ import { CursorPage, ErrorCode, Maybe, Paginated, UUID, UserPermission } from '@
|
|
|
14
14
|
// One service's wire surface.
|
|
15
15
|
import type { CaptureSummary, Endpoint } from '@iveri/contracts/conduit';
|
|
16
16
|
import type { AuthSession, Principal } from '@iveri/contracts/identity';
|
|
17
|
+
import type { Conversation, Message } from '@iveri/contracts/unibox';
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
Four entry points, and no others — never `@iveri/contracts/dist/...`.
|
|
20
21
|
|
|
21
22
|
The per-service surfaces are **not** re-exported from the root, deliberately. Conduit owns a
|
|
22
|
-
`Provider` and an `Endpoint`; Unibox
|
|
23
|
-
|
|
24
|
-
renaming a type in a shipped API
|
|
23
|
+
`Provider`, a `Connection` and an `Endpoint`; Unibox owns a `Channel` and a `Contact` and would
|
|
24
|
+
have wanted two of those names for entirely different things. Keeping each service behind its own
|
|
25
|
+
entry point means that collision never has to be resolved by renaming a type in a shipped API —
|
|
26
|
+
and it stopped being hypothetical the moment `unibox` was added.
|
|
25
27
|
|
|
26
28
|
## Zero runtime dependencies — the rule that defines this package
|
|
27
29
|
|
|
@@ -69,8 +71,14 @@ catalogue and are already embedded in issued tokens and stored role rows.
|
|
|
69
71
|
### `api/identity/` → `@iveri/contracts/identity`
|
|
70
72
|
|
|
71
73
|
`iveri-identity-api`'s authentication surface: `SignupBody`, `LoginBody`, `RefreshTokenBody`,
|
|
72
|
-
`AcceptInvitationBody`, `AuthTokens`, `Principal`, `AuthSession`, plus
|
|
73
|
-
`MembershipStatus`.
|
|
74
|
+
`AcceptInvitationBody`, `AuthTokens`, `ServiceToken`, `Principal`, `AuthSession`, plus
|
|
75
|
+
`TenantStatus`, `MembershipStatus` and `PrincipalType`.
|
|
76
|
+
|
|
77
|
+
It also holds **`AccessTokenPayload`** — the claims inside the JWT itself, rather than a request
|
|
78
|
+
or response body. It belongs here for a stronger reason than the rest: identity is the only
|
|
79
|
+
service that _issues_ a token and every other service _verifies_ one, so both sides must agree
|
|
80
|
+
claim for claim. `conduit-api` kept a hand-copied duplicate until the `pt` claim had to be added
|
|
81
|
+
to both at once — the drift a copy invites, arriving exactly when it is most expensive.
|
|
74
82
|
|
|
75
83
|
Auth only. Identity also exposes tenant, user, membership, role and API-key routes; those have
|
|
76
84
|
one consumer each and stay in the service until a second one appears.
|
|
@@ -89,6 +97,22 @@ Plus the request bodies for each, and the seven enums they are built from — `S
|
|
|
89
97
|
`SignatureAlgorithm`, `SignatureEncoding`, `SignatureHeaderScheme`, `HandshakeKind`,
|
|
90
98
|
`MatchOperator`, `DeliveryStatus`.
|
|
91
99
|
|
|
100
|
+
### `api/unibox/` → `@iveri/contracts/unibox`
|
|
101
|
+
|
|
102
|
+
`unibox-api`'s surface: `Channel` and its `ChannelIngestSecretResponse`, `Contact` with its
|
|
103
|
+
`ContactIdentity`, `Conversation`/`ConversationWithMessages`, `Message` with `MessageAttachment`,
|
|
104
|
+
`Team`, `CannedReply`, plus the request bodies and six enums — `ChannelPlatform`,
|
|
105
|
+
`ConversationStatus`, `MessageDirection`, `MessageAuthorType`, `MessageStatus`,
|
|
106
|
+
`MessageContentType`.
|
|
107
|
+
|
|
108
|
+
Two of those enums carry members nothing writes yet, and both are deliberate rather than
|
|
109
|
+
speculative. `MessageAuthorType.AI` exists before `unibox-ai` does, because it is what an agent
|
|
110
|
+
taking over reads to know what the customer was already told — adding it later would leave every
|
|
111
|
+
message written before then indistinguishable from a human's, retroactively. `ChannelPlatform.ECHO`
|
|
112
|
+
is a first-class member for the same reason Conduit's `local-echo` connector is: the whole pipeline
|
|
113
|
+
is provable without a Meta app, and a test double would put a branch in the normalizer registry
|
|
114
|
+
that only exists under Jest.
|
|
115
|
+
|
|
92
116
|
### Why the wire shapes moved here
|
|
93
117
|
|
|
94
118
|
`conduit-admin-web` hand-transcribed all of it, carefully and with comments. It still drifted:
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { UserPermission } from '../../enum/user-permission.enum.js';
|
|
2
|
+
import type { UUID } from '../../type/uuid.type.js';
|
|
3
|
+
import type { PrincipalType } from './principal-type.enum.js';
|
|
4
|
+
/**
|
|
5
|
+
* Claims carried by an access token.
|
|
6
|
+
*
|
|
7
|
+
* **`iveri-identity-api` issues these; every other service only verifies them.** The shape is
|
|
8
|
+
* therefore a contract owned by identity, and it lives here rather than in either repo because
|
|
9
|
+
* both sides have to agree byte for byte — a service whose idea of these claims has drifted
|
|
10
|
+
* does not fail loudly, it rejects every request or, worse, reads the wrong field and keeps
|
|
11
|
+
* going. `conduit-api` carried a hand-copied duplicate of this interface until the `pt` claim
|
|
12
|
+
* had to be added to both at once, which is exactly the drift the copy invited.
|
|
13
|
+
*
|
|
14
|
+
* Names are short and non-obvious (`tid`, `perms`) because this is JWT convention and because
|
|
15
|
+
* the token travels on every request — the registered claims are three characters for the same
|
|
16
|
+
* reason.
|
|
17
|
+
*
|
|
18
|
+
* **Permissions are embedded rather than resolved per request.** That is the whole point of a
|
|
19
|
+
* JWT here: any Iveri service verifies a token with the shared secret and never calls identity
|
|
20
|
+
* on the hot path. The cost is staleness — revoking a role does not invalidate access tokens
|
|
21
|
+
* already issued, so a removed permission survives up to identity's `JWT_ACCESS_TOKEN_TTL`
|
|
22
|
+
* (15 minutes by default), or `JWT_SERVICE_TOKEN_TTL` for a service token. Anything needing
|
|
23
|
+
* immediate revocation must check state at the point of use, not rely on the token.
|
|
24
|
+
*/
|
|
25
|
+
export interface AccessTokenPayload {
|
|
26
|
+
/**
|
|
27
|
+
* Subject — **the principal**, which is a user id or an API key id depending on {@link pt}.
|
|
28
|
+
*
|
|
29
|
+
* Read it together with `pt` or not at all. Treating it as a user id unconditionally is the
|
|
30
|
+
* one mistake this shape is arranged to prevent.
|
|
31
|
+
*/
|
|
32
|
+
sub: UUID;
|
|
33
|
+
/**
|
|
34
|
+
* Which kind of principal `sub` names.
|
|
35
|
+
*
|
|
36
|
+
* Required, so adding the service-token path could not silently leave a consumer reading a
|
|
37
|
+
* machine's `sub` as a person's id.
|
|
38
|
+
*/
|
|
39
|
+
pt: PrincipalType;
|
|
40
|
+
/** Tenant the token is scoped to. Becomes `RequestContext.tenantId`; never trusted from a header. */
|
|
41
|
+
tid: UUID;
|
|
42
|
+
/** Permissions resolved from the principal's roles — or from the API key's grant — at issue time. */
|
|
43
|
+
perms: UserPermission[];
|
|
44
|
+
/** Issued-at, seconds since epoch. Set by the signer. */
|
|
45
|
+
iat?: number;
|
|
46
|
+
/** Expiry, seconds since epoch. Set by the signer. */
|
|
47
|
+
exp?: number;
|
|
48
|
+
/** Issuer, verified on every decode so a token from another Iveri environment is rejected. */
|
|
49
|
+
iss?: string;
|
|
50
|
+
/** Audience, verified alongside `iss`. */
|
|
51
|
+
aud?: string;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=access-token-payload.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token-payload.interface.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/access-token-payload.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,IAAI,CAAC;IAEV;;;;;OAKG;IACH,EAAE,EAAE,aAAa,CAAC;IAElB,qGAAqG;IACrG,GAAG,EAAE,IAAI,CAAC;IAEV,qGAAqG;IACrG,KAAK,EAAE,cAAc,EAAE,CAAC;IAExB,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token-payload.interface.js","sourceRoot":"","sources":["../../../../src/api/identity/access-token-payload.interface.ts"],"names":[],"mappings":""}
|
|
@@ -51,6 +51,27 @@ export interface AuthTokens {
|
|
|
51
51
|
/** Lifetime of `accessToken` in seconds, not of `refreshToken`. */
|
|
52
52
|
expiresInSeconds: number;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returned by `POST /auth/token` — the API-key-for-JWT exchange a service makes so that other
|
|
56
|
+
* Iveri services can authorize it **without calling identity**.
|
|
57
|
+
*
|
|
58
|
+
* **There is deliberately no refresh token.** The API key already is the long-lived credential,
|
|
59
|
+
* and it is revocable by a row. A refresh token here would be a second long-lived credential
|
|
60
|
+
* with its own independent lifetime, rotation rule and leak story, obtained from the first —
|
|
61
|
+
* so revoking the key would no longer be enough to stop the caller. A service re-exchanges
|
|
62
|
+
* instead, which costs one call per {@link expiresInSeconds}.
|
|
63
|
+
*
|
|
64
|
+
* The key travels as `x-api-key`, so this call has no request body.
|
|
65
|
+
*/
|
|
66
|
+
export interface ServiceToken {
|
|
67
|
+
/** Send as `Authorization: Bearer <accessToken>`. Carries `pt: 'service'`. */
|
|
68
|
+
accessToken: string;
|
|
69
|
+
/**
|
|
70
|
+
* Lifetime in seconds. Shorter than a human session's: a service re-exchanges with nobody
|
|
71
|
+
* waiting, so the revocation window is cheap to shrink.
|
|
72
|
+
*/
|
|
73
|
+
expiresInSeconds: number;
|
|
74
|
+
}
|
|
54
75
|
/**
|
|
55
76
|
* Who the caller is. Returned by `GET /auth/me` and nested inside {@link AuthSession}.
|
|
56
77
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.type.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/auth.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACvB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAE3B,qCAAqC;IACrC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,gEAAgE;IAChE,WAAW,EAAE,cAAc,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACtB"}
|
|
1
|
+
{"version":3,"file":"auth.type.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/auth.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACvB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,YAAY;IACzB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAE3B,qCAAqC;IACrC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,gEAAgE;IAChE,WAAW,EAAE,cAAc,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC"}
|
|
@@ -14,7 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./access-token-payload.interface.js"), exports);
|
|
17
18
|
__exportStar(require("./auth.type.js"), exports);
|
|
18
19
|
__exportStar(require("./membership-status.enum.js"), exports);
|
|
20
|
+
__exportStar(require("./principal-type.enum.js"), exports);
|
|
19
21
|
__exportStar(require("./tenant-status.enum.js"), exports);
|
|
20
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B;AAC/B,8DAA4C;AAC5C,0DAAwC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sEAAoD;AACpD,iDAA+B;AAC/B,8DAA4C;AAC5C,2DAAyC;AACzC,0DAAwC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of principal an access token was minted for.
|
|
3
|
+
*
|
|
4
|
+
* It exists because `sub` alone cannot answer the question. A JWT's subject is *the principal*,
|
|
5
|
+
* and identity issues tokens for two kinds of them: a person who logged in, and a service that
|
|
6
|
+
* exchanged an API key. Both put a UUID in `sub`, and the two UUIDs mean entirely different
|
|
7
|
+
* things — one names a row in `user`, the other a row in `api_key`.
|
|
8
|
+
*
|
|
9
|
+
* A consumer that reads `sub` as a user id without checking this claim attributes a machine's
|
|
10
|
+
* actions to a user who does not exist. That is why every guard branches on it, and why it is
|
|
11
|
+
* required rather than optional: `tsc` then makes the branch impossible to forget.
|
|
12
|
+
*/
|
|
13
|
+
export declare enum PrincipalType {
|
|
14
|
+
/** A person, authenticated by password. `sub` is their `user.id`. */
|
|
15
|
+
USER = "user",
|
|
16
|
+
/** A service, authenticated by API key. `sub` is the `api_key.id`, and there is no user. */
|
|
17
|
+
SERVICE = "service"
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=principal-type.enum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal-type.enum.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/principal-type.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,oBAAY,aAAa;IACrB,qEAAqE;IACrE,IAAI,SAAS;IAEb,4FAA4F;IAC5F,OAAO,YAAY;CACtB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrincipalType = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* What kind of principal an access token was minted for.
|
|
6
|
+
*
|
|
7
|
+
* It exists because `sub` alone cannot answer the question. A JWT's subject is *the principal*,
|
|
8
|
+
* and identity issues tokens for two kinds of them: a person who logged in, and a service that
|
|
9
|
+
* exchanged an API key. Both put a UUID in `sub`, and the two UUIDs mean entirely different
|
|
10
|
+
* things — one names a row in `user`, the other a row in `api_key`.
|
|
11
|
+
*
|
|
12
|
+
* A consumer that reads `sub` as a user id without checking this claim attributes a machine's
|
|
13
|
+
* actions to a user who does not exist. That is why every guard branches on it, and why it is
|
|
14
|
+
* required rather than optional: `tsc` then makes the branch impossible to forget.
|
|
15
|
+
*/
|
|
16
|
+
var PrincipalType;
|
|
17
|
+
(function (PrincipalType) {
|
|
18
|
+
/** A person, authenticated by password. `sub` is their `user.id`. */
|
|
19
|
+
PrincipalType["USER"] = "user";
|
|
20
|
+
/** A service, authenticated by API key. `sub` is the `api_key.id`, and there is no user. */
|
|
21
|
+
PrincipalType["SERVICE"] = "service";
|
|
22
|
+
})(PrincipalType || (exports.PrincipalType = PrincipalType = {}));
|
|
23
|
+
//# sourceMappingURL=principal-type.enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal-type.enum.js","sourceRoot":"","sources":["../../../../src/api/identity/principal-type.enum.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,IAAY,aAMX;AAND,WAAY,aAAa;IACrB,qEAAqE;IACrE,8BAAa,CAAA;IAEb,4FAA4F;IAC5F,oCAAmB,CAAA;AACvB,CAAC,EANW,aAAa,6BAAb,aAAa,QAMxB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { IsoDateTime } from '../../type/iso-date-time.type.js';
|
|
2
|
+
import type { Nullable } from '../../type/nil.type.js';
|
|
3
|
+
import type { Paginated } from '../../type/paginated.type.js';
|
|
4
|
+
import type { UUID } from '../../type/uuid.type.js';
|
|
5
|
+
/**
|
|
6
|
+
* A saved reply an agent can insert into a message.
|
|
7
|
+
*
|
|
8
|
+
* **Inserted into the composer, never sent by itself.** A canned reply is text an agent chooses
|
|
9
|
+
* and can edit before sending — it is not an automation, and there is no endpoint that sends one
|
|
10
|
+
* directly. That line is what keeps this out of §14's no-scripting territory: the moment a saved
|
|
11
|
+
* reply can fire on a condition, we have built a rules engine in a chat product.
|
|
12
|
+
*/
|
|
13
|
+
export interface CannedReply {
|
|
14
|
+
id: UUID;
|
|
15
|
+
/** What an agent types to find it, e.g. `/hours`. Unique within the tenant. */
|
|
16
|
+
shortcut: string;
|
|
17
|
+
title: string;
|
|
18
|
+
/**
|
|
19
|
+
* The text, with `{{placeholders}}` substituted client-side at insert time.
|
|
20
|
+
*
|
|
21
|
+
* Substitution is the client's job on purpose. The server would have to resolve them at send
|
|
22
|
+
* time, which means an agent could send a message whose final text they never saw — and the
|
|
23
|
+
* one thing an agent must always be able to do is read what they are about to send.
|
|
24
|
+
*/
|
|
25
|
+
body: string;
|
|
26
|
+
/** For grouping in the picker. Null is the ungrouped bucket. */
|
|
27
|
+
category: Nullable<string>;
|
|
28
|
+
createdByUserId: Nullable<UUID>;
|
|
29
|
+
createdAt: IsoDateTime;
|
|
30
|
+
updatedAt: IsoDateTime;
|
|
31
|
+
}
|
|
32
|
+
/** `GET /api/v1/canned-replies`. Offset-paged. */
|
|
33
|
+
export type CannedRepliesPage = Paginated<CannedReply>;
|
|
34
|
+
/** `POST /api/v1/canned-replies`. */
|
|
35
|
+
export interface CreateCannedReplyBody {
|
|
36
|
+
/** Lowercase, `[a-z0-9-]`, no leading slash — the UI supplies that. */
|
|
37
|
+
shortcut: string;
|
|
38
|
+
title: string;
|
|
39
|
+
body: string;
|
|
40
|
+
category?: Nullable<string>;
|
|
41
|
+
}
|
|
42
|
+
/** `PATCH /api/v1/canned-replies/:id`. */
|
|
43
|
+
export interface UpdateCannedReplyBody {
|
|
44
|
+
shortcut?: string;
|
|
45
|
+
title?: string;
|
|
46
|
+
body?: string;
|
|
47
|
+
category?: Nullable<string>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=canned-reply.type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canned-reply.type.d.ts","sourceRoot":"","sources":["../../../../src/api/unibox/canned-reply.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,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,IAAI,CAAC;IAET,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IAEjB,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb,gEAAgE;IAChE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEhC,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;CAC1B;AAED,kDAAkD;AAClD,MAAM,MAAM,iBAAiB,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;AAEvD,qCAAqC;AACrC,MAAM,WAAW,qBAAqB;IAClC,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canned-reply.type.js","sourceRoot":"","sources":["../../../../src/api/unibox/canned-reply.type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The messaging network a channel is connected to.
|
|
3
|
+
*
|
|
4
|
+
* This is Unibox's vocabulary, not Conduit's. Conduit knows about *connectors* — a set of hosts
|
|
5
|
+
* and operations someone wrote down — and has no idea that any of them carry conversations.
|
|
6
|
+
* Unibox knows that a WhatsApp message and an Instagram message are the same kind of thing and
|
|
7
|
+
* belong in one inbox, which is the whole product.
|
|
8
|
+
*
|
|
9
|
+
* The members are the platforms Unibox can **normalize**, which is a stricter bar than the ones
|
|
10
|
+
* Conduit can talk to: normalizing means we can read a provider's webhook into a contact, a
|
|
11
|
+
* conversation and a message, and write a reply back. Adding one is a normalizer plus a
|
|
12
|
+
* connector manifest, and is a minor version here.
|
|
13
|
+
*/
|
|
14
|
+
export declare enum ChannelPlatform {
|
|
15
|
+
/** WhatsApp Cloud API. The first real platform, and the one `conduit-api` ships a manifest for. */
|
|
16
|
+
WHATSAPP = "whatsapp",
|
|
17
|
+
/** Instagram Direct, via the Messenger Platform. */
|
|
18
|
+
INSTAGRAM = "instagram",
|
|
19
|
+
/** Facebook Messenger. */
|
|
20
|
+
MESSENGER = "messenger",
|
|
21
|
+
/**
|
|
22
|
+
* A loopback platform for local development and tests, matching Conduit's `local-echo`
|
|
23
|
+
* connector.
|
|
24
|
+
*
|
|
25
|
+
* It exists for the same reason that connector does: the entire pipeline — receive,
|
|
26
|
+
* normalize, thread, reply, dispatch — is provable without a Meta app, and a real Meta app
|
|
27
|
+
* would only prove we can talk to Meta. It is a first-class member rather than a test double
|
|
28
|
+
* so the normalizer registry has no branch that only exists under Jest.
|
|
29
|
+
*/
|
|
30
|
+
ECHO = "echo"
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=channel-platform.enum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-platform.enum.d.ts","sourceRoot":"","sources":["../../../../src/api/unibox/channel-platform.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,oBAAY,eAAe;IACvB,mGAAmG;IACnG,QAAQ,aAAa;IAErB,oDAAoD;IACpD,SAAS,cAAc;IAEvB,0BAA0B;IAC1B,SAAS,cAAc;IAEvB;;;;;;;;OAQG;IACH,IAAI,SAAS;CAChB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChannelPlatform = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The messaging network a channel is connected to.
|
|
6
|
+
*
|
|
7
|
+
* This is Unibox's vocabulary, not Conduit's. Conduit knows about *connectors* — a set of hosts
|
|
8
|
+
* and operations someone wrote down — and has no idea that any of them carry conversations.
|
|
9
|
+
* Unibox knows that a WhatsApp message and an Instagram message are the same kind of thing and
|
|
10
|
+
* belong in one inbox, which is the whole product.
|
|
11
|
+
*
|
|
12
|
+
* The members are the platforms Unibox can **normalize**, which is a stricter bar than the ones
|
|
13
|
+
* Conduit can talk to: normalizing means we can read a provider's webhook into a contact, a
|
|
14
|
+
* conversation and a message, and write a reply back. Adding one is a normalizer plus a
|
|
15
|
+
* connector manifest, and is a minor version here.
|
|
16
|
+
*/
|
|
17
|
+
var ChannelPlatform;
|
|
18
|
+
(function (ChannelPlatform) {
|
|
19
|
+
/** WhatsApp Cloud API. The first real platform, and the one `conduit-api` ships a manifest for. */
|
|
20
|
+
ChannelPlatform["WHATSAPP"] = "whatsapp";
|
|
21
|
+
/** Instagram Direct, via the Messenger Platform. */
|
|
22
|
+
ChannelPlatform["INSTAGRAM"] = "instagram";
|
|
23
|
+
/** Facebook Messenger. */
|
|
24
|
+
ChannelPlatform["MESSENGER"] = "messenger";
|
|
25
|
+
/**
|
|
26
|
+
* A loopback platform for local development and tests, matching Conduit's `local-echo`
|
|
27
|
+
* connector.
|
|
28
|
+
*
|
|
29
|
+
* It exists for the same reason that connector does: the entire pipeline — receive,
|
|
30
|
+
* normalize, thread, reply, dispatch — is provable without a Meta app, and a real Meta app
|
|
31
|
+
* would only prove we can talk to Meta. It is a first-class member rather than a test double
|
|
32
|
+
* so the normalizer registry has no branch that only exists under Jest.
|
|
33
|
+
*/
|
|
34
|
+
ChannelPlatform["ECHO"] = "echo";
|
|
35
|
+
})(ChannelPlatform || (exports.ChannelPlatform = ChannelPlatform = {}));
|
|
36
|
+
//# sourceMappingURL=channel-platform.enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-platform.enum.js","sourceRoot":"","sources":["../../../../src/api/unibox/channel-platform.enum.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,IAAY,eAoBX;AApBD,WAAY,eAAe;IACvB,mGAAmG;IACnG,wCAAqB,CAAA;IAErB,oDAAoD;IACpD,0CAAuB,CAAA;IAEvB,0BAA0B;IAC1B,0CAAuB,CAAA;IAEvB;;;;;;;;OAQG;IACH,gCAAa,CAAA;AACjB,CAAC,EApBW,eAAe,+BAAf,eAAe,QAoB1B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { IsoDateTime } from '../../type/iso-date-time.type.js';
|
|
2
|
+
import type { Nullable } from '../../type/nil.type.js';
|
|
3
|
+
import type { Paginated } from '../../type/paginated.type.js';
|
|
4
|
+
import type { UUID } from '../../type/uuid.type.js';
|
|
5
|
+
import type { ChannelPlatform } from './channel-platform.enum.js';
|
|
6
|
+
/**
|
|
7
|
+
* One messaging account a tenant has connected — a WhatsApp number, an Instagram business
|
|
8
|
+
* account, a Facebook page.
|
|
9
|
+
*
|
|
10
|
+
* A channel is the seam between Unibox and Conduit, and it points at Conduit twice, in opposite
|
|
11
|
+
* directions:
|
|
12
|
+
*
|
|
13
|
+
* - **Inbound**, Conduit forwards a provider's raw webhook to Unibox and presents this channel's
|
|
14
|
+
* ingest secret. Conduit does not parse it — it cannot, because §11 forbids it knowing what a
|
|
15
|
+
* chat is — so normalizing the bytes into a message is Unibox's job.
|
|
16
|
+
* - **Outbound**, a reply is submitted to Conduit naming `connectionKey` and `sendOperation`.
|
|
17
|
+
* Unibox never calls the Graph API itself (§12).
|
|
18
|
+
*
|
|
19
|
+
* Neither reference is a foreign key, because they name rows in another service's database. A
|
|
20
|
+
* `connectionKey` that no longer resolves is a misconfiguration Unibox reports; it is not
|
|
21
|
+
* something Unibox can prevent, and pretending otherwise would mean Unibox reading Conduit's
|
|
22
|
+
* tables, which is exactly what §2 forbids.
|
|
23
|
+
*/
|
|
24
|
+
export interface Channel {
|
|
25
|
+
id: UUID;
|
|
26
|
+
/** Tenant-scoped, stable, and how a normalizer is chosen for inbound traffic. */
|
|
27
|
+
key: string;
|
|
28
|
+
name: string;
|
|
29
|
+
platform: ChannelPlatform;
|
|
30
|
+
/**
|
|
31
|
+
* The account's own identifier at the provider — a WhatsApp phone-number id, an Instagram
|
|
32
|
+
* account id.
|
|
33
|
+
*
|
|
34
|
+
* Stored because a provider's webhook names the *recipient* account, and a tenant may have
|
|
35
|
+
* several channels on one platform. Without it there is no way to tell which of two WhatsApp
|
|
36
|
+
* numbers a message arrived at, and both would look equally plausible.
|
|
37
|
+
*/
|
|
38
|
+
externalAccountId: string;
|
|
39
|
+
/** Conduit connection a reply is dispatched through. Loose reference into another service. */
|
|
40
|
+
connectionKey: string;
|
|
41
|
+
/** Which of that connector's operations sends a message. */
|
|
42
|
+
sendOperation: string;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this channel accepts inbound traffic and sends replies.
|
|
45
|
+
*
|
|
46
|
+
* Turning it off stops new conversations and refuses sends. It does **not** delete anything
|
|
47
|
+
* already received: a channel a customer disconnected still has a history someone needs to
|
|
48
|
+
* read.
|
|
49
|
+
*/
|
|
50
|
+
isActive: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Whether an ingest secret is currently set.
|
|
53
|
+
*
|
|
54
|
+
* A boolean, never the value. The secret is what authenticates Conduit to Unibox, so it is
|
|
55
|
+
* envelope-encrypted, returned by no endpoint, and shown to the operator exactly once — at
|
|
56
|
+
* the moment it is generated, when it also has to be pasted into Conduit's destination
|
|
57
|
+
* headers. Same control as an endpoint's signing secret in Conduit.
|
|
58
|
+
*/
|
|
59
|
+
hasIngestSecret: boolean;
|
|
60
|
+
/** When a message was last received or sent here. Null until the first one. */
|
|
61
|
+
lastActivityAt: Nullable<IsoDateTime>;
|
|
62
|
+
createdAt: IsoDateTime;
|
|
63
|
+
updatedAt: IsoDateTime;
|
|
64
|
+
}
|
|
65
|
+
/** `GET /api/v1/channels`. Offset-paged: a tenant has a handful of channels, not a stream of them. */
|
|
66
|
+
export type ChannelsPage = Paginated<Channel>;
|
|
67
|
+
/** `POST /api/v1/channels`. */
|
|
68
|
+
export interface CreateChannelBody {
|
|
69
|
+
/** Lowercase, `[a-z0-9-]`. Unique within the tenant and immutable once created. */
|
|
70
|
+
key: string;
|
|
71
|
+
name: string;
|
|
72
|
+
platform: ChannelPlatform;
|
|
73
|
+
externalAccountId: string;
|
|
74
|
+
connectionKey: string;
|
|
75
|
+
/** Defaults to the platform's conventional send operation when omitted. */
|
|
76
|
+
sendOperation?: Nullable<string>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* `PATCH /api/v1/channels/:id`.
|
|
80
|
+
*
|
|
81
|
+
* `key` and `platform` are absent on purpose. The key is what an ingest request resolves and what
|
|
82
|
+
* an operator wrote into Conduit's configuration; the platform decides which normalizer reads the
|
|
83
|
+
* history already stored against this channel. Changing either would silently reinterpret rows
|
|
84
|
+
* that are already there, so both are create-only — delete and recreate is the honest path.
|
|
85
|
+
*/
|
|
86
|
+
export interface UpdateChannelBody {
|
|
87
|
+
name?: string;
|
|
88
|
+
externalAccountId?: string;
|
|
89
|
+
connectionKey?: string;
|
|
90
|
+
sendOperation?: string;
|
|
91
|
+
isActive?: boolean;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `POST /api/v1/channels/:id/ingest-secret` — the **only** response that ever carries the secret.
|
|
95
|
+
*
|
|
96
|
+
* Rotating replaces the stored value immediately, so the previous secret stops working the moment
|
|
97
|
+
* this returns. That is deliberate and it is a brief outage by design: an ingest secret that
|
|
98
|
+
* overlapped with its replacement would mean a leaked one keeps working for as long as the
|
|
99
|
+
* overlap, which is the thing rotation exists to end.
|
|
100
|
+
*/
|
|
101
|
+
export interface ChannelIngestSecretResponse {
|
|
102
|
+
channelId: UUID;
|
|
103
|
+
/** Shown once. Not stored in retrievable form, and no endpoint returns it again. */
|
|
104
|
+
ingestSecret: string;
|
|
105
|
+
/** The URL to configure as the destination in Conduit, for convenience. */
|
|
106
|
+
ingestUrl: string;
|
|
107
|
+
/** The header Conduit must carry the secret in. */
|
|
108
|
+
ingestHeaderName: string;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=channel.type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.type.d.ts","sourceRoot":"","sources":["../../../../src/api/unibox/channel.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,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,IAAI,CAAC;IAET,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,eAAe,CAAC;IAE1B;;;;;;;OAOG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B,8FAA8F;IAC9F,aAAa,EAAE,MAAM,CAAC;IAEtB,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;;;;;;OAOG;IACH,eAAe,EAAE,OAAO,CAAC;IAEzB,+EAA+E;IAC/E,cAAc,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEtC,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;CAC1B;AAED,sGAAsG;AACtG,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;AAE9C,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAC9B,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;IAEZ,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,eAAe,CAAC;IAE1B,iBAAiB,EAAE,MAAM,CAAC;IAE1B,aAAa,EAAE,MAAM,CAAC;IAEtB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IACxC,SAAS,EAAE,IAAI,CAAC;IAEhB,oFAAoF;IACpF,YAAY,EAAE,MAAM,CAAC;IAErB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAElB,mDAAmD;IACnD,gBAAgB,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.type.js","sourceRoot":"","sources":["../../../../src/api/unibox/channel.type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,102 @@
|
|
|
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 { ChannelPlatform } from './channel-platform.enum.js';
|
|
6
|
+
/**
|
|
7
|
+
* One way of reaching a contact on one platform.
|
|
8
|
+
*
|
|
9
|
+
* A person is not a phone number. The same human messages a business from WhatsApp on Monday and
|
|
10
|
+
* from Instagram on Thursday, and an inbox that treats those as two strangers is an inbox that
|
|
11
|
+
* loses the thread — so identities are a child collection rather than columns on the contact.
|
|
12
|
+
*
|
|
13
|
+
* That shape is what makes merging possible later without a migration. Merging is **not built**;
|
|
14
|
+
* the modelling for it is, which is the part that is expensive to retrofit.
|
|
15
|
+
*/
|
|
16
|
+
export interface ContactIdentity {
|
|
17
|
+
id: UUID;
|
|
18
|
+
platform: ChannelPlatform;
|
|
19
|
+
/**
|
|
20
|
+
* The provider's own identifier for this person — a WhatsApp `wa_id`, an Instagram-scoped
|
|
21
|
+
* user id.
|
|
22
|
+
*
|
|
23
|
+
* Unique per `(tenant, platform)`. **Scoped ids are not portable across platforms**: the same
|
|
24
|
+
* person has an entirely different id on Instagram and on Messenger, and treating one as the
|
|
25
|
+
* other merges two strangers. That is why the platform is part of the key rather than the id
|
|
26
|
+
* being assumed globally unique.
|
|
27
|
+
*/
|
|
28
|
+
externalId: string;
|
|
29
|
+
/** The name the provider supplied, when it supplied one. Not authoritative and often absent. */
|
|
30
|
+
displayName: Nullable<string>;
|
|
31
|
+
createdAt: IsoDateTime;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A person the tenant talks to.
|
|
35
|
+
*
|
|
36
|
+
* Personal data, deliberately minimal: Unibox stores what it needs to thread a conversation and
|
|
37
|
+
* address someone by name. A CRM's worth of fields belongs in the vertical that needs them, and
|
|
38
|
+
* §17's GDPR line is materially cheaper to honour on a small table.
|
|
39
|
+
*/
|
|
40
|
+
export interface Contact {
|
|
41
|
+
id: UUID;
|
|
42
|
+
/**
|
|
43
|
+
* The name shown in the inbox.
|
|
44
|
+
*
|
|
45
|
+
* Editable by an agent, because a provider's display name is frequently a handle or absent,
|
|
46
|
+
* and correcting it is the first thing anyone does. Falls back to the first identity's
|
|
47
|
+
* `displayName`, then to its `externalId`.
|
|
48
|
+
*/
|
|
49
|
+
displayName: string;
|
|
50
|
+
email: Nullable<string>;
|
|
51
|
+
/** E.164 where known. Not the same as a WhatsApp identity's `externalId`, which is scoped. */
|
|
52
|
+
phone: Nullable<string>;
|
|
53
|
+
/** Free-form agent notes. Not shown to the contact and never sent anywhere. */
|
|
54
|
+
notes: Nullable<string>;
|
|
55
|
+
/** Lowercased, deduplicated. For filtering the inbox, not for automation. */
|
|
56
|
+
tags: string[];
|
|
57
|
+
identities: ContactIdentity[];
|
|
58
|
+
/** When this contact last sent or received a message. Null until the first one. */
|
|
59
|
+
lastActivityAt: Nullable<IsoDateTime>;
|
|
60
|
+
createdAt: IsoDateTime;
|
|
61
|
+
updatedAt: IsoDateTime;
|
|
62
|
+
}
|
|
63
|
+
/** `GET /api/v1/contacts`. Cursor-paged: inbound traffic appends to it while it is being read. */
|
|
64
|
+
export type ContactsPage = CursorPage<Contact>;
|
|
65
|
+
/** Query for `GET /api/v1/contacts`. */
|
|
66
|
+
export interface GetContactsQuery {
|
|
67
|
+
limit?: number;
|
|
68
|
+
/** Opaque cursor from the previous page's `nextCursor`. Never parse it. */
|
|
69
|
+
after?: Nullable<string>;
|
|
70
|
+
/** Case-insensitive substring of the display name, email or phone. */
|
|
71
|
+
search?: Nullable<string>;
|
|
72
|
+
tag?: Nullable<string>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `POST /api/v1/contacts`.
|
|
76
|
+
*
|
|
77
|
+
* Creating a contact by hand is the uncommon path — almost every contact arrives because someone
|
|
78
|
+
* messaged in, and inbound creates them. This exists so an agent can prepare a record before a
|
|
79
|
+
* conversation starts, and so a vertical can seed one.
|
|
80
|
+
*/
|
|
81
|
+
export interface CreateContactBody {
|
|
82
|
+
displayName: string;
|
|
83
|
+
email?: Nullable<string>;
|
|
84
|
+
phone?: Nullable<string>;
|
|
85
|
+
notes?: Nullable<string>;
|
|
86
|
+
tags?: Nullable<string[]>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* `PATCH /api/v1/contacts/:id`.
|
|
90
|
+
*
|
|
91
|
+
* `identities` is absent: an identity is asserted by a provider, not chosen by an agent. Adding
|
|
92
|
+
* one by hand is how two strangers get merged into one record, and there is no undo for that.
|
|
93
|
+
*/
|
|
94
|
+
export interface UpdateContactBody {
|
|
95
|
+
displayName?: string;
|
|
96
|
+
email?: Nullable<string>;
|
|
97
|
+
phone?: Nullable<string>;
|
|
98
|
+
notes?: Nullable<string>;
|
|
99
|
+
/** Replaces the whole set. Omit to leave it alone; `[]` clears it. */
|
|
100
|
+
tags?: Nullable<string[]>;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=contact.type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact.type.d.ts","sourceRoot":"","sources":["../../../../src/api/unibox/contact.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,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,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,IAAI,CAAC;IAET,QAAQ,EAAE,eAAe,CAAC;IAE1B;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,gGAAgG;IAChG,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9B,SAAS,EAAE,WAAW,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,IAAI,CAAC;IAET;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAExB,8FAA8F;IAC9F,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAExB,+EAA+E;IAC/E,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAExB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf,UAAU,EAAE,eAAe,EAAE,CAAC;IAE9B,mFAAmF;IACnF,cAAc,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEtC,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;CAC1B;AAED,kGAAkG;AAClG,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAE/C,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEzB,sEAAsE;IACtE,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE1B,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEzB,sEAAsE;IACtE,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact.type.js","sourceRoot":"","sources":["../../../../src/api/unibox/contact.type.ts"],"names":[],"mappings":""}
|