@sealant/api-contracts-next 0.0.0-next.0 → 0.39.0-next.682

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 (40) hide show
  1. package/LICENSE +202 -0
  2. package/dist/core-api/access-tokens.d.ts +126 -0
  3. package/dist/core-api/access-tokens.js +72 -0
  4. package/dist/core-api/budgets.d.ts +15 -0
  5. package/dist/core-api/budgets.js +13 -0
  6. package/dist/core-api/connected-accounts.d.ts +226 -0
  7. package/dist/core-api/connected-accounts.js +128 -0
  8. package/dist/core-api/control-plane.d.ts +1318 -0
  9. package/dist/core-api/control-plane.js +31 -0
  10. package/dist/core-api/github.d.ts +261 -0
  11. package/dist/core-api/github.js +153 -0
  12. package/dist/core-api/inference.d.ts +223 -0
  13. package/dist/core-api/inference.js +139 -0
  14. package/dist/core-api/oci-names.d.ts +21 -0
  15. package/dist/core-api/oci-names.js +44 -0
  16. package/dist/core-api/packages.d.ts +179 -0
  17. package/dist/core-api/packages.js +60 -0
  18. package/dist/core-api/profiles.d.ts +188 -0
  19. package/dist/core-api/profiles.js +88 -0
  20. package/dist/core-api/record-events.d.ts +209 -0
  21. package/dist/core-api/record-events.js +193 -0
  22. package/dist/core-api/registries.d.ts +106 -0
  23. package/dist/core-api/registries.js +85 -0
  24. package/dist/core-api/runs.d.ts +516 -0
  25. package/dist/core-api/runs.js +284 -0
  26. package/dist/core-api/sessions.d.ts +390 -0
  27. package/dist/core-api/sessions.js +264 -0
  28. package/dist/core-api/ssh-keys.d.ts +120 -0
  29. package/dist/core-api/ssh-keys.js +108 -0
  30. package/dist/core-api/system.d.ts +51 -0
  31. package/dist/core-api/system.js +41 -0
  32. package/dist/core-api/users.d.ts +71 -0
  33. package/dist/core-api/users.js +48 -0
  34. package/dist/core-api/workspaces.d.ts +1880 -0
  35. package/dist/core-api/workspaces.js +980 -0
  36. package/dist/index.d.ts +18 -0
  37. package/dist/index.js +18 -0
  38. package/dist/workspace-environment.d.ts +124 -0
  39. package/dist/workspace-environment.js +259 -0
  40. package/package.json +31 -9
@@ -0,0 +1,120 @@
1
+ import { Schema } from "effect";
2
+ import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
3
+ export declare const sshKeyGatewayHeadersSchema: Schema.Struct<{
4
+ readonly "x-sealant-gateway-token": Schema.optional<Schema.String>;
5
+ }>;
6
+ export type SshKeyGatewayHeaders = typeof sshKeyGatewayHeadersSchema.Type;
7
+ export declare const createSshKeyRequestSchema: Schema.Struct<{
8
+ readonly ownerUserId: Schema.String;
9
+ readonly name: Schema.optional<Schema.String>;
10
+ readonly publicKey: Schema.String;
11
+ }>;
12
+ export type CreateSshKeyRequest = typeof createSshKeyRequestSchema.Type;
13
+ export declare const sshKeySummarySchema: Schema.Struct<{
14
+ readonly sshKeyId: Schema.String;
15
+ readonly ownerUserId: Schema.String;
16
+ readonly name: Schema.String;
17
+ readonly algorithm: Schema.String;
18
+ readonly fingerprint: Schema.String;
19
+ readonly createdAt: Schema.String;
20
+ }>;
21
+ export type SshKeySummary = typeof sshKeySummarySchema.Type;
22
+ export declare const listSshKeysQuerySchema: Schema.Struct<{
23
+ readonly ownerUserId: Schema.String;
24
+ }>;
25
+ export declare const listSshKeysResponseSchema: Schema.Struct<{
26
+ readonly items: Schema.$Array<Schema.Struct<{
27
+ readonly sshKeyId: Schema.String;
28
+ readonly ownerUserId: Schema.String;
29
+ readonly name: Schema.String;
30
+ readonly algorithm: Schema.String;
31
+ readonly fingerprint: Schema.String;
32
+ readonly createdAt: Schema.String;
33
+ }>>;
34
+ }>;
35
+ export type ListSshKeysResponse = typeof listSshKeysResponseSchema.Type;
36
+ export declare const resolveSshPrincipalRequestSchema: Schema.Struct<{
37
+ readonly algo: Schema.String;
38
+ readonly publicKeyBase64: Schema.String;
39
+ }>;
40
+ export type ResolveSshPrincipalRequest = typeof resolveSshPrincipalRequestSchema.Type;
41
+ export declare const resolveSshPrincipalResponseSchema: Schema.Struct<{
42
+ readonly principalId: Schema.String;
43
+ readonly sshKeyId: Schema.String;
44
+ readonly fingerprint: Schema.String;
45
+ }>;
46
+ export type ResolveSshPrincipalResponse = typeof resolveSshPrincipalResponseSchema.Type;
47
+ declare const SshKeyBadRequestError_base: Schema.Class<SshKeyBadRequestError, Schema.TaggedStruct<"SshKeyBadRequestError", {
48
+ readonly message: Schema.String;
49
+ }>, import("effect/Cause").YieldableError>;
50
+ export declare class SshKeyBadRequestError extends SshKeyBadRequestError_base {
51
+ }
52
+ declare const SshKeyUnauthorizedError_base: Schema.Class<SshKeyUnauthorizedError, Schema.TaggedStruct<"SshKeyUnauthorizedError", {
53
+ readonly message: Schema.String;
54
+ }>, import("effect/Cause").YieldableError>;
55
+ export declare class SshKeyUnauthorizedError extends SshKeyUnauthorizedError_base {
56
+ }
57
+ declare const SshKeyNotFoundError_base: Schema.Class<SshKeyNotFoundError, Schema.TaggedStruct<"SshKeyNotFoundError", {
58
+ readonly message: Schema.String;
59
+ }>, import("effect/Cause").YieldableError>;
60
+ export declare class SshKeyNotFoundError extends SshKeyNotFoundError_base {
61
+ }
62
+ declare const SshKeyConflictError_base: Schema.Class<SshKeyConflictError, Schema.TaggedStruct<"SshKeyConflictError", {
63
+ readonly message: Schema.String;
64
+ }>, import("effect/Cause").YieldableError>;
65
+ export declare class SshKeyConflictError extends SshKeyConflictError_base {
66
+ }
67
+ declare const SshKeyServiceUnavailableError_base: Schema.Class<SshKeyServiceUnavailableError, Schema.TaggedStruct<"SshKeyServiceUnavailableError", {
68
+ readonly message: Schema.String;
69
+ }>, import("effect/Cause").YieldableError>;
70
+ export declare class SshKeyServiceUnavailableError extends SshKeyServiceUnavailableError_base {
71
+ }
72
+ declare const SshKeyInternalServerError_base: Schema.Class<SshKeyInternalServerError, Schema.TaggedStruct<"SshKeyInternalServerError", {
73
+ readonly message: Schema.String;
74
+ }>, import("effect/Cause").YieldableError>;
75
+ export declare class SshKeyInternalServerError extends SshKeyInternalServerError_base {
76
+ }
77
+ export declare const SshKeysGroup: HttpApiGroup.HttpApiGroup<"sshKeys", HttpApiEndpoint.HttpApiEndpoint<"archiveSshKey", "DELETE", "/:sshKeyId", HttpApiEndpoint.StringTree<Schema.Struct<{
78
+ readonly sshKeyId: Schema.String;
79
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
80
+ readonly ownerUserId: Schema.String;
81
+ }>>, HttpApiEndpoint.Json<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
82
+ readonly sshKeyId: Schema.String;
83
+ readonly ownerUserId: Schema.String;
84
+ readonly name: Schema.String;
85
+ readonly algorithm: Schema.String;
86
+ readonly fingerprint: Schema.String;
87
+ readonly createdAt: Schema.String;
88
+ }>>, HttpApiEndpoint.Json<typeof SshKeyNotFoundError | typeof SshKeyInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"createSshKey", "POST", "/", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
89
+ readonly ownerUserId: Schema.String;
90
+ readonly name: Schema.optional<Schema.String>;
91
+ readonly publicKey: Schema.String;
92
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
93
+ readonly sshKeyId: Schema.String;
94
+ readonly ownerUserId: Schema.String;
95
+ readonly name: Schema.String;
96
+ readonly algorithm: Schema.String;
97
+ readonly fingerprint: Schema.String;
98
+ readonly createdAt: Schema.String;
99
+ }>>, HttpApiEndpoint.Json<typeof SshKeyBadRequestError | typeof SshKeyNotFoundError | typeof SshKeyConflictError | typeof SshKeyInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"listSshKeys", "GET", "/", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<Schema.Struct<{
100
+ readonly ownerUserId: Schema.String;
101
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
102
+ readonly items: Schema.$Array<Schema.Struct<{
103
+ readonly sshKeyId: Schema.String;
104
+ readonly ownerUserId: Schema.String;
105
+ readonly name: Schema.String;
106
+ readonly algorithm: Schema.String;
107
+ readonly fingerprint: Schema.String;
108
+ readonly createdAt: Schema.String;
109
+ }>>;
110
+ }>>, HttpApiEndpoint.Json<typeof SshKeyInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"resolveSshPrincipal", "POST", "/resolve-principal", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
111
+ readonly algo: Schema.String;
112
+ readonly publicKeyBase64: Schema.String;
113
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
114
+ readonly "x-sealant-gateway-token": Schema.optional<Schema.String>;
115
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
116
+ readonly principalId: Schema.String;
117
+ readonly sshKeyId: Schema.String;
118
+ readonly fingerprint: Schema.String;
119
+ }>>, HttpApiEndpoint.Json<typeof SshKeyUnauthorizedError | typeof SshKeyNotFoundError | typeof SshKeyServiceUnavailableError | typeof SshKeyInternalServerError>, never, never>, false>;
120
+ export {};
@@ -0,0 +1,108 @@
1
+ import { Schema } from "effect";
2
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi";
3
+ const NonEmptyString = Schema.String.check(Schema.isNonEmpty(), Schema.isTrimmed());
4
+ /*
5
+ User SSH public keys. Two audiences:
6
+ - the web app (via its tRPC proxy) registers/lists/archives keys for the logged-in user;
7
+ - the SSH gateway resolves an offered key to its owning principal at auth time via
8
+ `resolveSshPrincipal`, gated by the shared gateway token (same trust model as
9
+ `getWorkspaceSshTarget` in workspaces.ts).
10
+ */
11
+ export const sshKeyGatewayHeadersSchema = Schema.Struct({
12
+ // Authenticates the gateway as a trusted caller of the internal resolve endpoint.
13
+ "x-sealant-gateway-token": Schema.optional(NonEmptyString),
14
+ });
15
+ export const createSshKeyRequestSchema = Schema.Struct({
16
+ // Same trust model as createWorkspace: the internal API trusts the caller-supplied owner; the web
17
+ // server injects the session user id.
18
+ ownerUserId: NonEmptyString,
19
+ name: Schema.optional(NonEmptyString),
20
+ // Raw `<algorithm> <base64> [comment]` line; the API normalizes and fingerprints it.
21
+ publicKey: NonEmptyString,
22
+ });
23
+ export const sshKeySummarySchema = Schema.Struct({
24
+ sshKeyId: NonEmptyString,
25
+ ownerUserId: NonEmptyString,
26
+ name: NonEmptyString,
27
+ algorithm: NonEmptyString,
28
+ fingerprint: NonEmptyString,
29
+ createdAt: Schema.String,
30
+ });
31
+ export const listSshKeysQuerySchema = Schema.Struct({
32
+ ownerUserId: NonEmptyString,
33
+ });
34
+ export const listSshKeysResponseSchema = Schema.Struct({
35
+ items: Schema.Array(sshKeySummarySchema),
36
+ });
37
+ export const resolveSshPrincipalRequestSchema = Schema.Struct({
38
+ // ctx.key.algo / base64(ctx.key.data) from the gateway's ssh2 auth context. The API recomputes
39
+ // the fingerprint server-side; callers never supply one.
40
+ algo: NonEmptyString,
41
+ publicKeyBase64: NonEmptyString,
42
+ });
43
+ export const resolveSshPrincipalResponseSchema = Schema.Struct({
44
+ principalId: NonEmptyString,
45
+ sshKeyId: NonEmptyString,
46
+ fingerprint: NonEmptyString,
47
+ });
48
+ export class SshKeyBadRequestError extends Schema.TaggedErrorClass()("SshKeyBadRequestError", {
49
+ message: Schema.String,
50
+ }, { httpApiStatus: 400 }) {
51
+ }
52
+ export class SshKeyUnauthorizedError extends Schema.TaggedErrorClass()("SshKeyUnauthorizedError", {
53
+ message: Schema.String,
54
+ }, { httpApiStatus: 401 }) {
55
+ }
56
+ export class SshKeyNotFoundError extends Schema.TaggedErrorClass()("SshKeyNotFoundError", {
57
+ message: Schema.String,
58
+ }, { httpApiStatus: 404 }) {
59
+ }
60
+ export class SshKeyConflictError extends Schema.TaggedErrorClass()("SshKeyConflictError", {
61
+ message: Schema.String,
62
+ }, { httpApiStatus: 409 }) {
63
+ }
64
+ export class SshKeyServiceUnavailableError extends Schema.TaggedErrorClass()("SshKeyServiceUnavailableError", {
65
+ message: Schema.String,
66
+ }, { httpApiStatus: 503 }) {
67
+ }
68
+ export class SshKeyInternalServerError extends Schema.TaggedErrorClass()("SshKeyInternalServerError", {
69
+ message: Schema.String,
70
+ }, { httpApiStatus: 500 }) {
71
+ }
72
+ const sshKeyIdParams = Schema.Struct({ sshKeyId: NonEmptyString });
73
+ export const SshKeysGroup = HttpApiGroup.make("sshKeys")
74
+ .add(HttpApiEndpoint.post("createSshKey", "/", {
75
+ payload: createSshKeyRequestSchema,
76
+ success: sshKeySummarySchema.pipe(HttpApiSchema.status(201)),
77
+ error: [
78
+ SshKeyBadRequestError,
79
+ SshKeyNotFoundError,
80
+ SshKeyConflictError,
81
+ SshKeyInternalServerError,
82
+ ],
83
+ }))
84
+ .add(HttpApiEndpoint.get("listSshKeys", "/", {
85
+ query: listSshKeysQuerySchema,
86
+ success: listSshKeysResponseSchema,
87
+ error: [SshKeyInternalServerError],
88
+ }))
89
+ .add(HttpApiEndpoint.delete("archiveSshKey", "/:sshKeyId", {
90
+ params: sshKeyIdParams,
91
+ query: listSshKeysQuerySchema,
92
+ success: sshKeySummarySchema,
93
+ error: [SshKeyNotFoundError, SshKeyInternalServerError],
94
+ }))
95
+ .add(
96
+ // POST (not GET) so key material stays out of URLs and access logs.
97
+ HttpApiEndpoint.post("resolveSshPrincipal", "/resolve-principal", {
98
+ headers: sshKeyGatewayHeadersSchema,
99
+ payload: resolveSshPrincipalRequestSchema,
100
+ success: resolveSshPrincipalResponseSchema,
101
+ error: [
102
+ SshKeyUnauthorizedError,
103
+ SshKeyNotFoundError,
104
+ SshKeyServiceUnavailableError,
105
+ SshKeyInternalServerError,
106
+ ],
107
+ }))
108
+ .annotate(OpenApi.Description, "User SSH public keys and gateway principal resolution.");
@@ -0,0 +1,51 @@
1
+ import { Schema } from "effect";
2
+ import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
3
+ export declare const systemIndexResponseSchema: Schema.Struct<{
4
+ readonly name: Schema.String;
5
+ readonly version: Schema.String;
6
+ readonly docsPath: Schema.String;
7
+ readonly openApiPath: Schema.String;
8
+ }>;
9
+ export type SystemIndexResponse = typeof systemIndexResponseSchema.Type;
10
+ export declare const systemHealthResponseSchema: Schema.Struct<{
11
+ readonly status: Schema.Literal<"ok">;
12
+ }>;
13
+ export type SystemHealthResponse = typeof systemHealthResponseSchema.Type;
14
+ export declare const setupStateSshGatewaySchema: Schema.Struct<{
15
+ readonly host: Schema.String;
16
+ readonly port: Schema.Number;
17
+ readonly usernamePrefix: Schema.String;
18
+ }>;
19
+ export type SetupStateSshGateway = typeof setupStateSshGatewaySchema.Type;
20
+ export declare const setupStateResponseSchema: Schema.Struct<{
21
+ readonly needsSetup: Schema.Boolean;
22
+ readonly sshGateway: Schema.NullOr<Schema.Struct<{
23
+ readonly host: Schema.String;
24
+ readonly port: Schema.Number;
25
+ readonly usernamePrefix: Schema.String;
26
+ }>>;
27
+ }>;
28
+ export type SetupStateResponse = typeof setupStateResponseSchema.Type;
29
+ declare const SystemInternalServerError_base: Schema.Class<SystemInternalServerError, Schema.TaggedStruct<"SystemInternalServerError", {
30
+ readonly message: Schema.String;
31
+ }>, import("effect/Cause").YieldableError>;
32
+ export declare class SystemInternalServerError extends SystemInternalServerError_base {
33
+ }
34
+ export declare const SystemGroup: HttpApiGroup.HttpApiGroup<"system", HttpApiEndpoint.HttpApiEndpoint<"getIndex", "GET", "/", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
35
+ readonly name: Schema.String;
36
+ readonly version: Schema.String;
37
+ readonly docsPath: Schema.String;
38
+ readonly openApiPath: Schema.String;
39
+ }>>, HttpApiEndpoint.Json<never>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getSetupState", "GET", "/v1/system/setup-state", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
40
+ readonly needsSetup: Schema.Boolean;
41
+ readonly sshGateway: Schema.NullOr<Schema.Struct<{
42
+ readonly host: Schema.String;
43
+ readonly port: Schema.Number;
44
+ readonly usernamePrefix: Schema.String;
45
+ }>>;
46
+ }>>, HttpApiEndpoint.Json<typeof SystemInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"health", "GET", "/healthz", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
47
+ readonly status: Schema.Literal<"ok">;
48
+ }>>, HttpApiEndpoint.Json<never>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"ready", "GET", "/readyz", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
49
+ readonly status: Schema.Literal<"ok">;
50
+ }>>, HttpApiEndpoint.Json<never>, never, never>, false>;
51
+ export {};
@@ -0,0 +1,41 @@
1
+ import { Schema } from "effect";
2
+ import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi";
3
+ const NonEmptyString = Schema.String.check(Schema.isNonEmpty(), Schema.isTrimmed());
4
+ export const systemIndexResponseSchema = Schema.Struct({
5
+ name: NonEmptyString,
6
+ version: NonEmptyString,
7
+ docsPath: NonEmptyString,
8
+ openApiPath: NonEmptyString,
9
+ });
10
+ export const systemHealthResponseSchema = Schema.Struct({
11
+ status: Schema.Literal("ok"),
12
+ });
13
+ export const setupStateSshGatewaySchema = Schema.Struct({
14
+ host: NonEmptyString,
15
+ // Defaults (22 / "ws") are applied server-side so clients never hardcode them.
16
+ port: Schema.Number,
17
+ usernamePrefix: NonEmptyString,
18
+ });
19
+ export const setupStateResponseSchema = Schema.Struct({
20
+ // True while nobody can sign in (zero better-auth accounts); drives the first-run wizard. The
21
+ // seeded SDK owner (usr_local) has no credentials and does not count.
22
+ needsSetup: Schema.Boolean,
23
+ // Null when WORKSPACE_SSH_GATEWAY_HOST is not configured on the API.
24
+ sshGateway: Schema.NullOr(setupStateSshGatewaySchema),
25
+ });
26
+ export class SystemInternalServerError extends Schema.TaggedErrorClass()("SystemInternalServerError", {
27
+ message: Schema.String,
28
+ }, { httpApiStatus: 500 }) {
29
+ }
30
+ export const SystemGroup = HttpApiGroup.make("system")
31
+ .add(HttpApiEndpoint.get("getIndex", "/", { success: systemIndexResponseSchema }))
32
+ .add(HttpApiEndpoint.get("health", "/healthz", { success: systemHealthResponseSchema }))
33
+ .add(HttpApiEndpoint.get("ready", "/readyz", { success: systemHealthResponseSchema }))
34
+ .add(
35
+ // Public by design: pre-auth gating in the web app needs it. Exposes only needsSetup and the
36
+ // gateway connect coordinates (host/port/prefix), which every workspace endpoint echoes anyway.
37
+ HttpApiEndpoint.get("getSetupState", "/v1/system/setup-state", {
38
+ success: setupStateResponseSchema,
39
+ error: [SystemInternalServerError],
40
+ }))
41
+ .annotate(OpenApi.Description, "System metadata, health probes, and first-run setup state.");
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Users wire contracts — identity rows for products that own their own login.
3
+ *
4
+ * A SERVICE PRINCIPAL (Mend) provisions one Sealant user per person and then acts on their behalf
5
+ * through the `ownerUserId` every owned endpoint carries. `ensureUser` is idempotent on email so
6
+ * the caller can run it on every sign-in without bookkeeping; the id is the owner id to use from
7
+ * then on. Sign-in credentials are never part of this surface — better-auth (web) owns those.
8
+ */
9
+ import { Schema } from "effect";
10
+ import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
11
+ export declare const userSchema: Schema.Struct<{
12
+ readonly userId: Schema.String;
13
+ readonly email: Schema.String;
14
+ readonly name: Schema.String;
15
+ readonly createdAt: Schema.String;
16
+ }>;
17
+ export type UserWire = typeof userSchema.Type;
18
+ export declare const ensureUserRequestSchema: Schema.Struct<{
19
+ /** Matched case-insensitively; the stored form is lower-cased. */
20
+ readonly email: Schema.String;
21
+ readonly name: Schema.String;
22
+ /** Caller-chosen id for a NEW user (`usr_…`); ignored when the email already exists. */
23
+ readonly userId: Schema.optional<Schema.String>;
24
+ }>;
25
+ export type EnsureUserRequest = typeof ensureUserRequestSchema.Type;
26
+ export declare const ensureUserResponseSchema: Schema.Struct<{
27
+ readonly userId: Schema.String;
28
+ readonly email: Schema.String;
29
+ readonly name: Schema.String;
30
+ readonly createdAt: Schema.String;
31
+ /** True when this call created the user. */
32
+ readonly created: Schema.Boolean;
33
+ }>;
34
+ export type EnsureUserResponse = typeof ensureUserResponseSchema.Type;
35
+ declare const UserBadRequestError_base: Schema.Class<UserBadRequestError, Schema.TaggedStruct<"UserBadRequestError", {
36
+ readonly message: Schema.String;
37
+ }>, import("effect/Cause").YieldableError>;
38
+ export declare class UserBadRequestError extends UserBadRequestError_base {
39
+ }
40
+ declare const UserNotFoundError_base: Schema.Class<UserNotFoundError, Schema.TaggedStruct<"UserNotFoundError", {
41
+ readonly message: Schema.String;
42
+ }>, import("effect/Cause").YieldableError>;
43
+ export declare class UserNotFoundError extends UserNotFoundError_base {
44
+ }
45
+ declare const UserInternalServerError_base: Schema.Class<UserInternalServerError, Schema.TaggedStruct<"UserInternalServerError", {
46
+ readonly message: Schema.String;
47
+ }>, import("effect/Cause").YieldableError>;
48
+ export declare class UserInternalServerError extends UserInternalServerError_base {
49
+ }
50
+ export declare const UsersGroup: HttpApiGroup.HttpApiGroup<"users", HttpApiEndpoint.HttpApiEndpoint<"ensureUser", "POST", "/", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
51
+ /** Matched case-insensitively; the stored form is lower-cased. */
52
+ readonly email: Schema.String;
53
+ readonly name: Schema.String;
54
+ /** Caller-chosen id for a NEW user (`usr_…`); ignored when the email already exists. */
55
+ readonly userId: Schema.optional<Schema.String>;
56
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
57
+ readonly userId: Schema.String;
58
+ readonly email: Schema.String;
59
+ readonly name: Schema.String;
60
+ readonly createdAt: Schema.String;
61
+ /** True when this call created the user. */
62
+ readonly created: Schema.Boolean;
63
+ }>>, HttpApiEndpoint.Json<typeof UserBadRequestError | typeof UserInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getUser", "GET", "/:userId", HttpApiEndpoint.StringTree<Schema.Struct<{
64
+ readonly userId: Schema.String;
65
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
66
+ readonly userId: Schema.String;
67
+ readonly email: Schema.String;
68
+ readonly name: Schema.String;
69
+ readonly createdAt: Schema.String;
70
+ }>>, HttpApiEndpoint.Json<typeof UserNotFoundError | typeof UserInternalServerError>, never, never>, false>;
71
+ export {};
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Users wire contracts — identity rows for products that own their own login.
3
+ *
4
+ * A SERVICE PRINCIPAL (Mend) provisions one Sealant user per person and then acts on their behalf
5
+ * through the `ownerUserId` every owned endpoint carries. `ensureUser` is idempotent on email so
6
+ * the caller can run it on every sign-in without bookkeeping; the id is the owner id to use from
7
+ * then on. Sign-in credentials are never part of this surface — better-auth (web) owns those.
8
+ */
9
+ import { Schema } from "effect";
10
+ import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi";
11
+ const NonEmptyString = Schema.String.check(Schema.isNonEmpty(), Schema.isTrimmed());
12
+ export const userSchema = Schema.Struct({
13
+ userId: NonEmptyString,
14
+ email: NonEmptyString,
15
+ name: NonEmptyString,
16
+ createdAt: Schema.String,
17
+ });
18
+ export const ensureUserRequestSchema = Schema.Struct({
19
+ /** Matched case-insensitively; the stored form is lower-cased. */
20
+ email: NonEmptyString,
21
+ name: NonEmptyString,
22
+ /** Caller-chosen id for a NEW user (`usr_…`); ignored when the email already exists. */
23
+ userId: Schema.optional(NonEmptyString),
24
+ });
25
+ export const ensureUserResponseSchema = Schema.Struct({
26
+ ...userSchema.fields,
27
+ /** True when this call created the user. */
28
+ created: Schema.Boolean,
29
+ });
30
+ export class UserBadRequestError extends Schema.TaggedErrorClass()("UserBadRequestError", { message: Schema.String }, { httpApiStatus: 400 }) {
31
+ }
32
+ export class UserNotFoundError extends Schema.TaggedErrorClass()("UserNotFoundError", { message: Schema.String }, { httpApiStatus: 404 }) {
33
+ }
34
+ export class UserInternalServerError extends Schema.TaggedErrorClass()("UserInternalServerError", { message: Schema.String }, { httpApiStatus: 500 }) {
35
+ }
36
+ const userIdParams = Schema.Struct({ userId: NonEmptyString });
37
+ export const UsersGroup = HttpApiGroup.make("users")
38
+ .add(HttpApiEndpoint.post("ensureUser", "/", {
39
+ payload: ensureUserRequestSchema,
40
+ success: ensureUserResponseSchema,
41
+ error: [UserBadRequestError, UserInternalServerError],
42
+ }))
43
+ .add(HttpApiEndpoint.get("getUser", "/:userId", {
44
+ params: userIdParams,
45
+ success: userSchema,
46
+ error: [UserNotFoundError, UserInternalServerError],
47
+ }))
48
+ .annotate(OpenApi.Description, "Identity rows for products that own their own login and act on behalf of their users.");