@lenso/organization 0.0.0-stage → 0.2.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 CHANGED
@@ -1,3 +1,185 @@
1
- # Temporary Holding Version
1
+ # Organization
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Optional business organizations, memberships and invitations. The root entry is
4
+ an ordinary async service with no runtime dependencies on Lenso, Auth, Drizzle,
5
+ Manage, Tasks or notification providers.
6
+
7
+ ## Responsibility boundaries
8
+
9
+ - **Auth** establishes identity and revalidates its proof. A subject is
10
+ `{ realmId, subjectId }`, not a foreign key to a users table or an email address.
11
+ - **Organization** persists relationships: `member`, `admin`, `owner`, and
12
+ invitations.
13
+ - **Authorization** decides whether a verified subject may perform an action
14
+ within a particular organization. `OrganizationAccess.check` is required.
15
+
16
+ An organization owner is not a Console or platform administrator. A shared Auth
17
+ identity may be a business customer without Console admission, or a Console
18
+ member without any business organization. Independent Console SSO can use another
19
+ Auth realm/source; this package does not create a second password system.
20
+
21
+ ## Direct service and existing Auth
22
+
23
+ ```ts
24
+ import { audience } from "@lenso/auth";
25
+ import { createOrganizationService, memberRelationshipPolicy } from "@lenso/organization";
26
+ import { createAuthOrganizationAccess } from "@lenso/organization/auth";
27
+ import { postgresOrganizationStore } from "@lenso/organization/drizzle/pg";
28
+
29
+ // Borrow the application's existing Auth and Drizzle database.
30
+ const access = authentication.for(audience("business:organization"));
31
+ const organizations = createOrganizationService({
32
+ store: postgresOrganizationStore(database),
33
+ access: createAuthOrganizationAccess({
34
+ access,
35
+ policy: memberRelationshipPolicy,
36
+ }),
37
+ });
38
+
39
+ // evidence comes from the trusted ingress, never organization input JSON.
40
+ const actor = await access.required(evidence);
41
+ const created = await organizations.createOrganization(actor, { name: "Operations" });
42
+ const members = await organizations.listMembers(actor, {
43
+ organizationId: created.value.id,
44
+ });
45
+ ```
46
+
47
+ The Auth adapter uses `Access.enforce` on every call and CAS retry. Cloned,
48
+ wrong-audience, wrong-instance and revoked actors fail Auth verification.
49
+ `memberRelationshipPolicy` is an **opt-in example policy**, not inherent role
50
+ semantics: members can read the organization/members and leave; admins manage
51
+ ordinary members and invitations; owners can grant roles/ownership and transfer
52
+ their ownership. Creation is allowed to verified subjects under this policy.
53
+ Applications should constrain creation, actor kinds, realms and administrative
54
+ actions according to their own authorization requirements.
55
+
56
+ Cross-organization administrators require an explicit policy in the access
57
+ adapter, independently verified by the application. No global organization list,
58
+ implicit platform override or Console admission rule is provided. All member and
59
+ invitation IDs, lists and totals are checked in the supplied organization scope.
60
+ Policy callbacks receive actual stored relationships, not client-assigned roles.
61
+ Single-invitation actions include the actual scoped invitation and target, without
62
+ the digest. Role changes include the requested role. List actions authorize the
63
+ **whole organization's** list and count, not a filtered per-invitation view;
64
+ policies that permit only individual invitations should deny these list actions.
65
+
66
+ ## Thin Lenso registration
67
+
68
+ `createOrganizationPlugin` from `/plugin` accepts:
69
+
70
+ ```ts
71
+ import { createOrganizationPlugin } from "@lenso/organization/plugin";
72
+
73
+ const organization = createOrganizationPlugin({
74
+ id: "business-organizations",
75
+ database: databasePlugin,
76
+ access: organizationAccessPlugin,
77
+ store: postgresOrganizationStore, // d1OrganizationStore for D1
78
+ config: { invitationLifetimeMs: 86_400_000 },
79
+ });
80
+ ```
81
+
82
+ `databasePlugin` and `organizationAccessPlugin` are the **exact installed
83
+ instances**; the latter supplies `OrganizationAccess<Actor>`. Install those
84
+ instances alongside `organization`. No resources are acquired during config
85
+ import, no migrations run at startup, and stopping this plugin does not close
86
+ borrowed Auth/database resources.
87
+
88
+ Factory configuration is validated by `resolveOrganizationConfig`. Applications
89
+ using Lenso Config may resolve these values in their existing configured setup
90
+ and pass them to `createOrganizationService`; this package does not introduce a
91
+ configuration source or hot-reload system.
92
+
93
+ | Option | Default | Allowed |
94
+ | ---------------------- | ------- | ----------------------------------------- |
95
+ | `invitationLifetimeMs` | 7 days | 1 second to 30 days |
96
+ | `maxMembers` | 1000 | 1 to 1000 |
97
+ | `maxInvitations` | 1000 | 1 to 1000, including terminal invitations |
98
+ | `conflictRetries` | 8 | 0 to 32 known CAS misses |
99
+
100
+ ## Operations and invitation handling
101
+
102
+ The service exposes organization create/read/update; member list/add/remove/leave;
103
+ role changes; explicit owner grant/transfer; invitation create/read/list/accept/
104
+ revoke/prune. Input objects reject unknown fields. Add and invite always assign
105
+ `member`; ordinary role input cannot assign `owner`. Self-promotion from member
106
+ to admin and self-grant of ownership are rejected even with a permissive checker.
107
+
108
+ `createInvitation(actor, { organizationId, target })` returns a token once in
109
+ `result.value.token`. It contains 256 random bits. Storage contains only a SHA-256
110
+ digest bound to the organization, invitation ID, realm/subject, fixed role and
111
+ deadline. Do not log the token, put it in a URL or expose it in discovery metadata.
112
+ Acceptance requires the exact target identity, organization, invitation ID and
113
+ token. It atomically consumes the pending invitation and adds membership.
114
+
115
+ Replay returns `INVITATION_ACCEPTED`; revoked and expired invitations return
116
+ `INVITATION_REVOKED` and `INVITATION_EXPIRED`. Wrong subject/token/scoped ID returns
117
+ `NOT_FOUND` after access checks. Removing/leaving a membership also revokes its
118
+ pending invitations in the same write. Expiry is derived from storage time;
119
+ expired invitations never grant membership.
120
+
121
+ No email is sent. An application may call its optional Notification adapter
122
+ **after** invitation creation commits. Delivery failure must not roll back or
123
+ replace the invitation fact; retry delivery separately using the existing Tasks
124
+ integration if needed. An uncertain storage write is not automatically retried.
125
+
126
+ Terminal/expired records remain available until explicitly
127
+ `pruneInvitations(actor, { organizationId, before })` removes them. Pruning never
128
+ removes a still-live pending invitation. After pruning, replay returns `NOT_FOUND`.
129
+
130
+ Each mutation returns `{ value, change }`; `change` includes organization ID,
131
+ new version, action and affected subject references, never invitation credentials.
132
+ `organizationMembershipReader(store)` from `/auth` can feed
133
+ `Auth.Access.memberships`, returning role/version or null after removal.
134
+ Authorization caches must include organization + realm + subject and revalidate
135
+ versions or arrange their own invalidation. Return metadata is not an event
136
+ delivery guarantee; no global event platform is installed.
137
+
138
+ ## Storage and explicit migrations
139
+
140
+ - PG: `/drizzle/pg`, `/drizzle/schema-pg`, `migrations/pg/0000_organizations.sql`.
141
+ - D1: `/drizzle/d1`, `/drizzle/schema-sqlite`,
142
+ `migrations/sqlite/0000_organizations.sql`.
143
+
144
+ Add the chosen schema to the application's existing Drizzle schema. Incorporate
145
+ the reviewed SQL into **one** application-owned migration history and apply it
146
+ explicitly to an authorized database; do not apply both Drizzle and Wrangler
147
+ migration runners to one D1 store.
148
+
149
+ One row holds each organization's bounded JSON relationship snapshot and version.
150
+ Every mutation performs version CAS. PG locks the row inside a transaction and
151
+ checks the deadline in a fresh statement after waiting. D1 uses a single
152
+ conditional `UPDATE ... RETURNING`, without interactive transactions. Last-owner
153
+ checks and all transfer/member/invitation changes are committed together. A known
154
+ CAS miss rereads and reauthorizes; a storage exception has unknown outcome.
155
+
156
+ This design rewrites the snapshot and serializes writes per organization. It is
157
+ intended for bounded organizations, not unbounded enterprise directories. A
158
+ 512,000-byte serialized state limit supplements the count limits. High contention
159
+ can return `CONFLICT`; large organizations need a separately designed normalized
160
+ store. Direct SQL edits bypass service authorization and are not a public API.
161
+ Admission reserves timestamp-growth headroom so revoking many pending invites
162
+ cannot prevent member removal/exit. Lowering configured count limits stops growth,
163
+ but does not block cleanup of already-admitted relationships.
164
+
165
+ ## Validation
166
+
167
+ After building existing `@lenso/core` and `@lenso/auth`, run:
168
+
169
+ ```sh
170
+ bun run --cwd packages/organization build
171
+ bun run --cwd packages/organization typecheck
172
+ LENSO_REQUIRE_POSTGRES=1 bun run --cwd packages/organization test
173
+ ```
174
+
175
+ PG tests create and stop their own private loopback cluster; the required flag
176
+ fails if `postgres`, `initdb` or `pg_ctl` is unavailable. D1 tests use the existing
177
+ installed Miniflare/workerd toolchain with disposable local D1 state, not a SQLite
178
+ shim. Auth fixture tests exercise the real Auth proof boundary but do not validate
179
+ an external identity provider. Local D1 evidence does not validate deployed
180
+ Cloudflare replication. No exactly-once or cross-resource consistency is claimed.
181
+
182
+ The workspace uses the single integration-owned root Bun lockfile.
183
+ Manage/CLI/MCP operations are not automatically registered. If an application
184
+ exposes any, it must explicitly select methods, authenticate trusted entry context
185
+ and apply its organization authorization policy.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { Access, Actor, MembershipReader } from "@lenso/auth";
2
+ import type { AccessRequest, MemberRole, OrganizationAccess, OrganizationStore } from "./contracts";
3
+ /**
4
+ * Borrow the application's audience-bound Auth access. Policy is required:
5
+ * authentication and organization membership are not authorization by themselves.
6
+ */
7
+ export declare function createAuthOrganizationAccess<R extends string, Evidence, S extends string, A extends string>(options: {
8
+ access: Access<R, Evidence, S, A>;
9
+ policy(principal: Actor<R, S, A>, request: AccessRequest): boolean | Promise<boolean>;
10
+ }): OrganizationAccess<Actor<R, S, A> | null>;
11
+ export interface OrganizationMembership {
12
+ role: MemberRole;
13
+ version: number;
14
+ }
15
+ /**
16
+ * Trusted internal reader for Auth.Access.memberships. No Console admission and
17
+ * no permission grant. Cache keys must include organization, realm and subject;
18
+ * consumers must revalidate versions or explicitly invalidate after each change.
19
+ */
20
+ export declare function organizationMembershipReader(store: OrganizationStore): MembershipReader<string, string, {
21
+ organizationId: string;
22
+ }, OrganizationMembership>;
package/dist/auth.js ADDED
@@ -0,0 +1,26 @@
1
+ import {
2
+ sameSubject2
3
+ } from "./index-kdetdk6w.js";
4
+
5
+ // src/auth.ts
6
+ function createAuthOrganizationAccess(options) {
7
+ return {
8
+ async check(actor, request) {
9
+ const verified = await options.access.enforce(actor, request, ({ principal, resource }) => options.policy(principal, resource));
10
+ return { realmId: verified.realmId, subjectId: verified.subjectId };
11
+ }
12
+ };
13
+ }
14
+ function organizationMembershipReader(store) {
15
+ return async (subject, resource, context) => {
16
+ context.signal.throwIfAborted();
17
+ const snapshot = await store.read(resource.organizationId);
18
+ context.signal.throwIfAborted();
19
+ const relationship = snapshot?.state.members.find((item) => sameSubject2(item.subject, subject));
20
+ return relationship && snapshot ? { role: relationship.role, version: snapshot.state.organization.version } : null;
21
+ };
22
+ }
23
+ export {
24
+ createAuthOrganizationAccess,
25
+ organizationMembershipReader
26
+ };
@@ -0,0 +1,3 @@
1
+ import { type OrganizationState } from "./contracts";
2
+ export declare const maximumSnapshotBytes = 512000;
3
+ export declare function assertSnapshotCapacity(state: OrganizationState): void;
@@ -0,0 +1,96 @@
1
+ /** A business subject reference, structurally compatible with Auth.SubjectRef. */
2
+ export interface SubjectRef {
3
+ readonly realmId: string;
4
+ readonly subjectId: string;
5
+ }
6
+ export type MemberRole = "member" | "admin" | "owner";
7
+ export interface Member {
8
+ subject: SubjectRef;
9
+ role: MemberRole;
10
+ joinedAt: number;
11
+ }
12
+ export interface Organization {
13
+ id: string;
14
+ name: string;
15
+ version: number;
16
+ createdAt: number;
17
+ updatedAt: number;
18
+ }
19
+ export interface Invitation {
20
+ id: string;
21
+ organizationId: string;
22
+ target: SubjectRef;
23
+ role: "member";
24
+ createdBy: SubjectRef;
25
+ createdAt: number;
26
+ expiresAt: number;
27
+ status: "pending" | "accepted" | "revoked" | "expired";
28
+ resolvedAt: number | null;
29
+ }
30
+ export interface StoredInvitation extends Omit<Invitation, "status"> {
31
+ status: "pending" | "accepted" | "revoked";
32
+ tokenDigest: string;
33
+ }
34
+ /** One CAS domain: every relationship change increments the organization version. */
35
+ export interface OrganizationState {
36
+ organization: Organization;
37
+ members: Member[];
38
+ invitations: StoredInvitation[];
39
+ }
40
+ export interface OrganizationSnapshot {
41
+ state: OrganizationState;
42
+ /** Storage-authoritative milliseconds, used for invitation expiry. */
43
+ observedAt: number;
44
+ }
45
+ export interface OrganizationStore {
46
+ create(state: OrganizationState): Promise<void>;
47
+ read(organizationId: string): Promise<OrganizationSnapshot | null>;
48
+ /**
49
+ * Atomically replace only at expectedVersion. If validUntil is supplied, check
50
+ * storage time strictly before it at the write boundary, including lock waits.
51
+ * False means no write. An exception means unknown outcome and MUST NOT retry.
52
+ */
53
+ compareAndSwap(expectedVersion: number, next: OrganizationState, validUntil?: number): Promise<boolean>;
54
+ }
55
+ export type OrganizationAction = "organization.create" | "organization.read" | "organization.update" | "member.list" | "member.add" | "member.remove" | "member.leave" | "member.role" | "owner.grant" | "owner.transfer" | "invitation.create" | "invitation.read" | "invitation.list" | "invitation.accept" | "invitation.revoke" | "invitation.prune";
56
+ export interface AccessRequest {
57
+ action: OrganizationAction;
58
+ organizationId: string | null;
59
+ /** Never includes invitation digests. Null on create or missing organization. */
60
+ organization: Organization | null;
61
+ members: readonly Member[];
62
+ target?: SubjectRef;
63
+ requestedRole?: MemberRole;
64
+ invitationId?: string;
65
+ /** Actual scoped invitation, stripped of its digest; null when missing. */
66
+ invitation?: Invitation | null;
67
+ }
68
+ /**
69
+ * Trusted integration boundary, not business JSON. Must revalidate identity,
70
+ * organization scope and action permission each call (including CAS retries).
71
+ * Cross-organization administration is permitted only by an explicit policy here.
72
+ */
73
+ export interface OrganizationAccess<Actor> {
74
+ check(actor: Actor, request: AccessRequest): Promise<SubjectRef>;
75
+ }
76
+ export interface OrganizationConfig {
77
+ invitationLifetimeMs: number;
78
+ maxMembers: number;
79
+ maxInvitations: number;
80
+ conflictRetries: number;
81
+ }
82
+ export interface MembershipChange {
83
+ organizationId: string;
84
+ version: number;
85
+ action: OrganizationAction;
86
+ subjects: SubjectRef[];
87
+ }
88
+ export interface MutationResult<T> {
89
+ value: T;
90
+ change: MembershipChange;
91
+ }
92
+ export type OrganizationErrorCode = "INVALID_INPUT" | "FORBIDDEN" | "NOT_FOUND" | "LAST_OWNER" | "ALREADY_MEMBER" | "INVITATION_ACCEPTED" | "INVITATION_REVOKED" | "INVITATION_EXPIRED" | "CONFLICT" | "CAPACITY";
93
+ export declare class OrganizationError extends Error {
94
+ readonly code: OrganizationErrorCode;
95
+ constructor(code: OrganizationErrorCode);
96
+ }
@@ -0,0 +1,3 @@
1
+ import type { DrizzleD1Database } from "drizzle-orm/d1";
2
+ import type { OrganizationStore } from "../contracts";
3
+ export declare function d1OrganizationStore<TSchema extends Record<string, unknown> = Record<string, unknown>>(db: DrizzleD1Database<TSchema>): OrganizationStore;
@@ -0,0 +1,45 @@
1
+ import {
2
+ organizations3
3
+ } from "../index-0ax8jdj3.js";
4
+ import {
5
+ assertValidVersion,
6
+ validateState,
7
+ encodeState,
8
+ decodeSnapshot
9
+ } from "../index-2bwbvaz5.js";
10
+
11
+ // src/drizzle/d1.ts
12
+ import { and, eq, sql } from "drizzle-orm";
13
+ function d1OrganizationStore(db) {
14
+ return {
15
+ async create(state) {
16
+ await db.insert(organizations3).values({
17
+ id: state.organization.id,
18
+ version: state.organization.version,
19
+ state: encodeState(state)
20
+ }).run();
21
+ },
22
+ async read(id) {
23
+ const [row] = await db.select({
24
+ id: organizations3.id,
25
+ version: organizations3.version,
26
+ state: organizations3.state,
27
+ observedAt: sql`CAST(unixepoch('subsec') * 1000 AS INTEGER)`
28
+ }).from(organizations3).where(eq(organizations3.id, id)).limit(1).all();
29
+ return row ? decodeSnapshot(row.id, row.version, row.state, Number(row.observedAt)) : null;
30
+ },
31
+ async compareAndSwap(expectedVersion, next, validUntil) {
32
+ assertValidVersion(expectedVersion, "expected version");
33
+ validateState(next);
34
+ if (next.organization.version !== expectedVersion + 1)
35
+ throw new Error("Invalid organization version transition");
36
+ if (validUntil !== undefined && !Number.isSafeInteger(validUntil))
37
+ throw new Error("Invalid organization validity deadline");
38
+ const rows = await db.update(organizations3).set({ version: next.organization.version, state: encodeState(next) }).where(and(eq(organizations3.id, next.organization.id), eq(organizations3.version, expectedVersion), validUntil === undefined ? sql`1 = 1` : sql`CAST(unixepoch('subsec') * 1000 AS INTEGER) < ${validUntil}`)).returning({ id: organizations3.id }).all();
39
+ return rows.length > 0;
40
+ }
41
+ };
42
+ }
43
+ export {
44
+ d1OrganizationStore
45
+ };
@@ -0,0 +1,3 @@
1
+ import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
2
+ import type { OrganizationStore } from "../contracts";
3
+ export declare function postgresOrganizationStore<TSchema extends Record<string, unknown> = Record<string, unknown>, TResult extends PgQueryResultHKT = PgQueryResultHKT>(db: PgDatabase<TResult, TSchema>): OrganizationStore;
@@ -0,0 +1,60 @@
1
+ import {
2
+ organizations2
3
+ } from "../index-zjdv6bc6.js";
4
+ import {
5
+ assertValidVersion,
6
+ validateState,
7
+ encodeState,
8
+ decodeSnapshot
9
+ } from "../index-2bwbvaz5.js";
10
+
11
+ // src/drizzle/pg.ts
12
+ import { and, eq, sql } from "drizzle-orm";
13
+ function postgresOrganizationStore(db) {
14
+ return {
15
+ async create(state) {
16
+ const encoded = encodeState(state);
17
+ await db.insert(organizations2).values({
18
+ id: state.organization.id,
19
+ version: state.organization.version,
20
+ state: encoded
21
+ });
22
+ },
23
+ async read(id) {
24
+ const [row] = await db.select({
25
+ id: organizations2.id,
26
+ version: organizations2.version,
27
+ state: organizations2.state,
28
+ observedAt: sql`floor(extract(epoch from clock_timestamp()) * 1000)::bigint`
29
+ }).from(organizations2).where(eq(organizations2.id, id)).limit(1);
30
+ return row ? decodeSnapshot(row.id, row.version, row.state, Number(row.observedAt)) : null;
31
+ },
32
+ async compareAndSwap(expectedVersion, next, validUntil) {
33
+ assertValidVersion(expectedVersion, "expected version");
34
+ validateState(next);
35
+ if (next.organization.version !== expectedVersion + 1) {
36
+ throw new Error("Invalid organization version transition");
37
+ }
38
+ const encoded = encodeState(next);
39
+ return db.transaction(async (tx) => {
40
+ const locked = await tx.select({ id: organizations2.id }).from(organizations2).where(eq(organizations2.id, next.organization.id)).for("update");
41
+ if (!locked.length)
42
+ return false;
43
+ const conditions = [
44
+ eq(organizations2.id, next.organization.id),
45
+ eq(organizations2.version, expectedVersion)
46
+ ];
47
+ if (validUntil !== undefined) {
48
+ if (!Number.isSafeInteger(validUntil))
49
+ throw new Error("Invalid organization validity deadline");
50
+ conditions.push(sql`floor(extract(epoch from clock_timestamp()) * 1000)::bigint < ${validUntil}::bigint`);
51
+ }
52
+ const rows = await tx.update(organizations2).set({ version: next.organization.version, state: encoded }).where(and(...conditions)).returning({ id: organizations2.id });
53
+ return rows.length > 0;
54
+ });
55
+ }
56
+ };
57
+ }
58
+ export {
59
+ postgresOrganizationStore
60
+ };
@@ -0,0 +1,58 @@
1
+ export declare const organizations: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "organizations";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "organizations";
8
+ dataType: "string";
9
+ columnType: "PgText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ version: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "version";
24
+ tableName: "organizations";
25
+ dataType: "number";
26
+ columnType: "PgBigInt53";
27
+ data: number;
28
+ driverParam: string | number;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: undefined;
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ state: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "state";
41
+ tableName: "organizations";
42
+ dataType: "string";
43
+ columnType: "PgText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
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
+ };
57
+ dialect: 'pg';
58
+ }>;
@@ -0,0 +1,6 @@
1
+ import {
2
+ organizations2
3
+ } from "../index-zjdv6bc6.js";
4
+ export {
5
+ organizations2 as organizations
6
+ };
@@ -0,0 +1,62 @@
1
+ export declare const organizations: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
+ name: "organizations";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
+ name: "id";
7
+ tableName: "organizations";
8
+ dataType: "string";
9
+ columnType: "SQLiteText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {
22
+ length: number | undefined;
23
+ }>;
24
+ version: import("drizzle-orm/sqlite-core").SQLiteColumn<{
25
+ name: "version";
26
+ tableName: "organizations";
27
+ dataType: "number";
28
+ columnType: "SQLiteInteger";
29
+ data: number;
30
+ driverParam: number;
31
+ notNull: true;
32
+ hasDefault: false;
33
+ isPrimaryKey: false;
34
+ isAutoincrement: false;
35
+ hasRuntimeDefault: false;
36
+ enumValues: undefined;
37
+ baseColumn: never;
38
+ identity: undefined;
39
+ generated: undefined;
40
+ }, {}, {}>;
41
+ state: import("drizzle-orm/sqlite-core").SQLiteColumn<{
42
+ name: "state";
43
+ tableName: "organizations";
44
+ dataType: "string";
45
+ columnType: "SQLiteText";
46
+ data: string;
47
+ driverParam: string;
48
+ notNull: true;
49
+ hasDefault: false;
50
+ isPrimaryKey: false;
51
+ isAutoincrement: false;
52
+ hasRuntimeDefault: false;
53
+ enumValues: [string, ...string[]];
54
+ baseColumn: never;
55
+ identity: undefined;
56
+ generated: undefined;
57
+ }, {}, {
58
+ length: number | undefined;
59
+ }>;
60
+ };
61
+ dialect: 'sqlite';
62
+ }>;
@@ -0,0 +1,6 @@
1
+ import {
2
+ organizations3
3
+ } from "../index-0ax8jdj3.js";
4
+ export {
5
+ organizations3 as organizations
6
+ };
@@ -0,0 +1,5 @@
1
+ import type { OrganizationSnapshot, OrganizationState } from "../contracts";
2
+ export declare function assertValidVersion(value: number, label: string): void;
3
+ export declare function validateState(state: OrganizationState): void;
4
+ export declare function encodeState(state: OrganizationState): string;
5
+ export declare function decodeSnapshot(id: string, version: number, encoded: string, observedAt: number): OrganizationSnapshot;
@@ -0,0 +1,9 @@
1
+ // src/drizzle/schema-sqlite.ts
2
+ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
3
+ var organizations3 = sqliteTable("organizations", {
4
+ id: text("id").primaryKey(),
5
+ version: integer("version").notNull(),
6
+ state: text("state").notNull()
7
+ });
8
+
9
+ export { organizations3 };