@flow-industries/id 0.26.3 → 0.26.4

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 CHANGED
@@ -23,6 +23,33 @@ partially migrated schema.
23
23
 
24
24
  ### AT Protocol identity
25
25
 
26
+ Consumers discover the signed-in user's current PDS through
27
+ `await flow.atproto.resolve({ freshness: "cached" })`. Use `"refresh"` after an
28
+ endpoint or authorization failure, and call `flow.atproto.invalidate()` after
29
+ identity or migration events. Flow account switches, logout and the first-party
30
+ linking helpers invalidate automatically. Invalidation forces the next request
31
+ to refresh. Discovery uses the configured Auth host, cookie or Flow bearer
32
+ session, and canonical user ID; it never provisions an account.
33
+
34
+ The result includes the DID, verified display handle (or null), current PDS
35
+ origin, hosting ownership, lifecycle and authorization status. DID documents
36
+ are authoritative for the endpoint. No OAuth tokens or private keys are
37
+ returned. `connected` indicates an unexpired stored grant whose audience and
38
+ issuer match public resource metadata; it does not prove that a remote grant
39
+ has not since been revoked. Writes must obtain fresh AT OAuth authorization,
40
+ and must not forward old credentials to a migrated host or replay mutations
41
+ with unknown outcomes. `AtprotoRequestError.code` distinguishes sign-in,
42
+ missing identity, pending or failed provisioning, account changes, unsupported
43
+ DID methods, invalid identity and unavailable resolution.
44
+
45
+ `ATPROTO_MANAGED_PDS_ORIGINS` is an optional comma-separated list of public
46
+ HTTPS PDS origins owned by Flow, used only to classify current hosting.
47
+ `ATPROTO_RESOLUTION_TTL_MS` enables a server metadata cache keyed by DID and
48
+ mapping version, bounded to five minutes; unset or invalid values disable
49
+ caching. Authorization status is checked separately on each request. A cached
50
+ authorization failure forces one fresh DID resolution. Public discovery works
51
+ without OAuth credential configuration and reports `reconnect_required`.
52
+
26
53
  Bluesky and other AT Protocol accounts can sign in to Flow ID or connect to an
27
54
  existing account. Flow user IDs remain stable; the provider DID identifies the
28
55
  connection. New accounts do not need an email, passkey, or wallet to enroll.
@@ -223,3 +250,14 @@ still need complete request/response descriptions and generated client coverage
223
250
  before the full HTTP contract work is complete.
224
251
 
225
252
  Server deployments may set `FLOW_ID_API_URL` to a trusted internal HTTP(S) origin for API and JWKS requests, or pass `apiUrl` to server session/verification helpers. `issuerUrl` remains the public JWT issuer and browser navigation origin. The transport URL must contain no credentials, path, query, or fragment.
253
+
254
+ PDS discovery requires explicit nonnegative integer budgets in
255
+ `ATPROTO_RESOLUTION_USER_HOURLY_CAP` and
256
+ `ATPROTO_RESOLUTION_GLOBAL_HOURLY_CAP`. Unset, invalid or zero budgets refuse
257
+ outbound discovery with HTTP 429 and retryable `RESOLUTION_UNAVAILABLE`. Every discovery
258
+ request, including cached reads and forced refreshes, consumes durable
259
+ per-user and fleet-wide hourly admission before any provider request. Failed
260
+ outbound attempts retain admission consumption; counters use the existing
261
+ bounded verification retention and canonical account erasure mechanism.
262
+ These caps bound request admission across replicas; operators must choose
263
+ budgets appropriate to replica count, outbound latency and capacity.
@@ -92,6 +92,31 @@
92
92
  "type": false,
93
93
  "value": true
94
94
  },
95
+ {
96
+ "name": "AtprotoApi",
97
+ "type": true,
98
+ "value": false
99
+ },
100
+ {
101
+ "name": "AtprotoRequestError",
102
+ "type": true,
103
+ "value": true
104
+ },
105
+ {
106
+ "name": "AtprotoResolution",
107
+ "type": true,
108
+ "value": false
109
+ },
110
+ {
111
+ "name": "AtprotoResolutionFailure",
112
+ "type": true,
113
+ "value": false
114
+ },
115
+ {
116
+ "name": "AtprotoResolveOptions",
117
+ "type": true,
118
+ "value": false
119
+ },
95
120
  {
96
121
  "name": "BrowserAccount",
97
122
  "type": true,
@@ -142,6 +167,11 @@
142
167
  "type": false,
143
168
  "value": true
144
169
  },
170
+ {
171
+ "name": "createAtprotoApi",
172
+ "type": false,
173
+ "value": true
174
+ },
145
175
  {
146
176
  "name": "createDialogHost",
147
177
  "type": false,
@@ -0,0 +1,29 @@
1
+ import type { AppDb } from "./db/handle";
2
+ import type { SessionUser } from "./types";
3
+ /**
4
+ * Caller-identity core, shared by the dialog-facing routes and the tests. Takes
5
+ * the drizzle handle as a parameter (postgres-js in the server, PGlite in
6
+ * tests) instead of importing the singleton, which requires DATABASE_URL at
7
+ * import time. The singleton-bound entry point is `resolveCaller` in
8
+ * `routes/caller.ts`.
9
+ */
10
+ /**
11
+ * The user behind a dialog request, and the better-auth session row that
12
+ * authenticated it — null when the caller presented an access JWT instead,
13
+ * since a bearer holds no session row.
14
+ */
15
+ /** The session row as callers read it: opaque beyond the fields session-view names. */
16
+ export type CallerSession = {
17
+ id: string;
18
+ audience?: string | null;
19
+ };
20
+ export type Caller = {
21
+ user: SessionUser;
22
+ session: CallerSession | null;
23
+ };
24
+ /**
25
+ * Reads the user a verified bearer's `sub` names, in the shape better-auth puts
26
+ * on `session.user`. Null when the subject no longer exists — a token for a
27
+ * deleted account is unauthorized, not a caller with an empty profile.
28
+ */
29
+ export declare function loadCallerUser(dbi: AppDb, userId: string): Promise<SessionUser | null>;
@@ -0,0 +1,37 @@
1
+ import { eq } from "drizzle-orm";
2
+ import * as schema from "./db/schema";
3
+ import { sessionUserOf } from "./session-view";
4
+ /**
5
+ * The columns better-auth puts on `session.user` (base fields plus the
6
+ * configured `additionalFields`), so a bearer-authenticated caller reads back
7
+ * the same user shape a cookie-authenticated one does. `sessionEpoch` is
8
+ * deliberately absent: it is an internal revocation lever, not profile data.
9
+ */
10
+ const USER_COLUMNS = {
11
+ id: schema.user.id,
12
+ name: schema.user.name,
13
+ username: schema.user.username,
14
+ email: schema.user.email,
15
+ emailVerified: schema.user.emailVerified,
16
+ image: schema.user.image,
17
+ bio: schema.user.bio,
18
+ isGuest: schema.user.isGuest,
19
+ role: schema.user.role,
20
+ walletAddress: schema.user.walletAddress,
21
+ subscribedToNews: schema.user.subscribedToNews,
22
+ createdAt: schema.user.createdAt,
23
+ updatedAt: schema.user.updatedAt,
24
+ };
25
+ /**
26
+ * Reads the user a verified bearer's `sub` names, in the shape better-auth puts
27
+ * on `session.user`. Null when the subject no longer exists — a token for a
28
+ * deleted account is unauthorized, not a caller with an empty profile.
29
+ */
30
+ export async function loadCallerUser(dbi, userId) {
31
+ const [user] = await dbi
32
+ .select(USER_COLUMNS)
33
+ .from(schema.user)
34
+ .where(eq(schema.user.id, userId))
35
+ .limit(1);
36
+ return user ? sessionUserOf(user) : null;
37
+ }
@@ -0,0 +1,8 @@
1
+ import type { AtprotoApi, AtprotoResolutionFailure } from "../types/atproto";
2
+ export declare class AtprotoRequestError extends Error {
3
+ readonly code: AtprotoResolutionFailure;
4
+ readonly retryable: boolean;
5
+ constructor(code: AtprotoResolutionFailure, retryable?: boolean);
6
+ }
7
+ /** Identity changes and migration completion should invalidate before another discovery. */
8
+ export declare function createAtprotoApi(host: string, getToken: () => Promise<string | null>, getUserId: () => string | null): AtprotoApi;
@@ -0,0 +1,87 @@
1
+ import { z } from "zod";
2
+ const failureSchema = z.enum([
3
+ "SIGN_IN_REQUIRED",
4
+ "NO_AT_IDENTITY",
5
+ "PROVISIONING_PENDING",
6
+ "PROVISIONING_FAILED",
7
+ "ACCOUNT_CHANGED",
8
+ "UNSUPPORTED_DID_METHOD",
9
+ "IDENTITY_INVALID",
10
+ "RESOLUTION_UNAVAILABLE",
11
+ ]);
12
+ const resolutionSchema = z.object({
13
+ flowUserId: z.string(),
14
+ did: z.string(),
15
+ handle: z.string().nullable(),
16
+ pdsUrl: z.url(),
17
+ hosting: z.enum(["flow", "external"]),
18
+ lifecycle: z.enum(["active", "suspended", "migrated", "deletion_pending"]),
19
+ authorization: z.enum(["connected", "reconnect_required"]),
20
+ resolvedAt: z.iso.datetime(),
21
+ expiresAt: z.iso.datetime(),
22
+ identityVersion: z.string(),
23
+ });
24
+ export class AtprotoRequestError extends Error {
25
+ code;
26
+ retryable;
27
+ constructor(code, retryable = false) {
28
+ super(code);
29
+ this.code = code;
30
+ this.retryable = retryable;
31
+ this.name = "AtprotoRequestError";
32
+ }
33
+ }
34
+ /** Identity changes and migration completion should invalidate before another discovery. */
35
+ export function createAtprotoApi(host, getToken, getUserId) {
36
+ let generation = 0;
37
+ let dirty = false;
38
+ return {
39
+ invalidate() {
40
+ generation++;
41
+ dirty = true;
42
+ },
43
+ async resolve(options = {}) {
44
+ options.signal?.throwIfAborted();
45
+ const userId = getUserId();
46
+ if (!userId)
47
+ throw new AtprotoRequestError("SIGN_IN_REQUIRED");
48
+ const requestGeneration = generation;
49
+ const unchanged = () => {
50
+ if (generation !== requestGeneration || getUserId() !== userId)
51
+ throw new AtprotoRequestError("ACCOUNT_CHANGED");
52
+ };
53
+ const token = await getToken();
54
+ unchanged();
55
+ const headers = new Headers({ "X-Flow-User-Id": userId });
56
+ if (token)
57
+ headers.set("Authorization", `Bearer ${token}`);
58
+ let response;
59
+ try {
60
+ response = await fetch(`${host}/api/atproto/resolve?freshness=${dirty ? "refresh" : (options.freshness ?? "cached")}`, { headers, credentials: "include", signal: options.signal });
61
+ }
62
+ catch {
63
+ options.signal?.throwIfAborted();
64
+ unchanged();
65
+ throw new AtprotoRequestError("RESOLUTION_UNAVAILABLE", true);
66
+ }
67
+ unchanged();
68
+ const body = await response.json().catch(() => null);
69
+ options.signal?.throwIfAborted();
70
+ unchanged();
71
+ if (!response.ok) {
72
+ const parsed = z.object({ code: failureSchema }).safeParse(body);
73
+ const code = parsed.success
74
+ ? parsed.data.code
75
+ : "RESOLUTION_UNAVAILABLE";
76
+ throw new AtprotoRequestError(code, ["RESOLUTION_UNAVAILABLE", "PROVISIONING_PENDING"].includes(code));
77
+ }
78
+ const parsed = resolutionSchema.safeParse(body);
79
+ if (!parsed.success)
80
+ throw new AtprotoRequestError("IDENTITY_INVALID");
81
+ if (parsed.data.flowUserId !== userId)
82
+ throw new AtprotoRequestError("ACCOUNT_CHANGED");
83
+ dirty = false;
84
+ return parsed.data;
85
+ },
86
+ };
87
+ }
@@ -1,3 +1,4 @@
1
+ import { getFlow } from "./create-flow";
1
2
  async function readResponse(response) {
2
3
  const body = await response.json();
3
4
  if (!response.ok)
@@ -17,6 +18,7 @@ export async function unlinkAtproto(expectedUserId) {
17
18
  headers: { "Content-Type": "application/json" },
18
19
  body: JSON.stringify({ expectedUserId }),
19
20
  }));
21
+ getFlow()?.atproto.invalidate();
20
22
  }
21
23
  const completionKey = "flow.atproto.completion";
22
24
  export async function connectAtproto(request, signal) {
@@ -30,6 +32,7 @@ export async function connectAtproto(request, signal) {
30
32
  }));
31
33
  signal.throwIfAborted();
32
34
  sessionStorage.setItem(completionKey, result.completionToken);
35
+ getFlow()?.atproto.invalidate();
33
36
  window.location.assign(result.url);
34
37
  }
35
38
  let completion = null;
@@ -44,6 +47,11 @@ export function completeAtproto() {
44
47
  credentials: "include",
45
48
  headers: { "Content-Type": "application/json" },
46
49
  body: JSON.stringify({ completionToken }),
47
- }).then((readResponse));
50
+ })
51
+ .then((readResponse))
52
+ .then((result) => {
53
+ getFlow()?.atproto.invalidate();
54
+ return result;
55
+ });
48
56
  return completion;
49
57
  }
@@ -1,6 +1,7 @@
1
1
  import { BROWSER_FLOW_TTL_MS } from "../browser-contract";
2
2
  import { DEFAULT_SESSION_PATH, isLocalHostname, resolveIdHost, } from "../id-host";
3
3
  import { isExpiring } from "../token-expiry";
4
+ import { createAtprotoApi } from "./atproto-resolver";
4
5
  import { createDialogHost } from "./dialog-host";
5
6
  import { idb } from "./idb";
6
7
  import { createRoomsApi } from "./rooms";
@@ -510,6 +511,14 @@ export function createFlow(options = {}) {
510
511
  };
511
512
  window.addEventListener("focus", refreshOnFocus);
512
513
  document.addEventListener("visibilitychange", refreshOnFocus);
514
+ const atproto = createAtprotoApi(host, getToken, () => store.getSnapshot().user?.id ?? null);
515
+ let resolverUserId = store.getSnapshot().user?.id ?? null;
516
+ store.subscribe((state) => {
517
+ const userId = state.user?.id ?? null;
518
+ if (userId !== resolverUserId)
519
+ atproto.invalidate();
520
+ resolverUserId = userId;
521
+ });
513
522
  const flow = {
514
523
  get user() {
515
524
  return store.getSnapshot().user;
@@ -541,6 +550,7 @@ export function createFlow(options = {}) {
541
550
  getToken,
542
551
  mintAudience,
543
552
  rooms: createRoomsApi(host, getToken),
553
+ atproto,
544
554
  signMessage: async (args) => {
545
555
  const mod = await import("./signing");
546
556
  return mod.signMessage(buildSigningContext(), args);
@@ -1,7 +1,8 @@
1
1
  export { defaultIdHost, isLocalHostname } from "../id-host";
2
- export type { AccessKeyOptions, ActionCatalogEntry, ActionCatalogResponse, ActionCurve, ActionEventRequest, ActionEventResponse, ActionSubjectRef, ActionSurface, ActionsApi, ActionTransitionKind, ActiveAction, ActiveActionResponse, ActiveSessionConflict, AdditionalSession, Address, BrowserAccount, BrowserAccounts, CatalogFieldRef, ClockAnchor, ConnectCapabilities, ConnectResponse, CreateFlowOptions, CreateStudySubjectInput, CurrentAction, DialogHost, FinishActionRequest, FinishActionResponse, FinishedSummary, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, FlowWidgetHandle, FlowWidgetName, FocusApi, FocusPhase, FocusSessionEvent, FocusSessionState, ListStudySessionsOptions, ListStudySubjectsOptions, LoginOptions, MarkNotificationsReadInput, MethodName, MountFlowWidgetOptions, MountProfileOptions, NotificationCounts, NotificationEntry, NotificationPage, NotificationReadResult, NotificationRefusal, OpenProfileOptions, ProfileButtonHandle, ProfilePosition, RoomMemberEntry, Session, StartActionInput, StartActionResponse, StartActionResult, StartFocusRequest, StudyApi, StudyCatalog, StudyCategory, StudyCategoryTotals, StudyField, StudyRange, StudySearchHit, StudySessionEntry, StudySessionPage, StudySubject, StudySubjectList, StudySubjectRefusal, StudySubjectRemovalResponse, StudySubjectResponse, StudySubjectTotals, StudySummary, SwitchAccountOptions, UpdateStudySubjectInput, UserActionSurface, XpEstimateContext, XpMode, } from "../types";
2
+ export type { AccessKeyOptions, ActionCatalogEntry, ActionCatalogResponse, ActionCurve, ActionEventRequest, ActionEventResponse, ActionSubjectRef, ActionSurface, ActionsApi, ActionTransitionKind, ActiveAction, ActiveActionResponse, ActiveSessionConflict, AdditionalSession, Address, AtprotoApi, AtprotoResolution, AtprotoResolutionFailure, AtprotoResolveOptions, BrowserAccount, BrowserAccounts, CatalogFieldRef, ClockAnchor, ConnectCapabilities, ConnectResponse, CreateFlowOptions, CreateStudySubjectInput, CurrentAction, DialogHost, FinishActionRequest, FinishActionResponse, FinishedSummary, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, FlowWidgetHandle, FlowWidgetName, FocusApi, FocusPhase, FocusSessionEvent, FocusSessionState, ListStudySessionsOptions, ListStudySubjectsOptions, LoginOptions, MarkNotificationsReadInput, MethodName, MountFlowWidgetOptions, MountProfileOptions, NotificationCounts, NotificationEntry, NotificationPage, NotificationReadResult, NotificationRefusal, OpenProfileOptions, ProfileButtonHandle, ProfilePosition, RoomMemberEntry, Session, StartActionInput, StartActionResponse, StartActionResult, StartFocusRequest, StudyApi, StudyCatalog, StudyCategory, StudyCategoryTotals, StudyField, StudyRange, StudySearchHit, StudySessionEntry, StudySessionPage, StudySubject, StudySubjectList, StudySubjectRefusal, StudySubjectRemovalResponse, StudySubjectResponse, StudySubjectTotals, StudySummary, SwitchAccountOptions, UpdateStudySubjectInput, UserActionSurface, XpEstimateContext, XpMode, } from "../types";
3
3
  export { consistencyCurve, estimateActionXp, FLOW_SCORE_MAX, flowMultiplier, } from "../xp/curve";
4
4
  export { ActionsRequestError, createActionsApi, isActiveSessionConflict, } from "./actions";
5
+ export { AtprotoRequestError, createAtprotoApi } from "./atproto-resolver";
5
6
  export { createFlow, getFlow, requireFlow, resetFlow } from "./create-flow";
6
7
  export { createDialogHost } from "./dialog-host";
7
8
  export { createFlowWidget } from "./flow-widget";
@@ -1,6 +1,7 @@
1
1
  export { defaultIdHost, isLocalHostname } from "../id-host";
2
2
  export { consistencyCurve, estimateActionXp, FLOW_SCORE_MAX, flowMultiplier, } from "../xp/curve";
3
3
  export { ActionsRequestError, createActionsApi, isActiveSessionConflict, } from "./actions";
4
+ export { AtprotoRequestError, createAtprotoApi } from "./atproto-resolver";
4
5
  export { createFlow, getFlow, requireFlow, resetFlow } from "./create-flow";
5
6
  export { createDialogHost } from "./dialog-host";
6
7
  export { createFlowWidget } from "./flow-widget";
@@ -1,4 +1,5 @@
1
1
  import { resolveIdHost } from "../id-host";
2
+ import { createAtprotoApi } from "./atproto-resolver";
2
3
  import { createRoomsApi } from "./rooms";
3
4
  function unavailable(method) {
4
5
  throw new Error(`flow.${method} is not available on a static Flow — it renders ` +
@@ -55,6 +56,7 @@ export function createStaticFlow(state, options = {}) {
55
56
  getToken,
56
57
  mintAudience: async () => null,
57
58
  rooms: createRoomsApi(host, getToken),
59
+ atproto: createAtprotoApi(host, getToken, () => snapshot.user?.id ?? null),
58
60
  signMessage: () => unavailable("signMessage"),
59
61
  signTypedData: () => unavailable("signTypedData"),
60
62
  sendTransaction: () => unavailable("sendTransaction"),
@@ -0,0 +1,310 @@
1
+ export declare const signupPolicy: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "signup_policy";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "signup_policy";
8
+ dataType: "number";
9
+ columnType: "PgInteger";
10
+ data: number;
11
+ driverParam: string | number;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ invitationOnly: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "invitation_only";
24
+ tableName: "signup_policy";
25
+ dataType: "boolean";
26
+ columnType: "PgBoolean";
27
+ data: boolean;
28
+ driverParam: boolean;
29
+ notNull: true;
30
+ hasDefault: true;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: undefined;
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ updatedBy: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "updated_by";
41
+ tableName: "signup_policy";
42
+ dataType: "string";
43
+ columnType: "PgText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: false;
47
+ hasDefault: false;
48
+ isPrimaryKey: false;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: [string, ...string[]];
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "updated_at";
58
+ tableName: "signup_policy";
59
+ dataType: "date";
60
+ columnType: "PgTimestamp";
61
+ data: Date;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: true;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ };
74
+ dialect: "pg";
75
+ }>;
76
+ export declare const invitation: import("drizzle-orm/pg-core").PgTableWithColumns<{
77
+ name: "invitation";
78
+ schema: undefined;
79
+ columns: {
80
+ id: import("drizzle-orm/pg-core").PgColumn<{
81
+ name: "id";
82
+ tableName: "invitation";
83
+ dataType: "string";
84
+ columnType: "PgText";
85
+ data: string;
86
+ driverParam: string;
87
+ notNull: true;
88
+ hasDefault: false;
89
+ isPrimaryKey: true;
90
+ isAutoincrement: false;
91
+ hasRuntimeDefault: false;
92
+ enumValues: [string, ...string[]];
93
+ baseColumn: never;
94
+ identity: undefined;
95
+ generated: undefined;
96
+ }, {}, {}>;
97
+ codeHash: import("drizzle-orm/pg-core").PgColumn<{
98
+ name: "code_hash";
99
+ tableName: "invitation";
100
+ dataType: "string";
101
+ columnType: "PgText";
102
+ data: string;
103
+ driverParam: string;
104
+ notNull: true;
105
+ hasDefault: false;
106
+ isPrimaryKey: false;
107
+ isAutoincrement: false;
108
+ hasRuntimeDefault: false;
109
+ enumValues: [string, ...string[]];
110
+ baseColumn: never;
111
+ identity: undefined;
112
+ generated: undefined;
113
+ }, {}, {}>;
114
+ inviterId: import("drizzle-orm/pg-core").PgColumn<{
115
+ name: "inviter_id";
116
+ tableName: "invitation";
117
+ dataType: "string";
118
+ columnType: "PgText";
119
+ data: string;
120
+ driverParam: string;
121
+ notNull: false;
122
+ hasDefault: false;
123
+ isPrimaryKey: false;
124
+ isAutoincrement: false;
125
+ hasRuntimeDefault: false;
126
+ enumValues: [string, ...string[]];
127
+ baseColumn: never;
128
+ identity: undefined;
129
+ generated: undefined;
130
+ }, {}, {}>;
131
+ createdBy: import("drizzle-orm/pg-core").PgColumn<{
132
+ name: "created_by";
133
+ tableName: "invitation";
134
+ dataType: "string";
135
+ columnType: "PgText";
136
+ data: string;
137
+ driverParam: string;
138
+ notNull: false;
139
+ hasDefault: false;
140
+ isPrimaryKey: false;
141
+ isAutoincrement: false;
142
+ hasRuntimeDefault: false;
143
+ enumValues: [string, ...string[]];
144
+ baseColumn: never;
145
+ identity: undefined;
146
+ generated: undefined;
147
+ }, {}, {}>;
148
+ maxUses: import("drizzle-orm/pg-core").PgColumn<{
149
+ name: "max_uses";
150
+ tableName: "invitation";
151
+ dataType: "number";
152
+ columnType: "PgInteger";
153
+ data: number;
154
+ driverParam: string | number;
155
+ notNull: true;
156
+ hasDefault: true;
157
+ isPrimaryKey: false;
158
+ isAutoincrement: false;
159
+ hasRuntimeDefault: false;
160
+ enumValues: undefined;
161
+ baseColumn: never;
162
+ identity: undefined;
163
+ generated: undefined;
164
+ }, {}, {}>;
165
+ usedCount: import("drizzle-orm/pg-core").PgColumn<{
166
+ name: "used_count";
167
+ tableName: "invitation";
168
+ dataType: "number";
169
+ columnType: "PgInteger";
170
+ data: number;
171
+ driverParam: string | number;
172
+ notNull: true;
173
+ hasDefault: true;
174
+ isPrimaryKey: false;
175
+ isAutoincrement: false;
176
+ hasRuntimeDefault: false;
177
+ enumValues: undefined;
178
+ baseColumn: never;
179
+ identity: undefined;
180
+ generated: undefined;
181
+ }, {}, {}>;
182
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
183
+ name: "created_at";
184
+ tableName: "invitation";
185
+ dataType: "date";
186
+ columnType: "PgTimestamp";
187
+ data: Date;
188
+ driverParam: string;
189
+ notNull: true;
190
+ hasDefault: true;
191
+ isPrimaryKey: false;
192
+ isAutoincrement: false;
193
+ hasRuntimeDefault: false;
194
+ enumValues: undefined;
195
+ baseColumn: never;
196
+ identity: undefined;
197
+ generated: undefined;
198
+ }, {}, {}>;
199
+ expiresAt: import("drizzle-orm/pg-core").PgColumn<{
200
+ name: "expires_at";
201
+ tableName: "invitation";
202
+ dataType: "date";
203
+ columnType: "PgTimestamp";
204
+ data: Date;
205
+ driverParam: string;
206
+ notNull: true;
207
+ hasDefault: false;
208
+ isPrimaryKey: false;
209
+ isAutoincrement: false;
210
+ hasRuntimeDefault: false;
211
+ enumValues: undefined;
212
+ baseColumn: never;
213
+ identity: undefined;
214
+ generated: undefined;
215
+ }, {}, {}>;
216
+ revokedAt: import("drizzle-orm/pg-core").PgColumn<{
217
+ name: "revoked_at";
218
+ tableName: "invitation";
219
+ dataType: "date";
220
+ columnType: "PgTimestamp";
221
+ data: Date;
222
+ driverParam: string;
223
+ notNull: false;
224
+ hasDefault: false;
225
+ isPrimaryKey: false;
226
+ isAutoincrement: false;
227
+ hasRuntimeDefault: false;
228
+ enumValues: undefined;
229
+ baseColumn: never;
230
+ identity: undefined;
231
+ generated: undefined;
232
+ }, {}, {}>;
233
+ };
234
+ dialect: "pg";
235
+ }>;
236
+ export declare const invitationRedemption: import("drizzle-orm/pg-core").PgTableWithColumns<{
237
+ name: "invitation_redemption";
238
+ schema: undefined;
239
+ columns: {
240
+ userId: import("drizzle-orm/pg-core").PgColumn<{
241
+ name: "user_id";
242
+ tableName: "invitation_redemption";
243
+ dataType: "string";
244
+ columnType: "PgText";
245
+ data: string;
246
+ driverParam: string;
247
+ notNull: true;
248
+ hasDefault: false;
249
+ isPrimaryKey: true;
250
+ isAutoincrement: false;
251
+ hasRuntimeDefault: false;
252
+ enumValues: [string, ...string[]];
253
+ baseColumn: never;
254
+ identity: undefined;
255
+ generated: undefined;
256
+ }, {}, {}>;
257
+ invitationId: import("drizzle-orm/pg-core").PgColumn<{
258
+ name: "invitation_id";
259
+ tableName: "invitation_redemption";
260
+ dataType: "string";
261
+ columnType: "PgText";
262
+ data: string;
263
+ driverParam: string;
264
+ notNull: true;
265
+ hasDefault: false;
266
+ isPrimaryKey: false;
267
+ isAutoincrement: false;
268
+ hasRuntimeDefault: false;
269
+ enumValues: [string, ...string[]];
270
+ baseColumn: never;
271
+ identity: undefined;
272
+ generated: undefined;
273
+ }, {}, {}>;
274
+ inviterId: import("drizzle-orm/pg-core").PgColumn<{
275
+ name: "inviter_id";
276
+ tableName: "invitation_redemption";
277
+ dataType: "string";
278
+ columnType: "PgText";
279
+ data: string;
280
+ driverParam: string;
281
+ notNull: false;
282
+ hasDefault: false;
283
+ isPrimaryKey: false;
284
+ isAutoincrement: false;
285
+ hasRuntimeDefault: false;
286
+ enumValues: [string, ...string[]];
287
+ baseColumn: never;
288
+ identity: undefined;
289
+ generated: undefined;
290
+ }, {}, {}>;
291
+ redeemedAt: import("drizzle-orm/pg-core").PgColumn<{
292
+ name: "redeemed_at";
293
+ tableName: "invitation_redemption";
294
+ dataType: "date";
295
+ columnType: "PgTimestamp";
296
+ data: Date;
297
+ driverParam: string;
298
+ notNull: true;
299
+ hasDefault: true;
300
+ isPrimaryKey: false;
301
+ isAutoincrement: false;
302
+ hasRuntimeDefault: false;
303
+ enumValues: undefined;
304
+ baseColumn: never;
305
+ identity: undefined;
306
+ generated: undefined;
307
+ }, {}, {}>;
308
+ };
309
+ dialect: "pg";
310
+ }>;