@tula-tech/contract 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Names of the request headers the client API reads (the key, the client kind, the session
3
+ * profile, an attempt's secret), of the response header the admin factor
4
+ * reset answers with, and of the parameters an emailed sign-in link and an OAuth sign-in carry
5
+ * in a URL fragment.
6
+ *
7
+ * This module has no dependencies (no Zod), so an SDK can import it from
8
+ * `@tula-tech/contract/headers` without adding anything to an application's bundle.
9
+ */
10
+ /**
11
+ * Request header carrying the publishable key (`tula_pk_<env>_…`) on every `/v1/client/*` call.
12
+ * It names the project and environment the request belongs to.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * await fetch(`${api}/v1/client/config`, { headers: { [PUBLISHABLE_KEY_HEADER]: key } })
17
+ * ```
18
+ */
19
+ declare const PUBLISHABLE_KEY_HEADER = "x-tula-publishable-key";
20
+ /**
21
+ * Request header naming the kind of client (`web`, `ios`, `android` or `server`), read when an
22
+ * attempt starts. It decides how the refresh token is delivered when the flow completes: an
23
+ * httpOnly cookie for `web` (the default), the response body for the others.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * await fetch(`${api}/v1/client/sign-ins`, {
28
+ * method: 'POST',
29
+ * headers: { [CLIENT_HEADER]: 'ios', ...others },
30
+ * body: JSON.stringify({ identifier }),
31
+ * })
32
+ * ```
33
+ */
34
+ declare const CLIENT_HEADER = "x-tula-client";
35
+ /**
36
+ * Request header naming the session profile a client would like, read when an attempt starts
37
+ * (next to {@link CLIENT_HEADER}). Optional.
38
+ *
39
+ * It is a request, not a choice: the session gets that profile only when the environment marks
40
+ * it `clientSelectable`. Any other name (unknown, not offered, a built-in) is ignored and the
41
+ * session gets the profile of its client kind, so a client can never give itself a
42
+ * longer-lived session than the operator offered (ADR 0028).
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * await fetch(`${api}/v1/client/sign-ins`, {
47
+ * method: 'POST',
48
+ * headers: { [SESSION_PROFILE_HEADER]: 'admin', ...others },
49
+ * body: JSON.stringify({ identifier }),
50
+ * })
51
+ * ```
52
+ */
53
+ declare const SESSION_PROFILE_HEADER = "x-tula-session-profile";
54
+ /**
55
+ * Request header carrying an attempt's secret.
56
+ *
57
+ * Starting a sign-up, sign-in or password reset returns `attemptSecret` once; every later call
58
+ * on that attempt sends it in this header. Without it (or with another attempt's) the attempt
59
+ * answers `flow.not_found`, so an attempt id seen in a URL, a log or an email is useless alone.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * await fetch(`${api}/v1/client/sign-ins/${attempt.id}/password`, {
64
+ * method: 'POST',
65
+ * headers: { [FLOW_ATTEMPT_HEADER]: attempt.attemptSecret, ...others },
66
+ * body: JSON.stringify({ password }),
67
+ * })
68
+ * ```
69
+ */
70
+ declare const FLOW_ATTEMPT_HEADER = "x-tula-attempt";
71
+ /**
72
+ * Response header of the admin factor reset (`DELETE /v1/admin/users/:userId/factors`):
73
+ * `true` or `false`, whether the user can still sign in with what they have left.
74
+ *
75
+ * The reset removes the user's passkeys together with their authenticator and backup codes.
76
+ * `false` means nothing remains that the environment's settings would let them in with (no
77
+ * password where passwords are on, no verified address where the emailed code is on, no
78
+ * connected provider that is enabled): the operator has to give the account a way in, for
79
+ * example the user's own "Forgot password" where the password method is on. The route still
80
+ * answers `204` with no body; the header is how the outcome is told without changing that.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const res = await fetch(`${api}/v1/admin/users/${userId}/factors`, {
85
+ * method: 'DELETE',
86
+ * headers: { authorization: `Bearer ${secretKey}` },
87
+ * })
88
+ * const lockedOut = res.headers.get(CAN_STILL_SIGN_IN_HEADER) === 'false'
89
+ * ```
90
+ */
91
+ declare const CAN_STILL_SIGN_IN_HEADER = "x-tula-can-still-sign-in";
92
+ /**
93
+ * Request header of `PUT /v1/admin/settings` naming the tool that manages the environment's
94
+ * settings from a config file (`tula-apply`), or `none` to hand them back.
95
+ *
96
+ * Sent together with {@link CONFIG_HASH_HEADER}. The API records both with the settings and
97
+ * returns them as `managedBy`, so a dashboard can say "these settings come from a config file"
98
+ * and warn when they were changed around it. A replace without the header keeps the record,
99
+ * which then shows as drifted. See ADR 0030.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * headers[CONFIG_MANAGED_BY_HEADER] = 'tula-apply'
104
+ * headers[CONFIG_HASH_HEADER] = await hashEnvironmentConfig(environment)
105
+ * ```
106
+ */
107
+ declare const CONFIG_MANAGED_BY_HEADER = "x-tula-managed-by";
108
+ /**
109
+ * Request header of `PUT /v1/admin/settings` carrying the fingerprint of the config file's
110
+ * content for the environment: `sha256:` and 64 hex characters. It identifies a version of the
111
+ * file; it is computed over secret **names**, never values.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * headers[CONFIG_HASH_HEADER] // 'sha256:9f2c…'
116
+ * ```
117
+ */
118
+ declare const CONFIG_HASH_HEADER = "x-tula-config-hash";
119
+ /**
120
+ * The value of {@link CONFIG_MANAGED_BY_HEADER} that removes the record of a managing tool.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * headers[CONFIG_MANAGED_BY_HEADER] = CONFIG_UNMANAGED
125
+ * ```
126
+ */
127
+ declare const CONFIG_UNMANAGED = "none";
128
+ /**
129
+ * Request header every call the dashboard makes with its session cookie must carry, with the
130
+ * value {@link DASHBOARD_HEADER_VALUE} (ADR 0032).
131
+ *
132
+ * A browser attaches a cookie whoever wrote the page. This header is what a form cannot send
133
+ * and what a cross-origin `fetch` may send only after a preflight, which the API answers for
134
+ * the deployment's own origins alone. Without it the dashboard session cookie is ignored. With
135
+ * it, an `Authorization` header is refused: one request has one credential.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * await fetch('/v1/instance/session', { headers: { [DASHBOARD_HEADER]: DASHBOARD_HEADER_VALUE } })
140
+ * ```
141
+ */
142
+ declare const DASHBOARD_HEADER = "x-tula-dashboard";
143
+ /**
144
+ * The one value of {@link DASHBOARD_HEADER} the API accepts.
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * headers[DASHBOARD_HEADER] = DASHBOARD_HEADER_VALUE
149
+ * ```
150
+ */
151
+ declare const DASHBOARD_HEADER_VALUE = "1";
152
+ /**
153
+ * Request header naming the environment an `/v1/admin/*` call made with a dashboard session is
154
+ * for: the environment's id (a UUID). A secret key names its environment by itself and must not
155
+ * send it.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * await fetch('/v1/admin/users', {
160
+ * headers: { [DASHBOARD_HEADER]: DASHBOARD_HEADER_VALUE, [ENVIRONMENT_HEADER]: environment.id },
161
+ * })
162
+ * ```
163
+ */
164
+ declare const ENVIRONMENT_HEADER = "x-tula-environment";
165
+ /**
166
+ * Name of the dashboard session cookie (`HttpOnly`, `SameSite=Strict`). Over https it carries
167
+ * the `__Secure-` prefix. A page never reads it; the name is published for operators and
168
+ * proxies.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * const name = `${secure ? '__Secure-' : ''}${DASHBOARD_SESSION_COOKIE}`
173
+ * ```
174
+ */
175
+ declare const DASHBOARD_SESSION_COOKIE = "tula_dashboard";
176
+ /**
177
+ * Name of the URL-fragment parameter carrying an emailed sign-in link's token.
178
+ *
179
+ * The link is the app's own URL followed by `#tula_link=<token>&tula_attempt=<attempt id>`. A
180
+ * fragment is never sent to a server, a proxy or in `Referer`, so the token reaches only the
181
+ * page's own script, which posts it to `/v1/client/sign-ins/link` and removes it from the URL.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * const token = new URLSearchParams(location.hash.slice(1)).get(EMAIL_LINK_TOKEN_PARAM)
186
+ * ```
187
+ */
188
+ declare const EMAIL_LINK_TOKEN_PARAM = "tula_link";
189
+ /**
190
+ * Name of the URL-fragment parameter carrying the id of the sign-in an emailed link belongs to.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const attemptId = new URLSearchParams(location.hash.slice(1)).get(EMAIL_LINK_ATTEMPT_PARAM)
195
+ * ```
196
+ */
197
+ declare const EMAIL_LINK_ATTEMPT_PARAM = "tula_attempt";
198
+ /**
199
+ * Name of the URL-fragment parameter carrying the single-use ticket an OAuth sign-in returns to
200
+ * the app's page with (ADR 0026).
201
+ *
202
+ * The API redirects to the app's URL followed by `#tula_ticket=<ticket>&tula_attempt=<attempt
203
+ * id>`. The page's script removes the fragment from the address and posts the ticket, with the
204
+ * binding its browser kept, to `/v1/client/sign-ins/oauth/exchange`. The ticket is not a token:
205
+ * it lasts a minute, works once, and only together with that binding.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const ticket = new URLSearchParams(location.hash.slice(1)).get(OAUTH_TICKET_PARAM)
210
+ * ```
211
+ */
212
+ declare const OAUTH_TICKET_PARAM = "tula_ticket";
213
+ /**
214
+ * Name of the URL-fragment parameter carrying the contract error code of an OAuth sign-in that
215
+ * failed before a ticket was issued (the user cancelled, the provider failed).
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * const code = new URLSearchParams(location.hash.slice(1)).get(OAUTH_ERROR_PARAM)
220
+ * ```
221
+ */
222
+ declare const OAUTH_ERROR_PARAM = "tula_error";
223
+ export { CAN_STILL_SIGN_IN_HEADER, CLIENT_HEADER, CONFIG_HASH_HEADER, CONFIG_MANAGED_BY_HEADER, CONFIG_UNMANAGED, DASHBOARD_HEADER, DASHBOARD_HEADER_VALUE, DASHBOARD_SESSION_COOKIE, EMAIL_LINK_ATTEMPT_PARAM, EMAIL_LINK_TOKEN_PARAM, ENVIRONMENT_HEADER, FLOW_ATTEMPT_HEADER, OAUTH_ERROR_PARAM, OAUTH_TICKET_PARAM, PUBLISHABLE_KEY_HEADER, SESSION_PROFILE_HEADER };
@@ -0,0 +1,36 @@
1
+ import {
2
+ PUBLISHABLE_KEY_HEADER2,
3
+ CLIENT_HEADER2,
4
+ SESSION_PROFILE_HEADER2,
5
+ FLOW_ATTEMPT_HEADER2,
6
+ CAN_STILL_SIGN_IN_HEADER2,
7
+ CONFIG_MANAGED_BY_HEADER2,
8
+ CONFIG_HASH_HEADER2,
9
+ CONFIG_UNMANAGED2,
10
+ DASHBOARD_HEADER2,
11
+ DASHBOARD_HEADER_VALUE2,
12
+ ENVIRONMENT_HEADER2,
13
+ DASHBOARD_SESSION_COOKIE2,
14
+ EMAIL_LINK_TOKEN_PARAM2,
15
+ EMAIL_LINK_ATTEMPT_PARAM2,
16
+ OAUTH_TICKET_PARAM2,
17
+ OAUTH_ERROR_PARAM2
18
+ } from "./shared/chunk-q309e9bk.js";
19
+ export {
20
+ CAN_STILL_SIGN_IN_HEADER2 as CAN_STILL_SIGN_IN_HEADER,
21
+ CLIENT_HEADER2 as CLIENT_HEADER,
22
+ CONFIG_HASH_HEADER2 as CONFIG_HASH_HEADER,
23
+ CONFIG_MANAGED_BY_HEADER2 as CONFIG_MANAGED_BY_HEADER,
24
+ CONFIG_UNMANAGED2 as CONFIG_UNMANAGED,
25
+ DASHBOARD_HEADER2 as DASHBOARD_HEADER,
26
+ DASHBOARD_HEADER_VALUE2 as DASHBOARD_HEADER_VALUE,
27
+ DASHBOARD_SESSION_COOKIE2 as DASHBOARD_SESSION_COOKIE,
28
+ EMAIL_LINK_ATTEMPT_PARAM2 as EMAIL_LINK_ATTEMPT_PARAM,
29
+ EMAIL_LINK_TOKEN_PARAM2 as EMAIL_LINK_TOKEN_PARAM,
30
+ ENVIRONMENT_HEADER2 as ENVIRONMENT_HEADER,
31
+ FLOW_ATTEMPT_HEADER2 as FLOW_ATTEMPT_HEADER,
32
+ OAUTH_ERROR_PARAM2 as OAUTH_ERROR_PARAM,
33
+ OAUTH_TICKET_PARAM2 as OAUTH_TICKET_PARAM,
34
+ PUBLISHABLE_KEY_HEADER2 as PUBLISHABLE_KEY_HEADER,
35
+ SESSION_PROFILE_HEADER2 as SESSION_PROFILE_HEADER
36
+ };