silosdk 0.0.0 → 0.0.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.
Files changed (85) hide show
  1. package/README.md +3 -1
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/cli/d1.cjs +93 -0
  4. package/dist/cli/d1.mjs +92 -0
  5. package/dist/cli/index.cjs +93 -0
  6. package/dist/cli/index.d.cts +1 -0
  7. package/dist/cli/index.d.mts +1 -0
  8. package/dist/cli/index.mjs +94 -0
  9. package/dist/cli/init.cjs +134 -0
  10. package/dist/cli/init.mjs +133 -0
  11. package/dist/cli/kv.cjs +63 -0
  12. package/dist/cli/kv.mjs +60 -0
  13. package/dist/cli/r2.cjs +83 -0
  14. package/dist/cli/r2.mjs +82 -0
  15. package/dist/cli/wrangler.cjs +93 -0
  16. package/dist/cli/wrangler.mjs +89 -0
  17. package/dist/local/adapters/cloudflare.cjs +200 -0
  18. package/dist/local/adapters/cloudflare.d.cts +50 -0
  19. package/dist/local/adapters/cloudflare.d.mts +50 -0
  20. package/dist/local/adapters/cloudflare.mjs +200 -0
  21. package/dist/local/auth-context.cjs +14 -0
  22. package/dist/local/auth-context.d.cts +7 -0
  23. package/dist/local/auth-context.d.mts +7 -0
  24. package/dist/local/auth-context.mjs +12 -0
  25. package/dist/local/auth.cjs +109 -0
  26. package/dist/local/auth.d.cts +26 -0
  27. package/dist/local/auth.d.mts +26 -0
  28. package/dist/local/auth.mjs +99 -0
  29. package/dist/local/commit.cjs +350 -0
  30. package/dist/local/commit.d.cts +59 -0
  31. package/dist/local/commit.d.mts +59 -0
  32. package/dist/local/commit.mjs +349 -0
  33. package/dist/local/config.cjs +17 -0
  34. package/dist/local/config.mjs +15 -0
  35. package/dist/local/index.cjs +16 -0
  36. package/dist/local/index.d.cts +10 -0
  37. package/dist/local/index.d.mts +10 -0
  38. package/dist/local/index.mjs +9 -0
  39. package/dist/local/provider.cjs +204 -0
  40. package/dist/local/provider.d.cts +25 -0
  41. package/dist/local/provider.d.mts +25 -0
  42. package/dist/local/provider.mjs +203 -0
  43. package/dist/local/query-store.cjs +276 -0
  44. package/dist/local/query-store.mjs +274 -0
  45. package/dist/local/storage.cjs +71 -0
  46. package/dist/local/storage.d.cts +7 -0
  47. package/dist/local/storage.d.mts +7 -0
  48. package/dist/local/storage.mjs +68 -0
  49. package/dist/local/sync.cjs +124 -0
  50. package/dist/local/sync.d.cts +36 -0
  51. package/dist/local/sync.d.mts +36 -0
  52. package/dist/local/sync.mjs +122 -0
  53. package/dist/local/view.cjs +257 -0
  54. package/dist/local/view.d.cts +24 -0
  55. package/dist/local/view.d.mts +24 -0
  56. package/dist/local/view.mjs +254 -0
  57. package/dist/package.cjs +11 -0
  58. package/dist/package.mjs +5 -0
  59. package/dist/schema/index.cjs +276 -0
  60. package/dist/schema/index.d.cts +207 -0
  61. package/dist/schema/index.d.mts +207 -0
  62. package/dist/schema/index.mjs +265 -0
  63. package/dist/server/auth.cjs +132 -0
  64. package/dist/server/auth.d.cts +49 -0
  65. package/dist/server/auth.d.mts +49 -0
  66. package/dist/server/auth.mjs +122 -0
  67. package/dist/server/d1.cjs +120 -0
  68. package/dist/server/d1.mjs +116 -0
  69. package/dist/server/do.cjs +132 -0
  70. package/dist/server/do.d.cts +21 -0
  71. package/dist/server/do.d.mts +21 -0
  72. package/dist/server/do.mjs +131 -0
  73. package/dist/server/index.cjs +355 -0
  74. package/dist/server/index.d.cts +65 -0
  75. package/dist/server/index.d.mts +65 -0
  76. package/dist/server/index.mjs +348 -0
  77. package/dist/server/protect.cjs +34 -0
  78. package/dist/server/protect.d.cts +32 -0
  79. package/dist/server/protect.d.mts +32 -0
  80. package/dist/server/protect.mjs +33 -0
  81. package/dist/server/r2.cjs +58 -0
  82. package/dist/server/r2.d.cts +4 -0
  83. package/dist/server/r2.d.mts +4 -0
  84. package/dist/server/r2.mjs +53 -0
  85. package/package.json +55 -2
@@ -0,0 +1,265 @@
1
+ import { name } from "../package.mjs";
2
+
3
+ //#region src/schema/index.ts
4
+ function issue(message) {
5
+ return { message };
6
+ }
7
+ function validateString(input, options) {
8
+ const issues = [];
9
+ if (typeof input !== "string") {
10
+ issues.push({ message: "Expected string" });
11
+ return issues;
12
+ }
13
+ if (!options) return issues;
14
+ const { minLength, maxLength } = options;
15
+ if (typeof minLength === "number" && input.length < minLength) issues.push(issue(`Must be at least ${minLength} characters`));
16
+ if (typeof maxLength === "number" && input.length > maxLength) issues.push(issue(`Must be at most ${maxLength} characters`));
17
+ return issues;
18
+ }
19
+ function validateNumber(input, options) {
20
+ const issues = [];
21
+ if (typeof input !== "number") {
22
+ issues.push({ message: "Expected number" });
23
+ return issues;
24
+ }
25
+ if (!options) return issues;
26
+ const { min, max, integer } = options;
27
+ if (typeof min === "number" && input < min) issues.push(issue(`Must be at least ${min}`));
28
+ if (typeof max === "number" && input > max) issues.push(issue(`Must be at most ${max}`));
29
+ if (integer && !Number.isInteger(input)) issues.push(issue("Must be an integer"));
30
+ return issues;
31
+ }
32
+ function validateAsset(input) {
33
+ const issues = [];
34
+ if (typeof input !== "object" || input === null) {
35
+ issues.push({ message: "Expected asset object" });
36
+ return issues;
37
+ }
38
+ const hasKey = typeof input.key === "string" && input.key.length > 0;
39
+ const hasUri = typeof input.uri === "string" && input.uri.length > 0;
40
+ if (!hasKey && !hasUri) issues.push({ message: "Expected either non-empty key or uri" });
41
+ if (input.name !== void 0 && typeof input.name !== "string") issues.push({ message: "name must be a string" });
42
+ if (input.contentType !== void 0 && typeof input.contentType !== "string") issues.push({ message: "contentType must be a string" });
43
+ if (input.size !== void 0 && typeof input.size !== "number") issues.push({ message: "size must be a number" });
44
+ return issues;
45
+ }
46
+ function fieldSchema(type, options) {
47
+ function parse(value) {
48
+ return value;
49
+ }
50
+ function validate(input) {
51
+ const issues = [];
52
+ if (Array.isArray(type)) {
53
+ if (!type.includes(input)) {
54
+ issues.push({ message: `Expected one of: ${type.join(", ")}` });
55
+ return { issues };
56
+ }
57
+ return { value: parse(input) };
58
+ }
59
+ const def = options?.default;
60
+ if ((input === void 0 || input === null) && def !== void 0) input = typeof def === "function" ? def() : def;
61
+ switch (type) {
62
+ case "string":
63
+ issues.push(...validateString(input, options));
64
+ break;
65
+ case "number":
66
+ issues.push(...validateNumber(input, options));
67
+ break;
68
+ case "boolean":
69
+ if (typeof input !== "boolean") issues.push({ message: "Expected boolean" });
70
+ break;
71
+ case "asset":
72
+ issues.push(...validateAsset(input));
73
+ break;
74
+ }
75
+ if (issues.length > 0) return { issues };
76
+ else return { value: parse(input) };
77
+ }
78
+ return {
79
+ type,
80
+ options,
81
+ hasDefault: Boolean(options && options.default !== void 0),
82
+ validate,
83
+ "~standard": {
84
+ version: 1,
85
+ vendor: name,
86
+ validate
87
+ }
88
+ };
89
+ }
90
+ function string(options) {
91
+ return fieldSchema("string", options);
92
+ }
93
+ function number(options) {
94
+ return fieldSchema("number", options);
95
+ }
96
+ function boolean(options) {
97
+ return fieldSchema("boolean", options);
98
+ }
99
+ function asset(options) {
100
+ return fieldSchema("asset", options);
101
+ }
102
+ function literal(values, options) {
103
+ return fieldSchema(values, options);
104
+ }
105
+ function viewSchema(type) {
106
+ function parse(value) {
107
+ return value;
108
+ }
109
+ function validate(input) {
110
+ const issues = [];
111
+ if (typeof input !== "object" || input === null) {
112
+ issues.push({ message: "Expected object" });
113
+ return { issues };
114
+ }
115
+ const output = {};
116
+ Object.entries(type).forEach(([key, value]) => {
117
+ if (key in input) {
118
+ const res = value.validate(input[key]);
119
+ if ("issues" in res) res.issues?.forEach((issue$1) => {
120
+ issues.push({ message: `${key}: ${issue$1.message}` });
121
+ });
122
+ else output[key] = res.value;
123
+ return;
124
+ }
125
+ if (value.hasDefault) {
126
+ const res = value.validate(void 0);
127
+ if ("issues" in res) res.issues?.forEach((issue$1) => {
128
+ issues.push({ message: `${key}: ${issue$1.message}` });
129
+ });
130
+ else output[key] = res.value;
131
+ }
132
+ });
133
+ return issues.length > 0 ? { issues } : { value: parse(output) };
134
+ }
135
+ return {
136
+ type,
137
+ validate,
138
+ "~standard": {
139
+ version: 1,
140
+ vendor: name,
141
+ validate
142
+ }
143
+ };
144
+ }
145
+ function view(name$1, type) {
146
+ const schema = viewSchema(type);
147
+ const init = () => {
148
+ const value = {};
149
+ Object.keys(schema.type).forEach((key) => {
150
+ const field = schema.type[key];
151
+ if (field.options?.default !== void 0) {
152
+ const def = field.options.default;
153
+ value[key] = typeof def === "function" ? def() : def;
154
+ }
155
+ });
156
+ return value;
157
+ };
158
+ return {
159
+ name: name$1,
160
+ schema,
161
+ validate: schema["~standard"].validate,
162
+ init
163
+ };
164
+ }
165
+ function jex(field) {
166
+ return `json_extract(data, '$.${field}')`;
167
+ }
168
+ function stableStringify(obj) {
169
+ if (obj === null || obj === void 0) return "";
170
+ if (typeof obj !== "object") return String(obj);
171
+ if (Array.isArray(obj)) return obj.map(stableStringify).join(",");
172
+ return Object.keys(obj).sort().map((k) => `${k}:${stableStringify(obj[k])}`).join("|");
173
+ }
174
+ function compileWhere(where) {
175
+ const params = [];
176
+ const compileObject = (obj) => {
177
+ const parts = [];
178
+ for (const field of Object.keys(obj)) {
179
+ const value = obj[field];
180
+ if (value && Array.isArray(value)) {
181
+ const placeholders = value.map(() => "?").join(", ");
182
+ parts.push(`${jex(field)} IN (${placeholders})`);
183
+ params.push(...value);
184
+ } else if (value && typeof value === "object" && !Array.isArray(value)) for (const op of Object.keys(value)) {
185
+ parts.push(`${jex(field)} ${op} ?`);
186
+ params.push(value[op]);
187
+ }
188
+ else {
189
+ parts.push(`${jex(field)} = ?`);
190
+ params.push(value);
191
+ }
192
+ }
193
+ return parts.join(" AND ");
194
+ };
195
+ if (Array.isArray(where)) return {
196
+ clause: where.map((option) => `(${compileObject(option)})`).join(" OR "),
197
+ params
198
+ };
199
+ else return {
200
+ clause: compileObject(where),
201
+ params
202
+ };
203
+ }
204
+ async function getRows(args) {
205
+ const { name: name$1, options, db } = args;
206
+ let statement = `SELECT * FROM views WHERE view = ?`;
207
+ const params = [name$1];
208
+ const ids = [];
209
+ if (options && "parent" in options && options.parent) (await db.getAllAsync("SELECT * FROM relations WHERE parent = ? AND pid = ? AND child = ?", [
210
+ options?.parent?.[0]?.name,
211
+ options.parent[1],
212
+ name$1
213
+ ])).forEach((relation) => {
214
+ ids.push(relation.cid);
215
+ });
216
+ if (options && "child" in options && options.child) (await db.getAllAsync("SELECT * FROM relations WHERE child = ? AND cid = ? AND parent = ?", [
217
+ options.child[0].name,
218
+ options.child[1],
219
+ name$1
220
+ ])).forEach((relation) => {
221
+ ids.push(relation.pid);
222
+ });
223
+ if (ids.length > 0) {
224
+ statement += ` AND id IN (${ids.map(() => "?").join(",")})`;
225
+ params.push(...ids);
226
+ }
227
+ if (options.where) {
228
+ const { clause, params: whereParams } = compileWhere(options.where);
229
+ statement += ` AND (${clause})`;
230
+ params.push(...whereParams);
231
+ }
232
+ if (options.order) {
233
+ const orderClauses = [];
234
+ for (const [field, direction] of Object.entries(options.order)) orderClauses.push(`${jex(field)} ${direction?.toUpperCase() ?? "ASC"}`);
235
+ if (orderClauses.length > 0) statement += ` ORDER BY ${orderClauses.join(", ")}`;
236
+ }
237
+ if (options.take) {
238
+ statement += ` LIMIT ?`;
239
+ params.push(options.take);
240
+ if (args.offset) {
241
+ statement += ` OFFSET ?`;
242
+ params.push(args.offset);
243
+ }
244
+ }
245
+ return await db.getAllAsync(statement, params).then((rows) => rows.map((row) => ({
246
+ ...row,
247
+ data: JSON.parse(row.data)
248
+ }))).catch((err) => {
249
+ console.error("DB Query Error:", err);
250
+ }) || [];
251
+ }
252
+ async function getRow(args) {
253
+ const { name: name$1, options, db } = args;
254
+ const statement = `SELECT * FROM views WHERE view = ? AND id = ?`;
255
+ const params = [name$1, options.id];
256
+ return await db.getFirstAsync(statement, params).then((row) => row ? {
257
+ ...row,
258
+ data: JSON.parse(row.data)
259
+ } : null).catch((err) => {
260
+ console.error("DB Query Error:", err);
261
+ }) ?? null;
262
+ }
263
+
264
+ //#endregion
265
+ export { asset, boolean, fieldSchema, getRow, getRows, jex, literal, number, stableStringify, string, view, viewSchema };
@@ -0,0 +1,132 @@
1
+
2
+ //#region src/server/auth.ts
3
+ const DEFAULT_SESSION_DURATION = 3600 * 24 * 30;
4
+ function otpProvider(config) {
5
+ return {
6
+ _type: "otp",
7
+ config
8
+ };
9
+ }
10
+ function customProvider(fn) {
11
+ return {
12
+ _type: "custom",
13
+ resolve: fn
14
+ };
15
+ }
16
+ function createAuth(config) {
17
+ return {
18
+ _providers: config.providers,
19
+ _roles: config.roles,
20
+ _defaultRole: config.defaultRole ?? "user",
21
+ _sessionDuration: config.sessionDuration ?? DEFAULT_SESSION_DURATION
22
+ };
23
+ }
24
+ function randomDigits(n) {
25
+ const bytes = new Uint8Array(Math.ceil(n * 1.5));
26
+ crypto.getRandomValues(bytes);
27
+ return Array.from(bytes).map((b) => b % 10).join("").slice(0, n);
28
+ }
29
+ async function randomToken() {
30
+ const bytes = new Uint8Array(32);
31
+ crypto.getRandomValues(bytes);
32
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
33
+ }
34
+ async function findOrCreateUser(db, email, opts) {
35
+ const existing = await db.prepare(`SELECT id, email, role, createdAt FROM silo_users WHERE email = ?`).bind(email).first();
36
+ if (existing) return existing;
37
+ const id = await randomToken();
38
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
39
+ const role = opts?.role ?? "user";
40
+ await db.prepare(`INSERT INTO silo_users (id, email, role, createdAt) VALUES (?, ?, ?, ?)`).bind(id, email, role, createdAt).run();
41
+ return {
42
+ id,
43
+ email,
44
+ role,
45
+ createdAt
46
+ };
47
+ }
48
+ async function createAnonymousUser(db, kv, durationSeconds = DEFAULT_SESSION_DURATION, defaultRole = "user") {
49
+ const id = await randomToken();
50
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
51
+ const role = defaultRole;
52
+ await db.prepare(`INSERT INTO silo_users (id, email, role, createdAt) VALUES (?, NULL, ?, ?)`).bind(id, role, createdAt).run();
53
+ const token = await randomToken();
54
+ const payload = {
55
+ userId: id,
56
+ email: null,
57
+ role,
58
+ isAnonymous: true,
59
+ expiresAt: new Date(Date.now() + durationSeconds * 1e3).toISOString()
60
+ };
61
+ await kv.put(`session:${token}`, JSON.stringify(payload), { expirationTtl: durationSeconds });
62
+ return {
63
+ token,
64
+ userId: id
65
+ };
66
+ }
67
+ const OTP_TTL_SECONDS = 600;
68
+ async function createOtp(db, email) {
69
+ const code = randomDigits(6);
70
+ const expiresAt = new Date(Date.now() + OTP_TTL_SECONDS * 1e3).toISOString();
71
+ await db.prepare(`INSERT INTO silo_otps (email, code, expiresAt)
72
+ VALUES (?, ?, ?)
73
+ ON CONFLICT(email) DO UPDATE SET
74
+ code = excluded.code,
75
+ expiresAt = excluded.expiresAt`).bind(email, code, expiresAt).run();
76
+ return code;
77
+ }
78
+ async function verifyOtp(db, email, code) {
79
+ const row = await db.prepare(`SELECT code, expiresAt FROM silo_otps WHERE email = ?`).bind(email).first();
80
+ if (!row) return { valid: false };
81
+ if (row.code !== code) return { valid: false };
82
+ if (new Date(row.expiresAt) < /* @__PURE__ */ new Date()) return { valid: false };
83
+ await db.prepare(`DELETE FROM silo_otps WHERE email = ?`).bind(email).run();
84
+ return { valid: true };
85
+ }
86
+ async function createSession(kv, userId, email, role, durationSeconds = DEFAULT_SESSION_DURATION) {
87
+ const token = await randomToken();
88
+ const payload = {
89
+ userId,
90
+ email,
91
+ role,
92
+ isAnonymous: false,
93
+ expiresAt: new Date(Date.now() + durationSeconds * 1e3).toISOString()
94
+ };
95
+ await kv.put(`session:${token}`, JSON.stringify(payload), { expirationTtl: durationSeconds });
96
+ return token;
97
+ }
98
+ async function resolveSession(kv, token) {
99
+ const raw = await kv.get(`session:${token}`);
100
+ if (!raw) return null;
101
+ let payload;
102
+ try {
103
+ payload = JSON.parse(raw);
104
+ } catch {
105
+ return null;
106
+ }
107
+ if (new Date(payload.expiresAt) < /* @__PURE__ */ new Date()) {
108
+ await kv.delete(`session:${token}`);
109
+ return null;
110
+ }
111
+ return {
112
+ id: payload.userId,
113
+ email: payload.email,
114
+ role: payload.role,
115
+ isAnonymous: payload.isAnonymous
116
+ };
117
+ }
118
+ async function deleteSession(kv, token) {
119
+ await kv.delete(`session:${token}`);
120
+ }
121
+
122
+ //#endregion
123
+ exports.createAnonymousUser = createAnonymousUser;
124
+ exports.createAuth = createAuth;
125
+ exports.createOtp = createOtp;
126
+ exports.createSession = createSession;
127
+ exports.customProvider = customProvider;
128
+ exports.deleteSession = deleteSession;
129
+ exports.findOrCreateUser = findOrCreateUser;
130
+ exports.otpProvider = otpProvider;
131
+ exports.resolveSession = resolveSession;
132
+ exports.verifyOtp = verifyOtp;
@@ -0,0 +1,49 @@
1
+ //#region src/server/auth.d.ts
2
+ type OtpProviderConfig = {
3
+ sendOTP: (to: string, code: string, env: unknown) => Promise<void>;
4
+ };
5
+ type OtpProvider = {
6
+ _type: 'otp';
7
+ config: OtpProviderConfig;
8
+ };
9
+ type CustomProvider = {
10
+ _type: 'custom';
11
+ resolve: (token: string) => Promise<{
12
+ id: string;
13
+ role?: string;
14
+ } | null>;
15
+ };
16
+ type ViewPermissions = {
17
+ read: boolean;
18
+ write: boolean;
19
+ };
20
+ type RoleDefinition = {
21
+ default: ViewPermissions;
22
+ [viewName: string]: ViewPermissions;
23
+ };
24
+ type RolesConfig = Record<string, RoleDefinition>;
25
+ type CreateAuthConfig = {
26
+ providers: OtpProvider[] | CustomProvider;
27
+ roles?: RolesConfig;
28
+ defaultRole?: string;
29
+ sessionDuration?: number;
30
+ };
31
+ type AuthInstance = {
32
+ _providers: OtpProvider[] | CustomProvider;
33
+ _roles: RolesConfig | undefined;
34
+ _defaultRole: string;
35
+ _sessionDuration: number;
36
+ };
37
+ declare function otpProvider(config: OtpProviderConfig): OtpProvider;
38
+ declare function customProvider(fn: (token: string) => Promise<{
39
+ id: string;
40
+ } | null>): CustomProvider;
41
+ declare function createAuth(config: CreateAuthConfig): AuthInstance;
42
+ declare function resolveSession(kv: KVNamespace, token: string): Promise<{
43
+ id: string;
44
+ email: string | null;
45
+ role: string;
46
+ isAnonymous: boolean;
47
+ } | null>;
48
+ //#endregion
49
+ export { AuthInstance, RoleDefinition, RolesConfig, ViewPermissions, createAuth, customProvider, otpProvider, resolveSession };
@@ -0,0 +1,49 @@
1
+ //#region src/server/auth.d.ts
2
+ type OtpProviderConfig = {
3
+ sendOTP: (to: string, code: string, env: unknown) => Promise<void>;
4
+ };
5
+ type OtpProvider = {
6
+ _type: 'otp';
7
+ config: OtpProviderConfig;
8
+ };
9
+ type CustomProvider = {
10
+ _type: 'custom';
11
+ resolve: (token: string) => Promise<{
12
+ id: string;
13
+ role?: string;
14
+ } | null>;
15
+ };
16
+ type ViewPermissions = {
17
+ read: boolean;
18
+ write: boolean;
19
+ };
20
+ type RoleDefinition = {
21
+ default: ViewPermissions;
22
+ [viewName: string]: ViewPermissions;
23
+ };
24
+ type RolesConfig = Record<string, RoleDefinition>;
25
+ type CreateAuthConfig = {
26
+ providers: OtpProvider[] | CustomProvider;
27
+ roles?: RolesConfig;
28
+ defaultRole?: string;
29
+ sessionDuration?: number;
30
+ };
31
+ type AuthInstance = {
32
+ _providers: OtpProvider[] | CustomProvider;
33
+ _roles: RolesConfig | undefined;
34
+ _defaultRole: string;
35
+ _sessionDuration: number;
36
+ };
37
+ declare function otpProvider(config: OtpProviderConfig): OtpProvider;
38
+ declare function customProvider(fn: (token: string) => Promise<{
39
+ id: string;
40
+ } | null>): CustomProvider;
41
+ declare function createAuth(config: CreateAuthConfig): AuthInstance;
42
+ declare function resolveSession(kv: KVNamespace, token: string): Promise<{
43
+ id: string;
44
+ email: string | null;
45
+ role: string;
46
+ isAnonymous: boolean;
47
+ } | null>;
48
+ //#endregion
49
+ export { AuthInstance, RoleDefinition, RolesConfig, ViewPermissions, createAuth, customProvider, otpProvider, resolveSession };
@@ -0,0 +1,122 @@
1
+ //#region src/server/auth.ts
2
+ const DEFAULT_SESSION_DURATION = 3600 * 24 * 30;
3
+ function otpProvider(config) {
4
+ return {
5
+ _type: "otp",
6
+ config
7
+ };
8
+ }
9
+ function customProvider(fn) {
10
+ return {
11
+ _type: "custom",
12
+ resolve: fn
13
+ };
14
+ }
15
+ function createAuth(config) {
16
+ return {
17
+ _providers: config.providers,
18
+ _roles: config.roles,
19
+ _defaultRole: config.defaultRole ?? "user",
20
+ _sessionDuration: config.sessionDuration ?? DEFAULT_SESSION_DURATION
21
+ };
22
+ }
23
+ function randomDigits(n) {
24
+ const bytes = new Uint8Array(Math.ceil(n * 1.5));
25
+ crypto.getRandomValues(bytes);
26
+ return Array.from(bytes).map((b) => b % 10).join("").slice(0, n);
27
+ }
28
+ async function randomToken() {
29
+ const bytes = new Uint8Array(32);
30
+ crypto.getRandomValues(bytes);
31
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
32
+ }
33
+ async function findOrCreateUser(db, email, opts) {
34
+ const existing = await db.prepare(`SELECT id, email, role, createdAt FROM silo_users WHERE email = ?`).bind(email).first();
35
+ if (existing) return existing;
36
+ const id = await randomToken();
37
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
38
+ const role = opts?.role ?? "user";
39
+ await db.prepare(`INSERT INTO silo_users (id, email, role, createdAt) VALUES (?, ?, ?, ?)`).bind(id, email, role, createdAt).run();
40
+ return {
41
+ id,
42
+ email,
43
+ role,
44
+ createdAt
45
+ };
46
+ }
47
+ async function createAnonymousUser(db, kv, durationSeconds = DEFAULT_SESSION_DURATION, defaultRole = "user") {
48
+ const id = await randomToken();
49
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
50
+ const role = defaultRole;
51
+ await db.prepare(`INSERT INTO silo_users (id, email, role, createdAt) VALUES (?, NULL, ?, ?)`).bind(id, role, createdAt).run();
52
+ const token = await randomToken();
53
+ const payload = {
54
+ userId: id,
55
+ email: null,
56
+ role,
57
+ isAnonymous: true,
58
+ expiresAt: new Date(Date.now() + durationSeconds * 1e3).toISOString()
59
+ };
60
+ await kv.put(`session:${token}`, JSON.stringify(payload), { expirationTtl: durationSeconds });
61
+ return {
62
+ token,
63
+ userId: id
64
+ };
65
+ }
66
+ const OTP_TTL_SECONDS = 600;
67
+ async function createOtp(db, email) {
68
+ const code = randomDigits(6);
69
+ const expiresAt = new Date(Date.now() + OTP_TTL_SECONDS * 1e3).toISOString();
70
+ await db.prepare(`INSERT INTO silo_otps (email, code, expiresAt)
71
+ VALUES (?, ?, ?)
72
+ ON CONFLICT(email) DO UPDATE SET
73
+ code = excluded.code,
74
+ expiresAt = excluded.expiresAt`).bind(email, code, expiresAt).run();
75
+ return code;
76
+ }
77
+ async function verifyOtp(db, email, code) {
78
+ const row = await db.prepare(`SELECT code, expiresAt FROM silo_otps WHERE email = ?`).bind(email).first();
79
+ if (!row) return { valid: false };
80
+ if (row.code !== code) return { valid: false };
81
+ if (new Date(row.expiresAt) < /* @__PURE__ */ new Date()) return { valid: false };
82
+ await db.prepare(`DELETE FROM silo_otps WHERE email = ?`).bind(email).run();
83
+ return { valid: true };
84
+ }
85
+ async function createSession(kv, userId, email, role, durationSeconds = DEFAULT_SESSION_DURATION) {
86
+ const token = await randomToken();
87
+ const payload = {
88
+ userId,
89
+ email,
90
+ role,
91
+ isAnonymous: false,
92
+ expiresAt: new Date(Date.now() + durationSeconds * 1e3).toISOString()
93
+ };
94
+ await kv.put(`session:${token}`, JSON.stringify(payload), { expirationTtl: durationSeconds });
95
+ return token;
96
+ }
97
+ async function resolveSession(kv, token) {
98
+ const raw = await kv.get(`session:${token}`);
99
+ if (!raw) return null;
100
+ let payload;
101
+ try {
102
+ payload = JSON.parse(raw);
103
+ } catch {
104
+ return null;
105
+ }
106
+ if (new Date(payload.expiresAt) < /* @__PURE__ */ new Date()) {
107
+ await kv.delete(`session:${token}`);
108
+ return null;
109
+ }
110
+ return {
111
+ id: payload.userId,
112
+ email: payload.email,
113
+ role: payload.role,
114
+ isAnonymous: payload.isAnonymous
115
+ };
116
+ }
117
+ async function deleteSession(kv, token) {
118
+ await kv.delete(`session:${token}`);
119
+ }
120
+
121
+ //#endregion
122
+ export { createAnonymousUser, createAuth, createOtp, createSession, customProvider, deleteSession, findOrCreateUser, otpProvider, resolveSession, verifyOtp };