@glinr/theauth 0.4.2 → 0.6.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.
@@ -93,7 +93,7 @@ function headerAuth(options) {
93
93
  }
94
94
  };
95
95
  }
96
- var users = sqliteTable("kavach_users", {
96
+ var users = sqliteTable("theauth_users", {
97
97
  id: text("id").primaryKey(),
98
98
  email: text("email").notNull().unique(),
99
99
  name: text("name"),
@@ -109,14 +109,14 @@ var users = sqliteTable("kavach_users", {
109
109
  banExpiresAt: integer("ban_expires_at", { mode: "timestamp" }),
110
110
  forcePasswordReset: integer("force_password_reset").notNull().default(0),
111
111
  emailVerified: integer("email_verified").notNull().default(0),
112
- // Stripe integration fields (populated by kavach-stripe plugin)
112
+ // Stripe integration fields (populated by theauth-stripe plugin)
113
113
  stripeCustomerId: text("stripe_customer_id").unique(),
114
114
  stripeSubscriptionId: text("stripe_subscription_id"),
115
115
  stripeSubscriptionStatus: text("stripe_subscription_status"),
116
116
  stripePriceId: text("stripe_price_id"),
117
117
  stripeCurrentPeriodEnd: integer("stripe_current_period_end", { mode: "timestamp" }),
118
118
  stripeCancelAtPeriodEnd: integer("stripe_cancel_at_period_end", { mode: "boolean" }).notNull().default(false),
119
- // Polar integration fields (populated by kavach-polar plugin)
119
+ // Polar integration fields (populated by theauth-polar plugin)
120
120
  polarCustomerId: text("polar_customer_id").unique(),
121
121
  polarSubscriptionId: text("polar_subscription_id"),
122
122
  polarSubscriptionStatus: text("polar_subscription_status"),
@@ -126,7 +126,7 @@ var users = sqliteTable("kavach_users", {
126
126
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
127
127
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
128
128
  });
129
- var tenants = sqliteTable("kavach_tenants", {
129
+ var tenants = sqliteTable("theauth_tenants", {
130
130
  id: text("id").primaryKey(),
131
131
  name: text("name").notNull(),
132
132
  slug: text("slug").notNull().unique(),
@@ -135,7 +135,7 @@ var tenants = sqliteTable("kavach_tenants", {
135
135
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
136
136
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
137
137
  });
138
- var agents = sqliteTable("kavach_agents", {
138
+ var agents = sqliteTable("theauth_agents", {
139
139
  id: text("id").primaryKey(),
140
140
  ownerId: text("owner_id").notNull().references(() => users.id),
141
141
  tenantId: text("tenant_id").references(() => tenants.id),
@@ -153,7 +153,7 @@ var agents = sqliteTable("kavach_agents", {
153
153
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
154
154
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
155
155
  });
156
- var permissions = sqliteTable("kavach_permissions", {
156
+ var permissions = sqliteTable("theauth_permissions", {
157
157
  id: text("id").primaryKey(),
158
158
  agentId: text("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
159
159
  resource: text("resource").notNull(),
@@ -165,7 +165,7 @@ var permissions = sqliteTable("kavach_permissions", {
165
165
  relation: text("relation"),
166
166
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
167
167
  });
168
- var delegationChains = sqliteTable("kavach_delegation_chains", {
168
+ var delegationChains = sqliteTable("theauth_delegation_chains", {
169
169
  id: text("id").primaryKey(),
170
170
  fromAgentId: text("from_agent_id").notNull().references(() => agents.id),
171
171
  toAgentId: text("to_agent_id").notNull().references(() => agents.id),
@@ -176,7 +176,7 @@ var delegationChains = sqliteTable("kavach_delegation_chains", {
176
176
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
177
177
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
178
178
  });
179
- var auditLogs = sqliteTable("kavach_audit_logs", {
179
+ var auditLogs = sqliteTable("theauth_audit_logs", {
180
180
  id: text("id").primaryKey(),
181
181
  agentId: text("agent_id").notNull().references(() => agents.id),
182
182
  userId: text("user_id").notNull().references(() => users.id),
@@ -196,14 +196,14 @@ var auditLogs = sqliteTable("kavach_audit_logs", {
196
196
  cacheHit: integer("cache_hit", { mode: "boolean" }).notNull().default(false),
197
197
  timestamp: integer("timestamp", { mode: "timestamp" }).notNull()
198
198
  });
199
- sqliteTable("kavach_rate_limits", {
199
+ sqliteTable("theauth_rate_limits", {
200
200
  id: text("id").primaryKey(),
201
201
  agentId: text("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
202
202
  resource: text("resource").notNull(),
203
203
  windowStart: integer("window_start", { mode: "timestamp" }).notNull(),
204
204
  count: integer("count").notNull().default(0)
205
205
  });
206
- sqliteTable("kavach_mcp_servers", {
206
+ sqliteTable("theauth_mcp_servers", {
207
207
  id: text("id").primaryKey(),
208
208
  name: text("name").notNull(),
209
209
  endpoint: text("endpoint").notNull().unique(),
@@ -214,14 +214,14 @@ sqliteTable("kavach_mcp_servers", {
214
214
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
215
215
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
216
216
  });
217
- var sessions = sqliteTable("kavach_sessions", {
217
+ var sessions = sqliteTable("theauth_sessions", {
218
218
  id: text("id").primaryKey(),
219
219
  userId: text("user_id").notNull().references(() => users.id),
220
220
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
221
221
  metadata: text("metadata", { mode: "json" }).$type(),
222
222
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
223
223
  });
224
- var oauthClients = sqliteTable("kavach_oauth_clients", {
224
+ var oauthClients = sqliteTable("theauth_oauth_clients", {
225
225
  id: text("id").primaryKey(),
226
226
  clientId: text("client_id").notNull().unique(),
227
227
  clientSecret: text("client_secret"),
@@ -238,7 +238,7 @@ var oauthClients = sqliteTable("kavach_oauth_clients", {
238
238
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
239
239
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
240
240
  });
241
- var oauthAccessTokens = sqliteTable("kavach_oauth_access_tokens", {
241
+ var oauthAccessTokens = sqliteTable("theauth_oauth_access_tokens", {
242
242
  id: text("id").primaryKey(),
243
243
  accessToken: text("access_token").notNull().unique(),
244
244
  refreshToken: text("refresh_token").unique(),
@@ -252,7 +252,7 @@ var oauthAccessTokens = sqliteTable("kavach_oauth_access_tokens", {
252
252
  refreshTokenExpiresAt: integer("refresh_token_expires_at", { mode: "timestamp" }),
253
253
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
254
254
  });
255
- var oauthAuthorizationCodes = sqliteTable("kavach_oauth_authorization_codes", {
255
+ var oauthAuthorizationCodes = sqliteTable("theauth_oauth_authorization_codes", {
256
256
  id: text("id").primaryKey(),
257
257
  code: text("code").notNull().unique(),
258
258
  clientId: text("client_id").notNull().references(() => oauthClients.clientId),
@@ -268,7 +268,7 @@ var oauthAuthorizationCodes = sqliteTable("kavach_oauth_authorization_codes", {
268
268
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
269
269
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
270
270
  });
271
- var budgetPolicies = sqliteTable("kavach_budget_policies", {
271
+ var budgetPolicies = sqliteTable("theauth_budget_policies", {
272
272
  id: text("id").primaryKey(),
273
273
  agentId: text("agent_id").references(() => agents.id, { onDelete: "cascade" }),
274
274
  // nullable
@@ -282,7 +282,7 @@ var budgetPolicies = sqliteTable("kavach_budget_policies", {
282
282
  status: text("status", { enum: ["active", "triggered", "disabled"] }).notNull().default("active"),
283
283
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
284
284
  });
285
- sqliteTable("kavach_agent_cards", {
285
+ sqliteTable("theauth_agent_cards", {
286
286
  id: text("id").primaryKey(),
287
287
  agentId: text("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
288
288
  name: text("name").notNull(),
@@ -296,7 +296,7 @@ sqliteTable("kavach_agent_cards", {
296
296
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
297
297
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
298
298
  });
299
- var approvalRequests = sqliteTable("kavach_approval_requests", {
299
+ var approvalRequests = sqliteTable("theauth_approval_requests", {
300
300
  id: text("id").primaryKey(),
301
301
  agentId: text("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
302
302
  userId: text("user_id").notNull().references(() => users.id),
@@ -309,7 +309,7 @@ var approvalRequests = sqliteTable("kavach_approval_requests", {
309
309
  respondedBy: text("responded_by"),
310
310
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
311
311
  });
312
- sqliteTable("kavach_trust_scores", {
312
+ sqliteTable("theauth_trust_scores", {
313
313
  agentId: text("agent_id").primaryKey().references(() => agents.id, { onDelete: "cascade" }),
314
314
  score: integer("score").notNull(),
315
315
  level: text("level", {
@@ -318,7 +318,7 @@ sqliteTable("kavach_trust_scores", {
318
318
  factors: text("factors", { mode: "json" }).notNull().$type(),
319
319
  computedAt: integer("computed_at", { mode: "timestamp" }).notNull()
320
320
  });
321
- var magicLinks = sqliteTable("kavach_magic_links", {
321
+ var magicLinks = sqliteTable("theauth_magic_links", {
322
322
  id: text("id").primaryKey(),
323
323
  email: text("email").notNull(),
324
324
  token: text("token").notNull().unique(),
@@ -326,7 +326,7 @@ var magicLinks = sqliteTable("kavach_magic_links", {
326
326
  used: integer("used", { mode: "boolean" }).notNull().default(false),
327
327
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
328
328
  });
329
- var emailOtps = sqliteTable("kavach_email_otps", {
329
+ var emailOtps = sqliteTable("theauth_email_otps", {
330
330
  id: text("id").primaryKey(),
331
331
  email: text("email").notNull(),
332
332
  codeHash: text("code_hash").notNull(),
@@ -334,7 +334,7 @@ var emailOtps = sqliteTable("kavach_email_otps", {
334
334
  attempts: integer("attempts").notNull().default(0),
335
335
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
336
336
  });
337
- var totpRecords = sqliteTable("kavach_totp", {
337
+ var totpRecords = sqliteTable("theauth_totp", {
338
338
  userId: text("user_id").primaryKey().references(() => users.id),
339
339
  secret: text("secret").notNull(),
340
340
  // base32-encoded TOTP secret
@@ -343,7 +343,7 @@ var totpRecords = sqliteTable("kavach_totp", {
343
343
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
344
344
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
345
345
  });
346
- var organizations = sqliteTable("kavach_organizations", {
346
+ var organizations = sqliteTable("theauth_organizations", {
347
347
  id: text("id").primaryKey(),
348
348
  name: text("name").notNull(),
349
349
  slug: text("slug").notNull().unique(),
@@ -352,14 +352,14 @@ var organizations = sqliteTable("kavach_organizations", {
352
352
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
353
353
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
354
354
  });
355
- var orgMembers = sqliteTable("kavach_org_members", {
355
+ var orgMembers = sqliteTable("theauth_org_members", {
356
356
  id: text("id").primaryKey(),
357
357
  orgId: text("org_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
358
358
  userId: text("user_id").notNull().references(() => users.id),
359
359
  role: text("role").notNull().default("member"),
360
360
  joinedAt: integer("joined_at", { mode: "timestamp" }).notNull()
361
361
  });
362
- var orgInvitations = sqliteTable("kavach_org_invitations", {
362
+ var orgInvitations = sqliteTable("theauth_org_invitations", {
363
363
  id: text("id").primaryKey(),
364
364
  orgId: text("org_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
365
365
  email: text("email").notNull(),
@@ -369,13 +369,13 @@ var orgInvitations = sqliteTable("kavach_org_invitations", {
369
369
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
370
370
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
371
371
  });
372
- var orgRoles = sqliteTable("kavach_org_roles", {
372
+ var orgRoles = sqliteTable("theauth_org_roles", {
373
373
  id: text("id").primaryKey(),
374
374
  orgId: text("org_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
375
375
  name: text("name").notNull(),
376
376
  permissions: text("permissions", { mode: "json" }).notNull().$type()
377
377
  });
378
- var passkeyCredentials = sqliteTable("kavach_passkey_credentials", {
378
+ var passkeyCredentials = sqliteTable("theauth_passkey_credentials", {
379
379
  id: text("id").primaryKey(),
380
380
  userId: text("user_id").notNull().references(() => users.id),
381
381
  credentialId: text("credential_id").notNull().unique(),
@@ -388,7 +388,7 @@ var passkeyCredentials = sqliteTable("kavach_passkey_credentials", {
388
388
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
389
389
  lastUsedAt: integer("last_used_at", { mode: "timestamp" }).notNull()
390
390
  });
391
- var ssoConnections = sqliteTable("kavach_sso_connections", {
391
+ var ssoConnections = sqliteTable("theauth_sso_connections", {
392
392
  id: text("id").primaryKey(),
393
393
  orgId: text("org_id").notNull(),
394
394
  providerId: text("provider_id").notNull(),
@@ -397,7 +397,7 @@ var ssoConnections = sqliteTable("kavach_sso_connections", {
397
397
  enabled: integer("enabled").notNull().default(1),
398
398
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
399
399
  });
400
- var apiKeys = sqliteTable("kavach_api_keys", {
400
+ var apiKeys = sqliteTable("theauth_api_keys", {
401
401
  id: text("id").primaryKey(),
402
402
  userId: text("user_id").notNull().references(() => users.id),
403
403
  name: text("name").notNull(),
@@ -408,7 +408,7 @@ var apiKeys = sqliteTable("kavach_api_keys", {
408
408
  lastUsedAt: integer("last_used_at", { mode: "timestamp" }),
409
409
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
410
410
  });
411
- var passkeyChallenges = sqliteTable("kavach_passkey_challenges", {
411
+ var passkeyChallenges = sqliteTable("theauth_passkey_challenges", {
412
412
  id: text("id").primaryKey(),
413
413
  challenge: text("challenge").notNull().unique(),
414
414
  userId: text("user_id"),
@@ -417,7 +417,7 @@ var passkeyChallenges = sqliteTable("kavach_passkey_challenges", {
417
417
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
418
418
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
419
419
  });
420
- var usernameAccounts = sqliteTable("kavach_username_accounts", {
420
+ var usernameAccounts = sqliteTable("theauth_username_accounts", {
421
421
  id: text("id").primaryKey(),
422
422
  userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
423
423
  username: text("username").notNull().unique(),
@@ -425,7 +425,7 @@ var usernameAccounts = sqliteTable("kavach_username_accounts", {
425
425
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
426
426
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
427
427
  });
428
- var phoneVerifications = sqliteTable("kavach_phone_verifications", {
428
+ var phoneVerifications = sqliteTable("theauth_phone_verifications", {
429
429
  id: text("id").primaryKey(),
430
430
  phoneNumber: text("phone_number").notNull(),
431
431
  codeHash: text("code_hash").notNull(),
@@ -433,7 +433,7 @@ var phoneVerifications = sqliteTable("kavach_phone_verifications", {
433
433
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
434
434
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
435
435
  });
436
- var trustedDevices = sqliteTable("kavach_trusted_devices", {
436
+ var trustedDevices = sqliteTable("theauth_trusted_devices", {
437
437
  id: text("id").primaryKey(),
438
438
  userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
439
439
  fingerprint: text("fingerprint").notNull(),
@@ -443,7 +443,7 @@ var trustedDevices = sqliteTable("kavach_trusted_devices", {
443
443
  trustedAt: integer("trusted_at", { mode: "timestamp" }).notNull(),
444
444
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull()
445
445
  });
446
- var oneTimeTokens = sqliteTable("kavach_one_time_tokens", {
446
+ var oneTimeTokens = sqliteTable("theauth_one_time_tokens", {
447
447
  id: text("id").primaryKey(),
448
448
  tokenHash: text("token_hash").notNull().unique(),
449
449
  // SHA-256 hex of the raw token
@@ -457,7 +457,7 @@ var oneTimeTokens = sqliteTable("kavach_one_time_tokens", {
457
457
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
458
458
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
459
459
  });
460
- var loginHistory = sqliteTable("kavach_login_history", {
460
+ var loginHistory = sqliteTable("theauth_login_history", {
461
461
  id: text("id").primaryKey(),
462
462
  userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
463
463
  method: text("method").notNull(),
@@ -466,7 +466,7 @@ var loginHistory = sqliteTable("kavach_login_history", {
466
466
  userAgent: text("user_agent"),
467
467
  timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull()
468
468
  });
469
- sqliteTable("kavach_agent_dids", {
469
+ sqliteTable("theauth_agent_dids", {
470
470
  agentId: text("agent_id").primaryKey().references(() => agents.id, { onDelete: "cascade" }),
471
471
  did: text("did").notNull().unique(),
472
472
  method: text("method", { enum: ["key", "web"] }).notNull(),
@@ -476,7 +476,7 @@ sqliteTable("kavach_agent_dids", {
476
476
  // JSON-serialised DID Document
477
477
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
478
478
  });
479
- var oidcClients = sqliteTable("kavach_oidc_clients", {
479
+ var oidcClients = sqliteTable("theauth_oidc_clients", {
480
480
  id: text("id").primaryKey(),
481
481
  clientId: text("client_id").notNull().unique(),
482
482
  clientSecretHash: text("client_secret_hash").notNull(),
@@ -490,7 +490,7 @@ var oidcClients = sqliteTable("kavach_oidc_clients", {
490
490
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
491
491
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
492
492
  });
493
- var oidcAuthCodes = sqliteTable("kavach_oidc_auth_codes", {
493
+ var oidcAuthCodes = sqliteTable("theauth_oidc_auth_codes", {
494
494
  id: text("id").primaryKey(),
495
495
  codeHash: text("code_hash").notNull().unique(),
496
496
  // SHA-256 hex of the raw code
@@ -507,7 +507,7 @@ var oidcAuthCodes = sqliteTable("kavach_oidc_auth_codes", {
507
507
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
508
508
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
509
509
  });
510
- var oidcRefreshTokens = sqliteTable("kavach_oidc_refresh_tokens", {
510
+ var oidcRefreshTokens = sqliteTable("theauth_oidc_refresh_tokens", {
511
511
  id: text("id").primaryKey(),
512
512
  tokenHash: text("token_hash").notNull().unique(),
513
513
  // SHA-256 hex of the raw token
@@ -519,7 +519,7 @@ var oidcRefreshTokens = sqliteTable("kavach_oidc_refresh_tokens", {
519
519
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
520
520
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
521
521
  });
522
- var costEvents = sqliteTable("kavach_cost_events", {
522
+ var costEvents = sqliteTable("theauth_cost_events", {
523
523
  id: text("id").primaryKey(),
524
524
  agentId: text("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
525
525
  tool: text("tool").notNull(),
@@ -534,7 +534,7 @@ var costEvents = sqliteTable("kavach_cost_events", {
534
534
  // null when not part of a chain
535
535
  recordedAt: integer("recorded_at", { mode: "timestamp" }).notNull()
536
536
  });
537
- var ephemeralSessions = sqliteTable("kavach_ephemeral_sessions", {
537
+ var ephemeralSessions = sqliteTable("theauth_ephemeral_sessions", {
538
538
  id: text("id").primaryKey(),
539
539
  agentId: text("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
540
540
  ownerId: text("owner_id").notNull().references(() => users.id),
@@ -548,7 +548,7 @@ var ephemeralSessions = sqliteTable("kavach_ephemeral_sessions", {
548
548
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
549
549
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
550
550
  });
551
- var streamEvents = sqliteTable("kavach_stream_events", {
551
+ var streamEvents = sqliteTable("theauth_stream_events", {
552
552
  id: text("id").primaryKey(),
553
553
  type: text("type").notNull(),
554
554
  timestamp: integer("timestamp", { mode: "timestamp" }).notNull(),
@@ -556,7 +556,7 @@ var streamEvents = sqliteTable("kavach_stream_events", {
556
556
  agentId: text("agent_id"),
557
557
  userId: text("user_id")
558
558
  });
559
- var jwtRefreshTokens = sqliteTable("kavach_jwt_refresh_tokens", {
559
+ var jwtRefreshTokens = sqliteTable("theauth_jwt_refresh_tokens", {
560
560
  id: text("id").primaryKey(),
561
561
  /** SHA-256 hex of the raw refresh token. The raw token is never stored. */
562
562
  tokenHash: text("token_hash").notNull().unique(),
@@ -567,7 +567,7 @@ var jwtRefreshTokens = sqliteTable("kavach_jwt_refresh_tokens", {
567
567
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
568
568
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
569
569
  });
570
- var rebacResources = sqliteTable("kavach_rebac_resources", {
570
+ var rebacResources = sqliteTable("theauth_rebac_resources", {
571
571
  id: text("id").notNull().primaryKey(),
572
572
  type: text("type").notNull(),
573
573
  // 'org', 'workspace', 'project', 'document', etc.
@@ -575,7 +575,7 @@ var rebacResources = sqliteTable("kavach_rebac_resources", {
575
575
  parentType: text("parent_type"),
576
576
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
577
577
  });
578
- var rebacRelationships = sqliteTable("kavach_rebac_relationships", {
578
+ var rebacRelationships = sqliteTable("theauth_rebac_relationships", {
579
579
  id: text("id").primaryKey(),
580
580
  subjectType: text("subject_type").notNull(),
581
581
  // 'user', 'agent', 'team', 'role'
@@ -586,7 +586,7 @@ var rebacRelationships = sqliteTable("kavach_rebac_relationships", {
586
586
  objectId: text("object_id").notNull(),
587
587
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
588
588
  });
589
- sqliteTable("kavach_federation_instances", {
589
+ sqliteTable("theauth_federation_instances", {
590
590
  id: text("id").primaryKey(),
591
591
  instanceId: text("instance_id").notNull().unique(),
592
592
  instanceUrl: text("instance_url").notNull(),
@@ -597,7 +597,7 @@ sqliteTable("kavach_federation_instances", {
597
597
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
598
598
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
599
599
  });
600
- sqliteTable("kavach_federation_tokens", {
600
+ sqliteTable("theauth_federation_tokens", {
601
601
  id: text("id").primaryKey(),
602
602
  tokenJti: text("token_jti").notNull().unique(),
603
603
  // JWT ID for dedup
@@ -611,7 +611,7 @@ sqliteTable("kavach_federation_tokens", {
611
611
  expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
612
612
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
613
613
  });
614
- var refreshTokenFamilies = sqliteTable("kavach_refresh_token_families", {
614
+ var refreshTokenFamilies = sqliteTable("theauth_refresh_token_families", {
615
615
  id: text("id").primaryKey(),
616
616
  userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
617
617
  /** Absolute session expiry — no rotation can extend beyond this date. */
@@ -620,7 +620,7 @@ var refreshTokenFamilies = sqliteTable("kavach_refresh_token_families", {
620
620
  revoked: integer("revoked").notNull().default(0),
621
621
  createdAt: integer("created_at", { mode: "timestamp" }).notNull()
622
622
  });
623
- sqliteTable("kavach_refresh_tokens", {
623
+ sqliteTable("theauth_refresh_tokens", {
624
624
  id: text("id").primaryKey(),
625
625
  familyId: text("family_id").notNull().references(() => refreshTokenFamilies.id, { onDelete: "cascade" }),
626
626
  /** SHA-256 hash of the opaque token — never store the raw token. */
@@ -728,7 +728,7 @@ function createAdditionalFieldsModule(config, db) {
728
728
  }
729
729
  function additionalFields(config = {}) {
730
730
  return {
731
- id: "kavach-additional-fields",
731
+ id: "theauth-additional-fields",
732
732
  async init(ctx) {
733
733
  const mod = createAdditionalFieldsModule(config, ctx.db);
734
734
  ctx.addEndpoint({
@@ -1081,7 +1081,7 @@ function buildSetCookie(name, value, maxAge, path = "/", secure = true) {
1081
1081
  // src/auth/admin-plugin.ts
1082
1082
  function admin(config) {
1083
1083
  return {
1084
- id: "kavach-admin",
1084
+ id: "theauth-admin",
1085
1085
  async init(ctx) {
1086
1086
  const module = createAdminModule(config ?? {}, ctx.db, ctx.sessionManager ?? null);
1087
1087
  ctx.addEndpoint({
@@ -1510,7 +1510,7 @@ function createAnonymousAuthModule(config, db, sessionManager) {
1510
1510
  // src/auth/anonymous-plugin.ts
1511
1511
  function anonymousAuth(config) {
1512
1512
  return {
1513
- id: "kavach-anonymous",
1513
+ id: "theauth-anonymous",
1514
1514
  async init(ctx) {
1515
1515
  if (!ctx.sessionManager) {
1516
1516
  throw new Error("anonymousAuth plugin requires auth.session.secret to be configured");
@@ -1521,7 +1521,7 @@ function anonymousAuth(config) {
1521
1521
  path: "/auth/anonymous",
1522
1522
  metadata: {
1523
1523
  description: "Create an anonymous guest user and return a session token",
1524
- rateLimit: { window: 6e4, max: 20 }
1524
+ rateLimit: { window: 60, max: 20 }
1525
1525
  },
1526
1526
  async handler(_request, _endpointCtx) {
1527
1527
  try {
@@ -1745,7 +1745,7 @@ function createApiKeyManagerModule(config, db) {
1745
1745
  // src/auth/api-key-plugin.ts
1746
1746
  function apiKeys2(config) {
1747
1747
  return {
1748
- id: "kavach-api-key",
1748
+ id: "theauth-api-key",
1749
1749
  async init(ctx) {
1750
1750
  const module = createApiKeyManagerModule(config ?? {}, ctx.db);
1751
1751
  ctx.addEndpoint({
@@ -2214,7 +2214,7 @@ function createCustomSessionModule(_config, db) {
2214
2214
  }
2215
2215
  function customSession(config = {}) {
2216
2216
  return {
2217
- id: "kavach-custom-session",
2217
+ id: "theauth-custom-session",
2218
2218
  hooks: {
2219
2219
  /**
2220
2220
  * Fired by the plugin runner after a session record is inserted.
@@ -2531,7 +2531,7 @@ function createDeviceAuthModule(config) {
2531
2531
  }
2532
2532
  function deviceAuth(config) {
2533
2533
  return {
2534
- id: "kavach-device-auth",
2534
+ id: "theauth-device-auth",
2535
2535
  async init(ctx) {
2536
2536
  const mod = createDeviceAuthModule(config);
2537
2537
  ctx.addEndpoint({
@@ -2539,7 +2539,7 @@ function deviceAuth(config) {
2539
2539
  path: "/auth/device/code",
2540
2540
  metadata: {
2541
2541
  description: "Request a device code and user code for the device authorization flow",
2542
- rateLimit: { window: 6e4, max: 30 }
2542
+ rateLimit: { window: 60, max: 30 }
2543
2543
  },
2544
2544
  async handler(_request, _endpointCtx) {
2545
2545
  const response = await mod.requestCode();
@@ -2561,7 +2561,7 @@ function deviceAuth(config) {
2561
2561
  path: "/auth/device/token",
2562
2562
  metadata: {
2563
2563
  description: "Poll for device authorization status (RFC 8628)",
2564
- rateLimit: { window: 1e4, max: 5 }
2564
+ rateLimit: { window: 10, max: 5 }
2565
2565
  },
2566
2566
  async handler(request, _endpointCtx) {
2567
2567
  let body;
@@ -2799,11 +2799,11 @@ function createEmailOtpModule(config, db, sessionManager) {
2799
2799
  // src/auth/email-otp-plugin.ts
2800
2800
  function emailOtp(config) {
2801
2801
  return {
2802
- id: "kavach-email-otp",
2802
+ id: "theauth-email-otp",
2803
2803
  async init(ctx) {
2804
2804
  if (!ctx.sessionManager) {
2805
2805
  throw new Error(
2806
- "kavach-email-otp plugin requires auth.session to be configured so that sessions can be issued on successful verification."
2806
+ "theauth-email-otp plugin requires auth.session to be configured so that sessions can be issued on successful verification."
2807
2807
  );
2808
2808
  }
2809
2809
  const module = createEmailOtpModule(config, ctx.db, ctx.sessionManager);
@@ -3492,8 +3492,8 @@ data: ${JSON.stringify({ clientId, timestamp: (/* @__PURE__ */ new Date()).toISO
3492
3492
  return { emit, handleRequest, getConnectionCount, replay, close };
3493
3493
  }
3494
3494
  var DEFAULT_TOKEN_TTL_SECONDS2 = 300;
3495
- var WELL_KNOWN_PATH = "/.well-known/kavach-federation.json";
3496
- var FEDERATION_TOKEN_TYPE = "kavach-federation+jwt";
3495
+ var WELL_KNOWN_PATH = "/.well-known/theauth-federation.json";
3496
+ var FEDERATION_TOKEN_TYPE = "theauth-federation+jwt";
3497
3497
  var TrustLevelSchema = z.enum(["full", "limited", "verify-only"]);
3498
3498
  var TrustedInstanceSchema = z.object({
3499
3499
  instanceId: z.string().min(1),
@@ -3569,8 +3569,8 @@ function createFederationModule(config) {
3569
3569
  const now = Math.floor(Date.now() / 1e3);
3570
3570
  const exp = now + tokenTtlSeconds;
3571
3571
  const payload = {
3572
- kavach_instance: instanceId,
3573
- kavach_instance_url: instanceUrl,
3572
+ theauth_instance: instanceId,
3573
+ theauth_instance_url: instanceUrl,
3574
3574
  permissions: permissions2,
3575
3575
  trust_score: trustScore,
3576
3576
  delegation_scope: delegationScope,
@@ -3693,7 +3693,7 @@ function createFederationModule(config) {
3693
3693
  data: {
3694
3694
  agentId,
3695
3695
  sourceInstance: sourceInstanceId,
3696
- sourceInstanceUrl: claims.kavach_instance_url ?? "",
3696
+ sourceInstanceUrl: claims.theauth_instance_url ?? "",
3697
3697
  permissions: permissions2,
3698
3698
  trustScore: effectiveTrustScore,
3699
3699
  delegationScope,
@@ -4009,7 +4009,7 @@ function createGdprModule(db) {
4009
4009
  // src/auth/gdpr-plugin.ts
4010
4010
  function gdpr() {
4011
4011
  return {
4012
- id: "kavach-gdpr",
4012
+ id: "theauth-gdpr",
4013
4013
  async init(ctx) {
4014
4014
  const module = createGdprModule(ctx.db);
4015
4015
  ctx.addEndpoint({
@@ -4800,11 +4800,11 @@ function createRateLimiter(config) {
4800
4800
  // src/auth/magic-link-plugin.ts
4801
4801
  function magicLink(config) {
4802
4802
  return {
4803
- id: "kavach-magic-link",
4803
+ id: "theauth-magic-link",
4804
4804
  async init(ctx) {
4805
4805
  if (!ctx.sessionManager) {
4806
4806
  throw new Error(
4807
- "kavach-magic-link plugin requires auth.session to be configured so that sessions can be issued on successful verification."
4807
+ "theauth-magic-link plugin requires auth.session to be configured so that sessions can be issued on successful verification."
4808
4808
  );
4809
4809
  }
4810
4810
  const module = createMagicLinkModule(config, ctx.db, ctx.sessionManager);
@@ -4876,7 +4876,7 @@ function base64url(bytes) {
4876
4876
  }
4877
4877
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
4878
4878
  }
4879
- var oauthAccounts = sqliteTable("kavach_oauth_accounts", {
4879
+ var oauthAccounts = sqliteTable("theauth_oauth_accounts", {
4880
4880
  id: text("id").primaryKey(),
4881
4881
  userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
4882
4882
  /** Provider machine ID, e.g. `'google'`, `'github'`. */
@@ -4892,7 +4892,7 @@ var oauthAccounts = sqliteTable("kavach_oauth_accounts", {
4892
4892
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
4893
4893
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
4894
4894
  });
4895
- var oauthStates = sqliteTable("kavach_oauth_states", {
4895
+ var oauthStates = sqliteTable("theauth_oauth_states", {
4896
4896
  /** Random, opaque state value sent as the `state` query parameter. */
4897
4897
  state: text("state").primaryKey(),
4898
4898
  /** PKCE code verifier (plain text — never sent to the provider). */
@@ -4994,7 +4994,7 @@ function createOAuthModule(db, config) {
4994
4994
  await db.insert(oauthAccounts).values({
4995
4995
  id: accountId,
4996
4996
  // userId will be set by the caller via linkAccount once they know the
4997
- // kavach user ID. We use a placeholder that foreign-key-checks will
4997
+ // theauth user ID. We use a placeholder that foreign-key-checks will
4998
4998
  // reject on real databases — callers MUST call linkAccount.
4999
4999
  userId: "__pending__",
5000
5000
  provider: providerId,
@@ -5089,14 +5089,14 @@ function redirectResponse(url) {
5089
5089
  }
5090
5090
  function oauth(config) {
5091
5091
  return {
5092
- id: "kavach-oauth",
5092
+ id: "theauth-oauth",
5093
5093
  async init(ctx) {
5094
5094
  const module = createOAuthModule(ctx.db, config);
5095
5095
  const baseUrl = ctx.config.baseUrl ?? "";
5096
5096
  const sessionManager = ctx.sessionManager;
5097
5097
  if (!sessionManager) {
5098
5098
  throw new Error(
5099
- "kavach-oauth plugin requires auth.session to be configured so that sessions can be issued on successful OAuth callback."
5099
+ "theauth-oauth plugin requires auth.session to be configured so that sessions can be issued on successful OAuth callback."
5100
5100
  );
5101
5101
  }
5102
5102
  const authorizeLimiter = createRateLimiter({ max: 20, window: 60 });
@@ -5180,7 +5180,7 @@ function oauth(config) {
5180
5180
  const { session, token } = await sessionManager.create(userId);
5181
5181
  const maxAge = Math.floor((session.expiresAt.getTime() - Date.now()) / 1e3);
5182
5182
  const isSecure = baseUrl.startsWith("https://");
5183
- const cookie = buildSetCookie("kavach_session", token, maxAge, "/", isSecure);
5183
+ const cookie = buildSetCookie("theauth_session", token, maxAge, "/", isSecure);
5184
5184
  const userInfo = encodeURIComponent(JSON.stringify({ id: userId, email }));
5185
5185
  const callbackUrl = `${baseUrl}/?auth_user=${userInfo}`;
5186
5186
  return new Response(null, {
@@ -7755,7 +7755,7 @@ var OAuthProxyError = class extends Error {
7755
7755
  // src/auth/oauth-proxy-plugin.ts
7756
7756
  function oauthProxy(config) {
7757
7757
  return {
7758
- id: "kavach-oauth-proxy",
7758
+ id: "theauth-oauth-proxy",
7759
7759
  async init(ctx) {
7760
7760
  const baseUrl = ctx.config.baseUrl;
7761
7761
  if (!baseUrl) {
@@ -7770,7 +7770,7 @@ function oauthProxy(config) {
7770
7770
  metadata: {
7771
7771
  description: "Start an OAuth proxy flow for a mobile app. Returns the provider authorization URL.",
7772
7772
  rateLimit: {
7773
- window: (config.rateLimit?.windowSeconds ?? 60) * 1e3,
7773
+ window: config.rateLimit?.windowSeconds ?? 60,
7774
7774
  max: config.rateLimit?.max ?? 20
7775
7775
  }
7776
7776
  },
@@ -7886,7 +7886,7 @@ function createOidcProviderModule(config, db, getUserClaims) {
7886
7886
  jwk = { ...config.signingKey };
7887
7887
  }
7888
7888
  const { d, p, q, dp, dq, qi, k, ...publicComponents } = jwk;
7889
- return { ...publicComponents, alg: signingAlg, use: "sig", kid: "kavach-oidc-1" };
7889
+ return { ...publicComponents, alg: signingAlg, use: "sig", kid: "theauth-oidc-1" };
7890
7890
  })();
7891
7891
  }
7892
7892
  return publicJwkPromise;
@@ -8209,7 +8209,7 @@ function createOidcProviderModule(config, db, getUserClaims) {
8209
8209
  const accessTokenBuilder = new jose2.SignJWT({
8210
8210
  scope: scopes.join(" "),
8211
8211
  client_id: clientId
8212
- }).setProtectedHeader({ alg: signingAlg, kid: "kavach-oidc-1" }).setIssuer(issuer).setSubject(userId).setAudience(clientId).setIssuedAt(now).setExpirationTime(now + accessTokenTtl).setJti(jti);
8212
+ }).setProtectedHeader({ alg: signingAlg, kid: "theauth-oidc-1" }).setIssuer(issuer).setSubject(userId).setAudience(clientId).setIssuedAt(now).setExpirationTime(now + accessTokenTtl).setJti(jti);
8213
8213
  const accessToken = await accessTokenBuilder.sign(key);
8214
8214
  const userClaims = await getUserClaims(userId, scopes);
8215
8215
  const idTokenPayload = {
@@ -8222,7 +8222,7 @@ function createOidcProviderModule(config, db, getUserClaims) {
8222
8222
  const atHashBuffer = await sha256Raw(accessToken);
8223
8223
  const atHashHalf = atHashBuffer.subarray(0, atHashBuffer.length / 2);
8224
8224
  idTokenPayload.at_hash = jose2.base64url.encode(atHashHalf);
8225
- const idToken = await new jose2.SignJWT(idTokenPayload).setProtectedHeader({ alg: signingAlg, kid: "kavach-oidc-1" }).setIssuer(issuer).setSubject(userId).setAudience(clientId).setIssuedAt(now).setExpirationTime(now + idTokenTtl).sign(key);
8225
+ const idToken = await new jose2.SignJWT(idTokenPayload).setProtectedHeader({ alg: signingAlg, kid: "theauth-oidc-1" }).setIssuer(issuer).setSubject(userId).setAudience(clientId).setIssuedAt(now).setExpirationTime(now + idTokenTtl).sign(key);
8226
8226
  const rawRefreshToken = generateRandomHex(REFRESH_TOKEN_BYTE_LENGTH);
8227
8227
  const refreshTokenHash = await hashSecret(rawRefreshToken);
8228
8228
  const refreshExpiresAt = new Date((now + refreshTokenTtl) * 1e3);
@@ -8577,12 +8577,12 @@ function createSessionManager(config, db) {
8577
8577
  // src/auth/one-tap-plugin.ts
8578
8578
  function oneTap(config) {
8579
8579
  return {
8580
- id: "kavach-one-tap",
8580
+ id: "theauth-one-tap",
8581
8581
  async init(ctx) {
8582
8582
  const sessionConfig = ctx.config.auth?.session;
8583
8583
  if (!sessionConfig) {
8584
8584
  throw new Error(
8585
- "kavach-one-tap plugin requires auth.session to be configured so that sessions can be issued on successful sign-in."
8585
+ "theauth-one-tap plugin requires auth.session to be configured so that sessions can be issued on successful sign-in."
8586
8586
  );
8587
8587
  }
8588
8588
  const sessionManager = createSessionManager(sessionConfig, ctx.db);
@@ -8766,7 +8766,7 @@ function createOneTimeTokenModule(config, db) {
8766
8766
  var DEFAULT_TITLE = "TheAuth API";
8767
8767
  var DEFAULT_VERSION = "0.0.1";
8768
8768
  var DEFAULT_SERVER_URL = "/";
8769
- var DEFAULT_BASE_PATH = "/api/kavach";
8769
+ var DEFAULT_BASE_PATH = "/api/theauth";
8770
8770
  var ERROR_SCHEMA = {
8771
8771
  type: "object",
8772
8772
  properties: {
@@ -10351,7 +10351,7 @@ async function parseBody3(request) {
10351
10351
  var ADMIN_ROLES = /* @__PURE__ */ new Set(["owner", "admin"]);
10352
10352
  function organization(config) {
10353
10353
  return {
10354
- id: "kavach-organization",
10354
+ id: "theauth-organization",
10355
10355
  async init(ctx) {
10356
10356
  const module = createOrgModule(config ?? {}, ctx.db);
10357
10357
  ctx.addEndpoint({
@@ -11394,7 +11394,7 @@ async function parseBody5(request) {
11394
11394
  }
11395
11395
  function passkey(config) {
11396
11396
  return {
11397
- id: "kavach-passkey",
11397
+ id: "theauth-passkey",
11398
11398
  async init(ctx) {
11399
11399
  const module = createPasskeyModule(config, ctx.db);
11400
11400
  ctx.addEndpoint({
@@ -11503,7 +11503,7 @@ function passkey(config) {
11503
11503
  headers: {
11504
11504
  "Content-Type": "application/json",
11505
11505
  "Set-Cookie": buildSetCookie(
11506
- "kavach_session",
11506
+ "theauth_session",
11507
11507
  token,
11508
11508
  maxAge,
11509
11509
  "/",
@@ -11954,8 +11954,8 @@ function createPolarModule(config, db) {
11954
11954
  const sub = obj;
11955
11955
  let userId = null;
11956
11956
  const metadata = sub.metadata;
11957
- if (metadata && typeof metadata === "object" && typeof metadata.kavach_user_id === "string") {
11958
- userId = metadata.kavach_user_id;
11957
+ if (metadata && typeof metadata === "object" && typeof metadata.theauth_user_id === "string") {
11958
+ userId = metadata.theauth_user_id;
11959
11959
  }
11960
11960
  if (!userId && sub.customer_id) {
11961
11961
  const user = await findUserByPolarCustomerId(sub.customer_id);
@@ -11972,8 +11972,8 @@ function createPolarModule(config, db) {
11972
11972
  const sub = obj;
11973
11973
  let userId = null;
11974
11974
  const metadata = sub.metadata;
11975
- if (metadata && typeof metadata === "object" && typeof metadata.kavach_user_id === "string") {
11976
- userId = metadata.kavach_user_id;
11975
+ if (metadata && typeof metadata === "object" && typeof metadata.theauth_user_id === "string") {
11976
+ userId = metadata.theauth_user_id;
11977
11977
  }
11978
11978
  if (!userId && sub.customer_id) {
11979
11979
  const user = await findUserByPolarCustomerId(sub.customer_id);
@@ -11998,7 +11998,7 @@ function createPolarModule(config, db) {
11998
11998
  }
11999
11999
  const body = {
12000
12000
  product_id: productId,
12001
- metadata: { kavach_user_id: userId }
12001
+ metadata: { theauth_user_id: userId }
12002
12002
  };
12003
12003
  if (options.successUrl) {
12004
12004
  body.success_url = options.successUrl;
@@ -12095,7 +12095,7 @@ function createPolarModule(config, db) {
12095
12095
  // src/auth/polar-plugin.ts
12096
12096
  function polar(config) {
12097
12097
  return {
12098
- id: "kavach-polar",
12098
+ id: "theauth-polar",
12099
12099
  async init(ctx) {
12100
12100
  const module = createPolarModule(config, ctx.db);
12101
12101
  ctx.addEndpoint({
@@ -12248,7 +12248,7 @@ function rateLimit(config = {}) {
12248
12248
  const extractKey = config.keyExtractor ?? defaultKeyExtractor2;
12249
12249
  const onLimitFn = config.onLimit ?? defaultOnLimit;
12250
12250
  return {
12251
- id: "kavach-rate-limit",
12251
+ id: "theauth-rate-limit",
12252
12252
  hooks: {
12253
12253
  async onRequest(request) {
12254
12254
  const url = new URL(request.url);
@@ -12640,6 +12640,618 @@ function createReBACModule(config, db) {
12640
12640
  expand
12641
12641
  };
12642
12642
  }
12643
+
12644
+ // src/auth/scim-filter.ts
12645
+ var ScimFilterError = class extends Error {
12646
+ scimType = "invalidFilter";
12647
+ constructor(message) {
12648
+ super(message);
12649
+ this.name = "ScimFilterError";
12650
+ }
12651
+ };
12652
+ var WORD_RE = /[A-Za-z0-9_\-:.]/;
12653
+ function tokenize(input) {
12654
+ const tokens = [];
12655
+ let i = 0;
12656
+ const n = input.length;
12657
+ while (i < n) {
12658
+ const c = input[i];
12659
+ if (c === " " || c === " " || c === "\n" || c === "\r") {
12660
+ i += 1;
12661
+ continue;
12662
+ }
12663
+ if (c === "(") {
12664
+ tokens.push({ kind: "lparen", value: "(", start: i });
12665
+ i += 1;
12666
+ continue;
12667
+ }
12668
+ if (c === ")") {
12669
+ tokens.push({ kind: "rparen", value: ")", start: i });
12670
+ i += 1;
12671
+ continue;
12672
+ }
12673
+ if (c === "[") {
12674
+ tokens.push({ kind: "lbracket", value: "[", start: i });
12675
+ i += 1;
12676
+ continue;
12677
+ }
12678
+ if (c === "]") {
12679
+ tokens.push({ kind: "rbracket", value: "]", start: i });
12680
+ i += 1;
12681
+ continue;
12682
+ }
12683
+ if (c === '"') {
12684
+ const start = i;
12685
+ i += 1;
12686
+ let value = "";
12687
+ while (i < n && input[i] !== '"') {
12688
+ if (input[i] === "\\" && i + 1 < n) {
12689
+ const next = input[i + 1];
12690
+ if (next === '"' || next === "\\" || next === "/") {
12691
+ value += next;
12692
+ i += 2;
12693
+ continue;
12694
+ }
12695
+ if (next === "t") {
12696
+ value += " ";
12697
+ i += 2;
12698
+ continue;
12699
+ }
12700
+ if (next === "n") {
12701
+ value += "\n";
12702
+ i += 2;
12703
+ continue;
12704
+ }
12705
+ if (next === "r") {
12706
+ value += "\r";
12707
+ i += 2;
12708
+ continue;
12709
+ }
12710
+ value += input[i];
12711
+ i += 1;
12712
+ continue;
12713
+ }
12714
+ value += input[i];
12715
+ i += 1;
12716
+ }
12717
+ if (i >= n) {
12718
+ throw new ScimFilterError(`Unterminated string literal at position ${start}`);
12719
+ }
12720
+ i += 1;
12721
+ tokens.push({ kind: "string", value, start });
12722
+ continue;
12723
+ }
12724
+ if (c === "-" || c >= "0" && c <= "9") {
12725
+ const start = i;
12726
+ let raw = "";
12727
+ if (c === "-") {
12728
+ raw += "-";
12729
+ i += 1;
12730
+ }
12731
+ while (i < n && /[0-9.eE+-]/.test(input[i])) {
12732
+ raw += input[i];
12733
+ i += 1;
12734
+ }
12735
+ if (raw === "" || raw === "-" || Number.isNaN(Number(raw))) {
12736
+ throw new ScimFilterError(`Invalid number literal at position ${start}`);
12737
+ }
12738
+ tokens.push({ kind: "number", value: raw, start });
12739
+ continue;
12740
+ }
12741
+ if (WORD_RE.test(c)) {
12742
+ const start = i;
12743
+ let raw = "";
12744
+ while (i < n && WORD_RE.test(input[i])) {
12745
+ raw += input[i];
12746
+ i += 1;
12747
+ }
12748
+ const lower = raw.toLowerCase();
12749
+ if (lower === "true" || lower === "false") {
12750
+ tokens.push({ kind: "boolean", value: lower, start });
12751
+ continue;
12752
+ }
12753
+ if (lower === "null") {
12754
+ tokens.push({ kind: "null", value: "null", start });
12755
+ continue;
12756
+ }
12757
+ tokens.push({ kind: "word", value: raw, start });
12758
+ continue;
12759
+ }
12760
+ throw new ScimFilterError(`Unexpected character "${c}" at position ${i}`);
12761
+ }
12762
+ tokens.push({ kind: "eof", value: "", start: n });
12763
+ return tokens;
12764
+ }
12765
+ var COMP_OPS = /* @__PURE__ */ new Set(["eq", "ne", "co", "sw", "ew", "gt", "lt", "ge", "le"]);
12766
+ function parseFilter(input) {
12767
+ const tokens = tokenize(input);
12768
+ let pos = 0;
12769
+ function peek() {
12770
+ return tokens[pos];
12771
+ }
12772
+ function eat() {
12773
+ const t = tokens[pos];
12774
+ pos += 1;
12775
+ return t;
12776
+ }
12777
+ function expectKind(kind) {
12778
+ const t = peek();
12779
+ if (t.kind !== kind) {
12780
+ throw new ScimFilterError(`Expected ${kind} at position ${t.start}, got ${t.kind}`);
12781
+ }
12782
+ return eat();
12783
+ }
12784
+ function matchWord(word) {
12785
+ const t = peek();
12786
+ return t.kind === "word" && t.value.toLowerCase() === word;
12787
+ }
12788
+ function parseAttrPath() {
12789
+ const t = peek();
12790
+ if (t.kind !== "word") {
12791
+ throw new ScimFilterError(`Expected attribute path at position ${t.start}`);
12792
+ }
12793
+ eat();
12794
+ const raw = t.value;
12795
+ const colonIndex = raw.lastIndexOf(":");
12796
+ let schemaUrn;
12797
+ let tail = raw;
12798
+ if (colonIndex > 0 && raw.startsWith("urn:")) {
12799
+ schemaUrn = raw.slice(0, colonIndex);
12800
+ tail = raw.slice(colonIndex + 1);
12801
+ }
12802
+ const parts = tail.split(".").filter((part) => part.length > 0);
12803
+ if (parts.length === 0) {
12804
+ throw new ScimFilterError(`Empty attribute path at position ${t.start}`);
12805
+ }
12806
+ return { schemaUrn, path: parts };
12807
+ }
12808
+ function parseCompValue() {
12809
+ const t = eat();
12810
+ if (t.kind === "string") return t.value;
12811
+ if (t.kind === "number") return Number(t.value);
12812
+ if (t.kind === "boolean") return t.value === "true";
12813
+ if (t.kind === "null") return null;
12814
+ throw new ScimFilterError(`Expected comparison value at position ${t.start}, got ${t.kind}`);
12815
+ }
12816
+ function parsePrimary() {
12817
+ if (matchWord("not")) {
12818
+ eat();
12819
+ expectKind("lparen");
12820
+ const inner = parseOr();
12821
+ expectKind("rparen");
12822
+ return { kind: "not", inner };
12823
+ }
12824
+ if (peek().kind === "lparen") {
12825
+ eat();
12826
+ const inner = parseOr();
12827
+ expectKind("rparen");
12828
+ return inner;
12829
+ }
12830
+ const attr = parseAttrPath();
12831
+ if (peek().kind === "lbracket") {
12832
+ eat();
12833
+ const inner = parseOr();
12834
+ expectKind("rbracket");
12835
+ return { kind: "valuePath", attr, inner };
12836
+ }
12837
+ const opTok = peek();
12838
+ if (opTok.kind !== "word") {
12839
+ throw new ScimFilterError(`Expected operator at position ${opTok.start}`);
12840
+ }
12841
+ const op = opTok.value.toLowerCase();
12842
+ if (op === "pr") {
12843
+ eat();
12844
+ return { kind: "pr", attr };
12845
+ }
12846
+ if (COMP_OPS.has(op)) {
12847
+ eat();
12848
+ const value = parseCompValue();
12849
+ return { kind: "cmp", attr, op, value };
12850
+ }
12851
+ throw new ScimFilterError(`Unknown operator "${opTok.value}" at position ${opTok.start}`);
12852
+ }
12853
+ function parseAnd() {
12854
+ let left = parsePrimary();
12855
+ while (matchWord("and")) {
12856
+ eat();
12857
+ const right = parsePrimary();
12858
+ left = { kind: "and", left, right };
12859
+ }
12860
+ return left;
12861
+ }
12862
+ function parseOr() {
12863
+ let left = parseAnd();
12864
+ while (matchWord("or")) {
12865
+ eat();
12866
+ const right = parseAnd();
12867
+ left = { kind: "or", left, right };
12868
+ }
12869
+ return left;
12870
+ }
12871
+ const ast = parseOr();
12872
+ if (peek().kind !== "eof") {
12873
+ throw new ScimFilterError(`Unexpected trailing input at position ${peek().start}`);
12874
+ }
12875
+ return ast;
12876
+ }
12877
+ function evaluateFilter(ast, resource) {
12878
+ switch (ast.kind) {
12879
+ case "and":
12880
+ return evaluateFilter(ast.left, resource) && evaluateFilter(ast.right, resource);
12881
+ case "or":
12882
+ return evaluateFilter(ast.left, resource) || evaluateFilter(ast.right, resource);
12883
+ case "not":
12884
+ return !evaluateFilter(ast.inner, resource);
12885
+ case "pr":
12886
+ return hasPresent(readPath(resource, ast.attr));
12887
+ case "cmp":
12888
+ return compareAny(readPath(resource, ast.attr), ast.op, ast.value);
12889
+ case "valuePath":
12890
+ return evaluateValuePath(ast, resource);
12891
+ }
12892
+ }
12893
+ function evaluateValuePath(node, resource) {
12894
+ const value = readPath(resource, node.attr);
12895
+ if (value === void 0 || value === null) return false;
12896
+ const items = Array.isArray(value) ? value : [value];
12897
+ for (const item of items) {
12898
+ if (item && typeof item === "object") {
12899
+ if (evaluateFilter(node.inner, item)) return true;
12900
+ }
12901
+ }
12902
+ return false;
12903
+ }
12904
+ function readPath(resource, attr) {
12905
+ let current = resource;
12906
+ if (attr.schemaUrn && attr.schemaUrn !== "urn:ietf:params:scim:schemas:core:2.0:User") {
12907
+ if (current && typeof current === "object") {
12908
+ const record = current;
12909
+ current = record[attr.schemaUrn];
12910
+ }
12911
+ }
12912
+ for (const segment of attr.path) {
12913
+ if (current === void 0 || current === null) return void 0;
12914
+ if (Array.isArray(current)) {
12915
+ const collected = [];
12916
+ for (const item of current) {
12917
+ if (item && typeof item === "object") {
12918
+ const sub = item[segment];
12919
+ if (sub !== void 0) collected.push(sub);
12920
+ }
12921
+ }
12922
+ current = collected.length > 0 ? collected : void 0;
12923
+ continue;
12924
+ }
12925
+ if (typeof current !== "object") return void 0;
12926
+ current = current[segment];
12927
+ }
12928
+ return current;
12929
+ }
12930
+ function hasPresent(value) {
12931
+ if (value === void 0 || value === null) return false;
12932
+ if (typeof value === "string") return value.length > 0;
12933
+ if (Array.isArray(value)) return value.length > 0;
12934
+ if (typeof value === "object") return Object.keys(value).length > 0;
12935
+ return true;
12936
+ }
12937
+ function compareAny(raw, op, target) {
12938
+ if (Array.isArray(raw)) {
12939
+ return raw.some((item) => compareScalar(item, op, target));
12940
+ }
12941
+ return compareScalar(raw, op, target);
12942
+ }
12943
+ function compareScalar(raw, op, target) {
12944
+ if (raw === void 0) return false;
12945
+ if (typeof raw === "string" && typeof target === "string") {
12946
+ return compareString(raw, op, target);
12947
+ }
12948
+ if (typeof raw === "number" && typeof target === "number") {
12949
+ return compareNumber(raw, op, target);
12950
+ }
12951
+ if (typeof raw === "boolean" && typeof target === "boolean") {
12952
+ if (op === "eq") return raw === target;
12953
+ if (op === "ne") return raw !== target;
12954
+ return false;
12955
+ }
12956
+ if (raw === null) {
12957
+ if (op === "eq") return target === null;
12958
+ if (op === "ne") return target !== null;
12959
+ return false;
12960
+ }
12961
+ if (op === "ne") return true;
12962
+ return false;
12963
+ }
12964
+ function compareString(raw, op, target) {
12965
+ const a = raw.toLowerCase();
12966
+ const b = target.toLowerCase();
12967
+ switch (op) {
12968
+ case "eq":
12969
+ return a === b;
12970
+ case "ne":
12971
+ return a !== b;
12972
+ case "co":
12973
+ return a.includes(b);
12974
+ case "sw":
12975
+ return a.startsWith(b);
12976
+ case "ew":
12977
+ return a.endsWith(b);
12978
+ case "gt":
12979
+ return a > b;
12980
+ case "ge":
12981
+ return a >= b;
12982
+ case "lt":
12983
+ return a < b;
12984
+ case "le":
12985
+ return a <= b;
12986
+ }
12987
+ }
12988
+ function compareNumber(raw, op, target) {
12989
+ switch (op) {
12990
+ case "eq":
12991
+ return raw === target;
12992
+ case "ne":
12993
+ return raw !== target;
12994
+ case "gt":
12995
+ return raw > target;
12996
+ case "ge":
12997
+ return raw >= target;
12998
+ case "lt":
12999
+ return raw < target;
13000
+ case "le":
13001
+ return raw <= target;
13002
+ case "co":
13003
+ case "sw":
13004
+ case "ew":
13005
+ return false;
13006
+ }
13007
+ }
13008
+
13009
+ // src/auth/scim-patch.ts
13010
+ var ScimPatchError = class extends Error {
13011
+ scimType;
13012
+ status;
13013
+ constructor(message, scimType, status = 400) {
13014
+ super(message);
13015
+ this.name = "ScimPatchError";
13016
+ this.scimType = scimType;
13017
+ this.status = status;
13018
+ }
13019
+ };
13020
+ var IMMUTABLE_PATHS = /* @__PURE__ */ new Set([
13021
+ "id",
13022
+ "schemas",
13023
+ "meta",
13024
+ "meta.created",
13025
+ "meta.lastmodified",
13026
+ "meta.location",
13027
+ "meta.resourcetype"
13028
+ ]);
13029
+ function parsePatchPath(raw) {
13030
+ if (raw.length === 0) {
13031
+ throw new ScimPatchError("Empty PATCH path", "invalidPath");
13032
+ }
13033
+ const bracketIdx = raw.indexOf("[");
13034
+ if (bracketIdx === -1) {
13035
+ const { schemaUrn: schemaUrn2, parts } = splitSchemaUrn(raw);
13036
+ if (parts.length === 0) throw new ScimPatchError("Empty PATCH path", "invalidPath");
13037
+ return {
13038
+ schemaUrn: schemaUrn2,
13039
+ base: parts[0],
13040
+ subPath: parts.length > 1 ? parts.slice(1) : void 0
13041
+ };
13042
+ }
13043
+ const closeIdx = raw.lastIndexOf("]");
13044
+ if (closeIdx < bracketIdx) {
13045
+ throw new ScimPatchError("Unbalanced brackets in PATCH path", "invalidPath");
13046
+ }
13047
+ const headRaw = raw.slice(0, bracketIdx);
13048
+ const filterRaw = raw.slice(bracketIdx + 1, closeIdx);
13049
+ const tailRaw = raw.slice(closeIdx + 1);
13050
+ const { schemaUrn, parts: headParts } = splitSchemaUrn(headRaw);
13051
+ if (headParts.length === 0) {
13052
+ throw new ScimPatchError("PATCH path missing attribute before '['", "invalidPath");
13053
+ }
13054
+ if (headParts.length > 1) {
13055
+ throw new ScimPatchError(
13056
+ "PATCH value-filter must attach to a top-level attribute",
13057
+ "invalidPath"
13058
+ );
13059
+ }
13060
+ let valueFilter;
13061
+ try {
13062
+ valueFilter = parseFilter(filterRaw);
13063
+ } catch (err2) {
13064
+ if (err2 instanceof ScimFilterError) {
13065
+ throw new ScimPatchError(`Invalid value filter: ${err2.message}`, "invalidPath");
13066
+ }
13067
+ throw err2;
13068
+ }
13069
+ let subPath;
13070
+ if (tailRaw.length > 0) {
13071
+ if (!tailRaw.startsWith(".")) {
13072
+ throw new ScimPatchError(
13073
+ `Unexpected characters after value filter: "${tailRaw}"`,
13074
+ "invalidPath"
13075
+ );
13076
+ }
13077
+ subPath = tailRaw.slice(1).split(".").filter((p) => p.length > 0);
13078
+ if (subPath.length === 0) {
13079
+ throw new ScimPatchError("Trailing '.' in PATCH path", "invalidPath");
13080
+ }
13081
+ }
13082
+ return { schemaUrn, base: headParts[0], valueFilter, subPath };
13083
+ }
13084
+ function splitSchemaUrn(raw) {
13085
+ if (!raw.startsWith("urn:")) {
13086
+ return { parts: raw.split(".").filter((p) => p.length > 0) };
13087
+ }
13088
+ const colonIndex = raw.lastIndexOf(":");
13089
+ if (colonIndex < 4) return { parts: raw.split(".").filter((p) => p.length > 0) };
13090
+ const schemaUrn = raw.slice(0, colonIndex);
13091
+ const tail = raw.slice(colonIndex + 1);
13092
+ return { schemaUrn, parts: tail.split(".").filter((p) => p.length > 0) };
13093
+ }
13094
+ function applyPatchOps(resource, operations, options = {}) {
13095
+ const cap = options.maxOperations ?? 1e3;
13096
+ if (operations.length > cap) {
13097
+ throw new ScimPatchError(
13098
+ `PATCH has ${operations.length} operations, limit is ${cap}`,
13099
+ "tooMany",
13100
+ 413
13101
+ );
13102
+ }
13103
+ const extraReadonly = new Set((options.readonlyPaths ?? []).map((p) => p.toLowerCase()));
13104
+ for (const rawOp of operations) {
13105
+ const op = rawOp.op?.toLowerCase();
13106
+ if (op !== "add" && op !== "replace" && op !== "remove") {
13107
+ throw new ScimPatchError(`Unknown PATCH op "${rawOp.op}"`, "invalidValue");
13108
+ }
13109
+ if (!rawOp.path) {
13110
+ if (op === "remove") {
13111
+ throw new ScimPatchError("PATCH remove requires a path", "noTarget");
13112
+ }
13113
+ if (rawOp.value === void 0 || rawOp.value === null) continue;
13114
+ if (typeof rawOp.value !== "object") {
13115
+ throw new ScimPatchError(
13116
+ "PATCH value must be an object when path is absent",
13117
+ "invalidValue"
13118
+ );
13119
+ }
13120
+ mergeInto(resource, rawOp.value, op, extraReadonly);
13121
+ continue;
13122
+ }
13123
+ let path;
13124
+ try {
13125
+ path = parsePatchPath(rawOp.path);
13126
+ } catch (err2) {
13127
+ if (err2 instanceof ScimPatchError) throw err2;
13128
+ throw new ScimPatchError(`Invalid PATCH path "${rawOp.path}"`, "invalidPath");
13129
+ }
13130
+ const joinedPath = joinPath(path);
13131
+ if (IMMUTABLE_PATHS.has(joinedPath) || extraReadonly.has(joinedPath)) {
13132
+ throw new ScimPatchError(`Attribute "${joinedPath}" is immutable`, "mutability");
13133
+ }
13134
+ applyToResource(resource, path, op, rawOp.value);
13135
+ }
13136
+ return resource;
13137
+ }
13138
+ function joinPath(path) {
13139
+ const head = path.schemaUrn ? `${path.schemaUrn}:${path.base}` : path.base;
13140
+ const sub = path.subPath && path.subPath.length > 0 ? `.${path.subPath.join(".")}` : "";
13141
+ return `${head}${sub}`.toLowerCase();
13142
+ }
13143
+ function containerFor(resource, schemaUrn) {
13144
+ if (!schemaUrn) return resource;
13145
+ const existing = resource[schemaUrn];
13146
+ if (existing && typeof existing === "object" && !Array.isArray(existing)) {
13147
+ return existing;
13148
+ }
13149
+ const next = {};
13150
+ resource[schemaUrn] = next;
13151
+ return next;
13152
+ }
13153
+ function mergeInto(target, source, op, readonlyPaths) {
13154
+ for (const [key, value] of Object.entries(source)) {
13155
+ const lower = key.toLowerCase();
13156
+ if (IMMUTABLE_PATHS.has(lower) || readonlyPaths.has(lower)) {
13157
+ throw new ScimPatchError(`Attribute "${key}" is immutable`, "mutability");
13158
+ }
13159
+ if (op === "add" && Array.isArray(target[key]) && Array.isArray(value)) {
13160
+ target[key] = target[key].concat(value);
13161
+ continue;
13162
+ }
13163
+ target[key] = value;
13164
+ }
13165
+ }
13166
+ function applyToResource(resource, path, op, value) {
13167
+ const container = containerFor(resource, path.schemaUrn);
13168
+ const currentBase = container[path.base];
13169
+ if (path.valueFilter) {
13170
+ if (!Array.isArray(currentBase)) {
13171
+ throw new ScimPatchError(
13172
+ `Value filter requires "${path.base}" to be an array`,
13173
+ "invalidPath"
13174
+ );
13175
+ }
13176
+ const arr = currentBase;
13177
+ if (op === "remove") {
13178
+ container[path.base] = arr.filter(
13179
+ (item) => !evaluateFilter(path.valueFilter, item)
13180
+ );
13181
+ return;
13182
+ }
13183
+ for (const item of arr) {
13184
+ if (!evaluateFilter(path.valueFilter, item)) continue;
13185
+ if (path.subPath && path.subPath.length > 0) {
13186
+ setDeep(item, path.subPath, value);
13187
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
13188
+ mergeInto(item, value, op, /* @__PURE__ */ new Set());
13189
+ } else {
13190
+ throw new ScimPatchError(
13191
+ "Value filter without subPath requires object value",
13192
+ "invalidValue"
13193
+ );
13194
+ }
13195
+ }
13196
+ return;
13197
+ }
13198
+ if (op === "remove") {
13199
+ if (path.subPath && path.subPath.length > 0) {
13200
+ deleteDeep(currentBase, path.subPath);
13201
+ } else {
13202
+ container[path.base] = void 0;
13203
+ }
13204
+ return;
13205
+ }
13206
+ if (path.subPath && path.subPath.length > 0) {
13207
+ const host = ensureObject(container, path.base);
13208
+ setDeep(host, path.subPath, value);
13209
+ return;
13210
+ }
13211
+ if (op === "add" && Array.isArray(currentBase) && Array.isArray(value)) {
13212
+ container[path.base] = currentBase.concat(value);
13213
+ return;
13214
+ }
13215
+ container[path.base] = value;
13216
+ }
13217
+ function ensureObject(container, key) {
13218
+ const current = container[key];
13219
+ if (current && typeof current === "object" && !Array.isArray(current)) {
13220
+ return current;
13221
+ }
13222
+ const next = {};
13223
+ container[key] = next;
13224
+ return next;
13225
+ }
13226
+ function setDeep(target, path, value) {
13227
+ if (!target || typeof target !== "object") return;
13228
+ let cursor = target;
13229
+ for (let i = 0; i < path.length - 1; i += 1) {
13230
+ const segment = path[i];
13231
+ const next = cursor[segment];
13232
+ if (next && typeof next === "object" && !Array.isArray(next)) {
13233
+ cursor = next;
13234
+ } else {
13235
+ const fresh = {};
13236
+ cursor[segment] = fresh;
13237
+ cursor = fresh;
13238
+ }
13239
+ }
13240
+ cursor[path[path.length - 1]] = value;
13241
+ }
13242
+ function deleteDeep(target, path) {
13243
+ if (!target || typeof target !== "object") return;
13244
+ let cursor = target;
13245
+ for (let i = 0; i < path.length - 1; i += 1) {
13246
+ const segment = path[i];
13247
+ const next = cursor[segment];
13248
+ if (!next || typeof next !== "object") return;
13249
+ cursor = next;
13250
+ }
13251
+ cursor[path[path.length - 1]] = void 0;
13252
+ }
13253
+
13254
+ // src/auth/scim.ts
12643
13255
  var SCIM_CONTENT_TYPE = "application/scim+json";
12644
13256
  var SCHEMA_USER = "urn:ietf:params:scim:schemas:core:2.0:User";
12645
13257
  var SCHEMA_GROUP = "urn:ietf:params:scim:schemas:core:2.0:Group";
@@ -12648,6 +13260,7 @@ var SCHEMA_PATCH = "urn:ietf:params:scim:api:messages:2.0:PatchOp";
12648
13260
  var SCHEMA_ERROR = "urn:ietf:params:scim:api:messages:2.0:Error";
12649
13261
  var SCHEMA_SP_CONFIG = "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig";
12650
13262
  var SCHEMA_RESOURCE_TYPE = "urn:ietf:params:scim:schemas:core:2.0:ResourceType";
13263
+ var SCHEMA_ENTERPRISE = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
12651
13264
  function scimResponse(body, status = 200) {
12652
13265
  return new Response(JSON.stringify(body), {
12653
13266
  status,
@@ -12682,8 +13295,13 @@ function userRowToScim(row, baseUrl) {
12682
13295
  const primaryEmail = row.email;
12683
13296
  const meta = row.metadata ?? {};
12684
13297
  const active = meta["scim:active"] !== false && row.banned === 0;
12685
- return {
12686
- schemas: [SCHEMA_USER],
13298
+ const enterprise = meta["scim:enterprise"];
13299
+ const schemas = [SCHEMA_USER];
13300
+ if (enterprise && typeof enterprise === "object" && Object.keys(enterprise).length > 0) {
13301
+ schemas.push("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User");
13302
+ }
13303
+ const body = {
13304
+ schemas,
12687
13305
  id: row.id,
12688
13306
  externalId: row.externalId ?? void 0,
12689
13307
  userName: row.username ?? row.email,
@@ -12702,6 +13320,10 @@ function userRowToScim(row, baseUrl) {
12702
13320
  location: `${baseUrl}/scim/v2/Users/${row.id}`
12703
13321
  }
12704
13322
  };
13323
+ if (enterprise && typeof enterprise === "object") {
13324
+ body["urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"] = enterprise;
13325
+ }
13326
+ return body;
12705
13327
  }
12706
13328
  function orgRowToScim(row, members, memberEmails, baseUrl) {
12707
13329
  const meta = row.metadata ?? {};
@@ -12723,35 +13345,71 @@ function orgRowToScim(row, members, memberEmails, baseUrl) {
12723
13345
  }
12724
13346
  };
12725
13347
  }
12726
- function parseScimFilter(filter) {
13348
+ function flattenToLegacyClauses(ast) {
12727
13349
  const clauses = [];
12728
- const parts = filter.split(/\s+and\s+/i);
12729
- for (const part of parts) {
12730
- const trimmed = part.trim();
12731
- const matchWithValue = trimmed.match(/^(\S+)\s+(eq|co|sw)\s+"([^"]*)"$/i);
12732
- if (matchWithValue) {
13350
+ const stack = [ast];
13351
+ while (stack.length > 0) {
13352
+ const node = stack.pop();
13353
+ if (node.kind === "and") {
13354
+ stack.push(node.right, node.left);
13355
+ continue;
13356
+ }
13357
+ if (node.kind === "pr") {
13358
+ clauses.push({ attribute: attrKey(node.attr.path), op: "pr" });
13359
+ continue;
13360
+ }
13361
+ if (node.kind === "cmp") {
13362
+ if (node.op !== "eq" && node.op !== "co" && node.op !== "sw") return null;
13363
+ if (typeof node.value !== "string" && typeof node.value !== "boolean") return null;
12733
13364
  clauses.push({
12734
- attribute: matchWithValue[1].toLowerCase(),
12735
- op: matchWithValue[2].toLowerCase(),
12736
- value: matchWithValue[3]
13365
+ attribute: attrKey(node.attr.path),
13366
+ op: node.op,
13367
+ value: typeof node.value === "boolean" ? String(node.value) : node.value
12737
13368
  });
12738
13369
  continue;
12739
13370
  }
12740
- const matchPr = trimmed.match(/^(\S+)\s+pr$/i);
12741
- if (matchPr) {
12742
- clauses.push({ attribute: matchPr[1].toLowerCase(), op: "pr" });
12743
- }
13371
+ return null;
12744
13372
  }
12745
13373
  return clauses;
12746
13374
  }
13375
+ function attrKey(path) {
13376
+ return path.join(".").toLowerCase();
13377
+ }
13378
+ function parseScimFilter(filter) {
13379
+ const ast = parseFilter(filter);
13380
+ const clauses = flattenToLegacyClauses(ast);
13381
+ return { ast, clauses };
13382
+ }
12747
13383
  function createScimModule(config, db) {
12748
13384
  const {
12749
13385
  bearerToken,
12750
13386
  autoCreateUsers = true,
12751
13387
  autoDeactivateUsers = true,
12752
13388
  onProvision,
12753
- onDeprovision
13389
+ onDeprovision,
13390
+ resolveSelf,
13391
+ audit
12754
13392
  } = config;
13393
+ async function writeScimAudit(action, resource, subjectUserId, durationMs, result = "allowed", reason) {
13394
+ if (!audit || audit.enabled === false) return;
13395
+ try {
13396
+ await db.insert(auditLogs).values({
13397
+ id: generateId2(),
13398
+ agentId: audit.agentId,
13399
+ userId: subjectUserId,
13400
+ action,
13401
+ resource,
13402
+ parameters: {},
13403
+ result,
13404
+ reason: reason ?? null,
13405
+ durationMs,
13406
+ timestamp: /* @__PURE__ */ new Date(),
13407
+ ip: null,
13408
+ userAgent: null
13409
+ });
13410
+ } catch {
13411
+ }
13412
+ }
12755
13413
  function isAuthorized(request) {
12756
13414
  const authHeader = request.headers.get("Authorization");
12757
13415
  if (!authHeader) return false;
@@ -12762,64 +13420,144 @@ function createScimModule(config, db) {
12762
13420
  const url = new URL(request.url);
12763
13421
  return `${url.protocol}//${url.host}`;
12764
13422
  }
13423
+ function parseSortSpec(url) {
13424
+ const by = url.searchParams.get("sortBy");
13425
+ if (!by) return null;
13426
+ const order = url.searchParams.get("sortOrder")?.toLowerCase();
13427
+ return {
13428
+ by: by.split(".").filter((p) => p.length > 0),
13429
+ order: order === "descending" ? "descending" : "ascending"
13430
+ };
13431
+ }
13432
+ function sortResources(items, spec) {
13433
+ const direction = spec.order === "descending" ? -1 : 1;
13434
+ return [...items].sort((a, b) => {
13435
+ const av = readAttr(a.scim, spec.by);
13436
+ const bv = readAttr(b.scim, spec.by);
13437
+ const primary = compareSortValues(av, bv);
13438
+ if (primary !== 0) return primary * direction;
13439
+ const ida = a.scim.id ?? "";
13440
+ const idb = b.scim.id ?? "";
13441
+ return ida < idb ? -1 : ida > idb ? 1 : 0;
13442
+ });
13443
+ }
13444
+ function readAttr(resource, path) {
13445
+ let current = resource;
13446
+ for (const segment of path) {
13447
+ if (current === void 0 || current === null) return void 0;
13448
+ if (typeof current !== "object") return void 0;
13449
+ current = current[segment];
13450
+ }
13451
+ return current;
13452
+ }
13453
+ function compareSortValues(a, b) {
13454
+ if (a === void 0 && b === void 0) return 0;
13455
+ if (a === void 0) return 1;
13456
+ if (b === void 0) return -1;
13457
+ if (typeof a === "string" && typeof b === "string") {
13458
+ return a.toLowerCase().localeCompare(b.toLowerCase());
13459
+ }
13460
+ if (typeof a === "number" && typeof b === "number") {
13461
+ return a - b;
13462
+ }
13463
+ if (typeof a === "boolean" && typeof b === "boolean") {
13464
+ return a === b ? 0 : a ? 1 : -1;
13465
+ }
13466
+ return String(a).localeCompare(String(b));
13467
+ }
12765
13468
  async function handleListUsers(request) {
12766
13469
  const url = new URL(request.url);
12767
13470
  const filterParam = url.searchParams.get("filter") ?? "";
12768
13471
  const startIndex = Math.max(1, parseInt(url.searchParams.get("startIndex") ?? "1", 10));
12769
13472
  const count = Math.min(200, parseInt(url.searchParams.get("count") ?? "100", 10));
12770
13473
  const baseUrl = getBaseUrl(request);
12771
- const conditions = [];
13474
+ const sortSpec = parseSortSpec(url);
13475
+ let ast = null;
13476
+ let clauses = null;
12772
13477
  if (filterParam) {
12773
- const clauses = parseScimFilter(filterParam);
12774
- for (const clause of clauses) {
12775
- if (clause.attribute === "username" && clause.value !== void 0) {
12776
- if (clause.op === "eq") {
12777
- conditions.push(
12778
- sql`(${users.username} = ${clause.value} OR ${users.email} = ${clause.value})`
12779
- );
12780
- } else if (clause.op === "co") {
12781
- conditions.push(
12782
- sql`(${users.username} LIKE ${`%${clause.value}%`} OR ${users.email} LIKE ${`%${clause.value}%`})`
12783
- );
12784
- } else if (clause.op === "sw") {
12785
- conditions.push(
12786
- sql`(${users.username} LIKE ${`${clause.value}%`} OR ${users.email} LIKE ${`${clause.value}%`})`
12787
- );
12788
- }
12789
- } else if (clause.attribute === "emails.value" && clause.value !== void 0) {
12790
- if (clause.op === "eq") {
12791
- conditions.push(eq(users.email, clause.value));
12792
- } else if (clause.op === "co") {
12793
- conditions.push(like(users.email, `%${clause.value}%`));
12794
- } else if (clause.op === "sw") {
12795
- conditions.push(like(users.email, `${clause.value}%`));
12796
- }
12797
- } else if (clause.attribute === "externalid" && clause.value !== void 0) {
12798
- if (clause.op === "eq") {
12799
- conditions.push(eq(users.externalId, clause.value));
12800
- }
12801
- } else if (clause.attribute === "active") {
12802
- if (clause.op === "eq" && clause.value === "false") {
12803
- conditions.push(eq(users.banned, 1));
12804
- } else if (clause.op === "eq" && clause.value === "true") {
12805
- conditions.push(eq(users.banned, 0));
12806
- }
13478
+ try {
13479
+ const parsed = parseScimFilter(filterParam);
13480
+ ast = parsed.ast;
13481
+ clauses = parsed.clauses;
13482
+ } catch (err2) {
13483
+ if (err2 instanceof ScimFilterError) {
13484
+ return scimError(err2.message, 400, "invalidFilter");
12807
13485
  }
13486
+ throw err2;
12808
13487
  }
12809
13488
  }
12810
- const whereClause = conditions.length > 0 ? and(...conditions) : void 0;
12811
- const countRows = await db.select({ count: sql`count(*)` }).from(users).where(whereClause);
12812
- const totalResults = Number(countRows[0]?.count ?? 0);
12813
- const offset = startIndex - 1;
12814
- const rows = await db.select().from(users).where(whereClause).limit(count).offset(offset);
13489
+ const conditions = clauses ? buildUserSqlConditions(clauses) : null;
13490
+ const whereClause = conditions && conditions.length > 0 ? and(...conditions) : void 0;
13491
+ if (conditions !== null && !sortSpec) {
13492
+ const countRows = await db.select({ count: sql`count(*)` }).from(users).where(whereClause);
13493
+ const totalResults2 = Number(countRows[0]?.count ?? 0);
13494
+ const offset = startIndex - 1;
13495
+ const rows = await db.select().from(users).where(whereClause).limit(count).offset(offset);
13496
+ return scimResponse({
13497
+ schemas: [SCHEMA_LIST],
13498
+ totalResults: totalResults2,
13499
+ startIndex,
13500
+ itemsPerPage: rows.length,
13501
+ Resources: rows.map((r) => userRowToScim(r, baseUrl))
13502
+ });
13503
+ }
13504
+ const allRows = await db.select().from(users);
13505
+ const mapped = allRows.map((r) => ({ row: r, scim: userRowToScim(r, baseUrl) }));
13506
+ const matched = ast ? mapped.filter(({ scim: scim2 }) => evaluateFilter(ast, scim2)) : mapped;
13507
+ const sorted = sortSpec ? sortResources(matched, sortSpec) : matched;
13508
+ const totalResults = sorted.length;
13509
+ const page = sorted.slice(startIndex - 1, startIndex - 1 + count);
12815
13510
  return scimResponse({
12816
13511
  schemas: [SCHEMA_LIST],
12817
13512
  totalResults,
12818
13513
  startIndex,
12819
- itemsPerPage: rows.length,
12820
- Resources: rows.map((r) => userRowToScim(r, baseUrl))
13514
+ itemsPerPage: page.length,
13515
+ Resources: page.map((p) => p.scim)
12821
13516
  });
12822
13517
  }
13518
+ function buildUserSqlConditions(list) {
13519
+ const conds = [];
13520
+ for (const clause of list) {
13521
+ if (clause.attribute === "username" && clause.value !== void 0) {
13522
+ if (clause.op === "eq") {
13523
+ conds.push(
13524
+ sql`(${users.username} = ${clause.value} OR ${users.email} = ${clause.value})`
13525
+ );
13526
+ } else if (clause.op === "co") {
13527
+ conds.push(
13528
+ sql`(${users.username} LIKE ${`%${clause.value}%`} OR ${users.email} LIKE ${`%${clause.value}%`})`
13529
+ );
13530
+ } else if (clause.op === "sw") {
13531
+ conds.push(
13532
+ sql`(${users.username} LIKE ${`${clause.value}%`} OR ${users.email} LIKE ${`${clause.value}%`})`
13533
+ );
13534
+ } else {
13535
+ return null;
13536
+ }
13537
+ continue;
13538
+ }
13539
+ if (clause.attribute === "emails.value" && clause.value !== void 0) {
13540
+ if (clause.op === "eq") conds.push(eq(users.email, clause.value));
13541
+ else if (clause.op === "co") conds.push(like(users.email, `%${clause.value}%`));
13542
+ else if (clause.op === "sw") conds.push(like(users.email, `${clause.value}%`));
13543
+ else return null;
13544
+ continue;
13545
+ }
13546
+ if (clause.attribute === "externalid" && clause.value !== void 0) {
13547
+ if (clause.op === "eq") conds.push(eq(users.externalId, clause.value));
13548
+ else return null;
13549
+ continue;
13550
+ }
13551
+ if (clause.attribute === "active") {
13552
+ if (clause.op === "eq" && clause.value === "false") conds.push(eq(users.banned, 1));
13553
+ else if (clause.op === "eq" && clause.value === "true") conds.push(eq(users.banned, 0));
13554
+ else return null;
13555
+ continue;
13556
+ }
13557
+ return null;
13558
+ }
13559
+ return conds;
13560
+ }
12823
13561
  async function handleGetUser(request, userId) {
12824
13562
  const baseUrl = getBaseUrl(request);
12825
13563
  const rows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
@@ -12828,6 +13566,7 @@ function createScimModule(config, db) {
12828
13566
  return scimResponse(userRowToScim(row, baseUrl));
12829
13567
  }
12830
13568
  async function handleCreateUser(request) {
13569
+ const auditStart = performance.now();
12831
13570
  if (!autoCreateUsers) {
12832
13571
  return scimError("User provisioning is disabled", 403);
12833
13572
  }
@@ -12862,6 +13601,10 @@ function createScimModule(config, db) {
12862
13601
  };
12863
13602
  if (givenName) metadata["scim:givenName"] = givenName;
12864
13603
  if (familyName) metadata["scim:familyName"] = familyName;
13604
+ const enterpriseExt = body[SCHEMA_ENTERPRISE];
13605
+ if (enterpriseExt && typeof enterpriseExt === "object") {
13606
+ metadata["scim:enterprise"] = enterpriseExt;
13607
+ }
12865
13608
  await db.insert(users).values({
12866
13609
  id,
12867
13610
  email: primaryEmail,
@@ -12888,9 +13631,16 @@ function createScimModule(config, db) {
12888
13631
  await onProvision(scimUser);
12889
13632
  }
12890
13633
  const baseUrl = getBaseUrl(request);
13634
+ await writeScimAudit(
13635
+ "scim.user.create",
13636
+ `scim:users:${id}`,
13637
+ id,
13638
+ Math.round(performance.now() - auditStart)
13639
+ );
12891
13640
  return scimResponse(userRowToScim(created, baseUrl), 201);
12892
13641
  }
12893
13642
  async function handleReplaceUser(request, userId) {
13643
+ const auditStart = performance.now();
12894
13644
  const rows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
12895
13645
  const existing = rows[0];
12896
13646
  if (!existing) return scimError("User not found", 404, "noTarget");
@@ -12917,6 +13667,10 @@ function createScimModule(config, db) {
12917
13667
  };
12918
13668
  if (givenName !== void 0) metadata["scim:givenName"] = givenName;
12919
13669
  if (familyName !== void 0) metadata["scim:familyName"] = familyName;
13670
+ const enterpriseExt = body[SCHEMA_ENTERPRISE];
13671
+ if (enterpriseExt && typeof enterpriseExt === "object") {
13672
+ metadata["scim:enterprise"] = enterpriseExt;
13673
+ }
12920
13674
  const now = /* @__PURE__ */ new Date();
12921
13675
  await db.update(users).set({
12922
13676
  email: primaryEmail,
@@ -12929,9 +13683,16 @@ function createScimModule(config, db) {
12929
13683
  }).where(eq(users.id, userId));
12930
13684
  const updatedRows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
12931
13685
  const baseUrl = getBaseUrl(request);
13686
+ await writeScimAudit(
13687
+ "scim.user.replace",
13688
+ `scim:users:${userId}`,
13689
+ userId,
13690
+ Math.round(performance.now() - auditStart)
13691
+ );
12932
13692
  return scimResponse(userRowToScim(updatedRows[0], baseUrl));
12933
13693
  }
12934
13694
  async function handlePatchUser(request, userId) {
13695
+ const auditStart = performance.now();
12935
13696
  const rows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
12936
13697
  const current = rows[0];
12937
13698
  if (!current) return scimError("User not found", 404, "noTarget");
@@ -12944,76 +13705,68 @@ function createScimModule(config, db) {
12944
13705
  if (!body.schemas?.includes(SCHEMA_PATCH) || !Array.isArray(body.Operations) || body.Operations.length === 0) {
12945
13706
  return scimError("Invalid PATCH body: missing Operations", 400, "invalidValue");
12946
13707
  }
13708
+ const baseUrlForPatch = getBaseUrl(request);
13709
+ const scimView = userRowToScim(current, baseUrlForPatch);
13710
+ try {
13711
+ applyPatchOps(scimView, body.Operations, {
13712
+ readonlyPaths: ["id", "externalid"]
13713
+ });
13714
+ } catch (err2) {
13715
+ if (err2 instanceof ScimPatchError) {
13716
+ return scimError(err2.message, err2.status, err2.scimType);
13717
+ }
13718
+ throw err2;
13719
+ }
12947
13720
  const updatedMeta = { ...current.metadata ?? {} };
12948
13721
  let email = current.email;
12949
13722
  let name = current.name;
12950
13723
  let username = current.username;
12951
13724
  let banned = current.banned;
12952
- let externalId = current.externalId;
12953
- for (const op of body.Operations) {
12954
- const opLower = op.op?.toLowerCase();
12955
- if (!opLower || !["add", "replace", "remove"].includes(opLower)) continue;
12956
- const path = op.path?.toLowerCase();
12957
- if (opLower === "remove") {
12958
- if (path === "active") {
12959
- updatedMeta["scim:active"] = false;
12960
- banned = autoDeactivateUsers ? 1 : 0;
12961
- }
12962
- continue;
12963
- }
12964
- if (path === "active" || path === "urn:ietf:params:scim:schemas:core:2.0:user:active") {
12965
- const active = op.value === true || op.value === "true";
12966
- updatedMeta["scim:active"] = active;
12967
- banned = active ? 0 : autoDeactivateUsers ? 1 : 0;
12968
- } else if (path === "username" || path === "urn:ietf:params:scim:schemas:core:2.0:user:username") {
12969
- username = typeof op.value === "string" ? op.value : username;
12970
- } else if (path === "displayname") {
12971
- name = typeof op.value === "string" ? op.value : name;
12972
- } else if (path === "name.givenname") {
12973
- updatedMeta["scim:givenName"] = op.value;
12974
- } else if (path === "name.familyname") {
12975
- updatedMeta["scim:familyName"] = op.value;
12976
- } else if (path === "externalid") {
12977
- externalId = typeof op.value === "string" ? op.value : externalId;
12978
- } else if (path === "emails" || path?.startsWith("emails[")) {
12979
- const emailsVal = op.value;
12980
- if (Array.isArray(emailsVal)) {
12981
- const primary = emailsVal.find((e) => e.primary)?.value ?? emailsVal[0]?.value;
12982
- if (primary) email = primary;
12983
- }
12984
- } else if (!path) {
12985
- const val = op.value;
12986
- if (val && typeof val === "object") {
12987
- if ("active" in val) {
12988
- const active = val.active === true || val.active === "true";
12989
- updatedMeta["scim:active"] = active;
12990
- banned = active ? 0 : autoDeactivateUsers ? 1 : 0;
12991
- }
12992
- if ("displayName" in val && typeof val.displayName === "string") {
12993
- name = val.displayName;
12994
- }
12995
- if ("userName" in val && typeof val.userName === "string") {
12996
- username = val.userName;
12997
- }
12998
- if ("externalId" in val && typeof val.externalId === "string") {
12999
- externalId = val.externalId;
13000
- }
13001
- const nameVal = val.name;
13002
- if (nameVal) {
13003
- if (nameVal.givenName !== void 0) updatedMeta["scim:givenName"] = nameVal.givenName;
13004
- if (nameVal.familyName !== void 0)
13005
- updatedMeta["scim:familyName"] = nameVal.familyName;
13006
- }
13007
- }
13725
+ const externalId = current.externalId;
13726
+ const scimUserName = scimView.userName;
13727
+ if (typeof scimUserName === "string") username = scimUserName;
13728
+ const scimDisplay = scimView.displayName;
13729
+ if (typeof scimDisplay === "string") name = scimDisplay;
13730
+ const scimName = scimView.name;
13731
+ if (scimName) {
13732
+ if (scimName.givenName !== void 0) updatedMeta["scim:givenName"] = scimName.givenName;
13733
+ if (scimName.familyName !== void 0) updatedMeta["scim:familyName"] = scimName.familyName;
13734
+ }
13735
+ const scimActive = scimView.active;
13736
+ if (typeof scimActive === "boolean") {
13737
+ updatedMeta["scim:active"] = scimActive;
13738
+ banned = scimActive ? 0 : autoDeactivateUsers ? 1 : 0;
13739
+ } else if (scimActive === void 0 && scimView.active === void 0) {
13740
+ updatedMeta["scim:active"] = false;
13741
+ banned = autoDeactivateUsers ? 1 : 0;
13742
+ }
13743
+ const scimEmails = scimView.emails;
13744
+ if (Array.isArray(scimEmails)) {
13745
+ const primary = scimEmails.find(
13746
+ (entry) => typeof entry === "object" && entry !== null && entry.primary === true
13747
+ )?.value ?? scimEmails[0]?.value;
13748
+ if (typeof primary === "string" && primary.length > 0) {
13749
+ email = primary;
13008
13750
  }
13009
13751
  }
13752
+ const extKey = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
13753
+ const enterpriseExt = scimView[extKey];
13754
+ if (enterpriseExt && typeof enterpriseExt === "object") {
13755
+ updatedMeta["scim:enterprise"] = enterpriseExt;
13756
+ }
13010
13757
  const now = /* @__PURE__ */ new Date();
13011
13758
  await db.update(users).set({ email, name, username, banned, externalId, metadata: updatedMeta, updatedAt: now }).where(eq(users.id, userId));
13012
13759
  const updatedRows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
13013
- const baseUrl = getBaseUrl(request);
13014
- return scimResponse(userRowToScim(updatedRows[0], baseUrl));
13760
+ await writeScimAudit(
13761
+ "scim.user.update",
13762
+ `scim:users:${userId}`,
13763
+ userId,
13764
+ Math.round(performance.now() - auditStart)
13765
+ );
13766
+ return scimResponse(userRowToScim(updatedRows[0], baseUrlForPatch));
13015
13767
  }
13016
13768
  async function handleDeleteUser(_request, userId) {
13769
+ const auditStart = performance.now();
13017
13770
  const rows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
13018
13771
  const row = rows[0];
13019
13772
  if (!row) return scimError("User not found", 404, "noTarget");
@@ -13027,6 +13780,12 @@ function createScimModule(config, db) {
13027
13780
  if (onDeprovision) {
13028
13781
  await onDeprovision(userId);
13029
13782
  }
13783
+ await writeScimAudit(
13784
+ "scim.user.delete",
13785
+ `scim:users:${userId}`,
13786
+ userId,
13787
+ Math.round(performance.now() - auditStart)
13788
+ );
13030
13789
  return new Response(null, { status: 204 });
13031
13790
  }
13032
13791
  async function getMembersForOrg(orgId) {
@@ -13044,41 +13803,74 @@ function createScimModule(config, db) {
13044
13803
  const startIndex = Math.max(1, parseInt(url.searchParams.get("startIndex") ?? "1", 10));
13045
13804
  const count = Math.min(200, parseInt(url.searchParams.get("count") ?? "100", 10));
13046
13805
  const baseUrl = getBaseUrl(request);
13047
- const conditions = [];
13806
+ const sortSpec = parseSortSpec(url);
13807
+ let ast = null;
13808
+ let clauses = null;
13048
13809
  if (filterParam) {
13049
- const clauses = parseScimFilter(filterParam);
13050
- for (const clause of clauses) {
13051
- if (clause.attribute === "displayname" && clause.value !== void 0) {
13052
- if (clause.op === "eq") {
13053
- conditions.push(eq(organizations.name, clause.value));
13054
- } else if (clause.op === "co") {
13055
- conditions.push(like(organizations.name, `%${clause.value}%`));
13056
- } else if (clause.op === "sw") {
13057
- conditions.push(like(organizations.name, `${clause.value}%`));
13058
- }
13810
+ try {
13811
+ const parsed = parseScimFilter(filterParam);
13812
+ ast = parsed.ast;
13813
+ clauses = parsed.clauses;
13814
+ } catch (err2) {
13815
+ if (err2 instanceof ScimFilterError) {
13816
+ return scimError(err2.message, 400, "invalidFilter");
13059
13817
  }
13818
+ throw err2;
13060
13819
  }
13061
13820
  }
13062
- const whereClause = conditions.length > 0 ? and(...conditions) : void 0;
13063
- const countRows = await db.select({ count: sql`count(*)` }).from(organizations).where(whereClause);
13064
- const totalResults = Number(countRows[0]?.count ?? 0);
13065
- const offset = startIndex - 1;
13066
- const orgRows = await db.select().from(organizations).where(whereClause).limit(count).offset(offset);
13821
+ const conditions = clauses ? buildGroupSqlConditions(clauses) : null;
13822
+ const whereClause = conditions && conditions.length > 0 ? and(...conditions) : void 0;
13823
+ if (conditions !== null && !sortSpec) {
13824
+ const countRows = await db.select({ count: sql`count(*)` }).from(organizations).where(whereClause);
13825
+ const totalResults2 = Number(countRows[0]?.count ?? 0);
13826
+ const offset = startIndex - 1;
13827
+ const orgRows = await db.select().from(organizations).where(whereClause).limit(count).offset(offset);
13828
+ const resources2 = await Promise.all(
13829
+ orgRows.map(async (org) => {
13830
+ const members = await getMembersForOrg(org.id);
13831
+ const memberEmails = await getMemberEmails(members);
13832
+ return orgRowToScim(org, members, memberEmails, baseUrl);
13833
+ })
13834
+ );
13835
+ return scimResponse({
13836
+ schemas: [SCHEMA_LIST],
13837
+ totalResults: totalResults2,
13838
+ startIndex,
13839
+ itemsPerPage: orgRows.length,
13840
+ Resources: resources2
13841
+ });
13842
+ }
13843
+ const allOrgs = await db.select().from(organizations);
13067
13844
  const resources = await Promise.all(
13068
- orgRows.map(async (org) => {
13845
+ allOrgs.map(async (org) => {
13069
13846
  const members = await getMembersForOrg(org.id);
13070
13847
  const memberEmails = await getMemberEmails(members);
13071
- return orgRowToScim(org, members, memberEmails, baseUrl);
13848
+ return { org, scim: orgRowToScim(org, members, memberEmails, baseUrl) };
13072
13849
  })
13073
13850
  );
13851
+ const matched = ast ? resources.filter(({ scim: scim2 }) => evaluateFilter(ast, scim2)) : resources;
13852
+ const sorted = sortSpec ? sortResources(matched, sortSpec) : matched;
13853
+ const totalResults = sorted.length;
13854
+ const page = sorted.slice(startIndex - 1, startIndex - 1 + count);
13074
13855
  return scimResponse({
13075
13856
  schemas: [SCHEMA_LIST],
13076
13857
  totalResults,
13077
13858
  startIndex,
13078
- itemsPerPage: orgRows.length,
13079
- Resources: resources
13859
+ itemsPerPage: page.length,
13860
+ Resources: page.map((p) => p.scim)
13080
13861
  });
13081
13862
  }
13863
+ function buildGroupSqlConditions(list) {
13864
+ const conds = [];
13865
+ for (const clause of list) {
13866
+ if (clause.attribute !== "displayname" || clause.value === void 0) return null;
13867
+ if (clause.op === "eq") conds.push(eq(organizations.name, clause.value));
13868
+ else if (clause.op === "co") conds.push(like(organizations.name, `%${clause.value}%`));
13869
+ else if (clause.op === "sw") conds.push(like(organizations.name, `${clause.value}%`));
13870
+ else return null;
13871
+ }
13872
+ return conds;
13873
+ }
13082
13874
  async function handleGetGroup(request, groupId) {
13083
13875
  const baseUrl = getBaseUrl(request);
13084
13876
  const orgRows = await db.select().from(organizations).where(eq(organizations.id, groupId)).limit(1);
@@ -13089,6 +13881,7 @@ function createScimModule(config, db) {
13089
13881
  return scimResponse(orgRowToScim(orgRow, members, memberEmails, baseUrl));
13090
13882
  }
13091
13883
  async function handleCreateGroup(request) {
13884
+ const auditStart = performance.now();
13092
13885
  let body;
13093
13886
  try {
13094
13887
  body = await request.json();
@@ -13147,12 +13940,19 @@ function createScimModule(config, db) {
13147
13940
  const createdMembers = await getMembersForOrg(id);
13148
13941
  const memberEmails = await getMemberEmails(createdMembers);
13149
13942
  const baseUrl = getBaseUrl(request);
13943
+ await writeScimAudit(
13944
+ "scim.group.create",
13945
+ `scim:groups:${id}`,
13946
+ ownerId,
13947
+ Math.round(performance.now() - auditStart)
13948
+ );
13150
13949
  return scimResponse(
13151
13950
  orgRowToScim(orgRows[0], createdMembers, memberEmails, baseUrl),
13152
13951
  201
13153
13952
  );
13154
13953
  }
13155
13954
  async function handleReplaceGroup(request, groupId) {
13955
+ const auditStart = performance.now();
13156
13956
  const orgRows = await db.select().from(organizations).where(eq(organizations.id, groupId)).limit(1);
13157
13957
  const existingOrg = orgRows[0];
13158
13958
  if (!existingOrg) return scimError("Group not found", 404, "noTarget");
@@ -13188,9 +13988,16 @@ function createScimModule(config, db) {
13188
13988
  const members = await getMembersForOrg(groupId);
13189
13989
  const memberEmails = await getMemberEmails(members);
13190
13990
  const baseUrl = getBaseUrl(request);
13991
+ await writeScimAudit(
13992
+ "scim.group.replace",
13993
+ `scim:groups:${groupId}`,
13994
+ updatedOrg[0].ownerId,
13995
+ Math.round(performance.now() - auditStart)
13996
+ );
13191
13997
  return scimResponse(orgRowToScim(updatedOrg[0], members, memberEmails, baseUrl));
13192
13998
  }
13193
13999
  async function handlePatchGroup(request, groupId) {
14000
+ const auditStart = performance.now();
13194
14001
  const orgRows = await db.select().from(organizations).where(eq(organizations.id, groupId)).limit(1);
13195
14002
  const current = orgRows[0];
13196
14003
  if (!current) return scimError("Group not found", 404, "noTarget");
@@ -13258,13 +14065,27 @@ function createScimModule(config, db) {
13258
14065
  const members = await getMembersForOrg(groupId);
13259
14066
  const memberEmails = await getMemberEmails(members);
13260
14067
  const baseUrl = getBaseUrl(request);
14068
+ await writeScimAudit(
14069
+ "scim.group.update",
14070
+ `scim:groups:${groupId}`,
14071
+ updatedOrg[0].ownerId,
14072
+ Math.round(performance.now() - auditStart)
14073
+ );
13261
14074
  return scimResponse(orgRowToScim(updatedOrg[0], members, memberEmails, baseUrl));
13262
14075
  }
13263
14076
  async function handleDeleteGroup(_request, groupId) {
14077
+ const auditStart = performance.now();
13264
14078
  const orgRows = await db.select().from(organizations).where(eq(organizations.id, groupId)).limit(1);
13265
- if (orgRows.length === 0) return scimError("Group not found", 404, "noTarget");
14079
+ const existing = orgRows[0];
14080
+ if (!existing) return scimError("Group not found", 404, "noTarget");
13266
14081
  await db.delete(orgMembers).where(eq(orgMembers.orgId, groupId));
13267
14082
  await db.delete(organizations).where(eq(organizations.id, groupId));
14083
+ await writeScimAudit(
14084
+ "scim.group.delete",
14085
+ `scim:groups:${groupId}`,
14086
+ existing.ownerId,
14087
+ Math.round(performance.now() - auditStart)
14088
+ );
13268
14089
  return new Response(null, { status: 204 });
13269
14090
  }
13270
14091
  function handleServiceProviderConfig(request) {
@@ -13276,7 +14097,7 @@ function createScimModule(config, db) {
13276
14097
  bulk: { supported: false, maxOperations: 0, maxPayloadSize: 0 },
13277
14098
  filter: { supported: true, maxResults: 200 },
13278
14099
  changePassword: { supported: false },
13279
- sort: { supported: false },
14100
+ sort: { supported: true },
13280
14101
  etag: { supported: false },
13281
14102
  authenticationSchemes: [
13282
14103
  {
@@ -13297,9 +14118,9 @@ function createScimModule(config, db) {
13297
14118
  const baseUrl = getBaseUrl(request);
13298
14119
  return scimResponse({
13299
14120
  schemas: [SCHEMA_LIST],
13300
- totalResults: 2,
14121
+ totalResults: 3,
13301
14122
  startIndex: 1,
13302
- itemsPerPage: 2,
14123
+ itemsPerPage: 3,
13303
14124
  Resources: [
13304
14125
  {
13305
14126
  schemas: ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
@@ -13342,6 +14163,39 @@ function createScimModule(config, db) {
13342
14163
  resourceType: "Schema",
13343
14164
  location: `${baseUrl}/scim/v2/Schemas/${SCHEMA_GROUP}`
13344
14165
  }
14166
+ },
14167
+ {
14168
+ schemas: ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
14169
+ id: SCHEMA_ENTERPRISE,
14170
+ name: "EnterpriseUser",
14171
+ description: "Enterprise User extension (RFC 7643 \xA74.3). Carries employeeNumber, department, and manager.",
14172
+ attributes: [
14173
+ {
14174
+ name: "employeeNumber",
14175
+ type: "string",
14176
+ required: false,
14177
+ mutability: "readWrite"
14178
+ },
14179
+ { name: "department", type: "string", required: false, mutability: "readWrite" },
14180
+ { name: "costCenter", type: "string", required: false, mutability: "readWrite" },
14181
+ { name: "organization", type: "string", required: false, mutability: "readWrite" },
14182
+ { name: "division", type: "string", required: false, mutability: "readWrite" },
14183
+ {
14184
+ name: "manager",
14185
+ type: "complex",
14186
+ required: false,
14187
+ mutability: "readWrite",
14188
+ subAttributes: [
14189
+ { name: "value", type: "string" },
14190
+ { name: "$ref", type: "reference" },
14191
+ { name: "displayName", type: "string" }
14192
+ ]
14193
+ }
14194
+ ],
14195
+ meta: {
14196
+ resourceType: "Schema",
14197
+ location: `${baseUrl}/scim/v2/Schemas/${SCHEMA_ENTERPRISE}`
14198
+ }
13345
14199
  }
13346
14200
  ]
13347
14201
  });
@@ -13381,6 +14235,45 @@ function createScimModule(config, db) {
13381
14235
  ]
13382
14236
  });
13383
14237
  }
14238
+ const SCHEMA_BULK_REQUEST = "urn:ietf:params:scim:api:messages:2.0:BulkRequest";
14239
+ function handleBulk(request) {
14240
+ const contentType = request.headers.get("Content-Type") ?? "";
14241
+ if (!contentType.toLowerCase().startsWith(SCIM_CONTENT_TYPE)) {
14242
+ return scimError(`Bulk requires Content-Type "${SCIM_CONTENT_TYPE}"`, 415, "invalidValue");
14243
+ }
14244
+ return scimResponse(
14245
+ {
14246
+ schemas: [SCHEMA_ERROR],
14247
+ status: "501",
14248
+ detail: `Bulk operations are not supported by this server. See /scim/v2/ServiceProviderConfig for supported features. Requested schema: ${SCHEMA_BULK_REQUEST}`
14249
+ },
14250
+ 501
14251
+ );
14252
+ }
14253
+ async function handleMe(request) {
14254
+ if (!resolveSelf) {
14255
+ return scimError(
14256
+ "Me endpoint requires a resolveSelf configuration callback",
14257
+ 501,
14258
+ "invalidValue"
14259
+ );
14260
+ }
14261
+ let userId;
14262
+ try {
14263
+ userId = await resolveSelf(request);
14264
+ } catch {
14265
+ return scimError("resolveSelf threw while resolving the current user", 500);
14266
+ }
14267
+ if (!userId) {
14268
+ return scimError("No user is associated with this bearer token", 404, "noTarget");
14269
+ }
14270
+ const rows = await db.select().from(users).where(eq(users.id, userId)).limit(1);
14271
+ const row = rows[0];
14272
+ if (!row) {
14273
+ return scimError("Resolved user does not exist", 404, "noTarget");
14274
+ }
14275
+ return scimResponse(userRowToScim(row, getBaseUrl(request)));
14276
+ }
13384
14277
  async function handleRequest(request) {
13385
14278
  const url = new URL(request.url);
13386
14279
  const pathname = url.pathname;
@@ -13398,6 +14291,12 @@ function createScimModule(config, db) {
13398
14291
  if (method === "GET" && pathname.endsWith("/scim/v2/ResourceTypes")) {
13399
14292
  return handleResourceTypes(request);
13400
14293
  }
14294
+ if (method === "GET" && pathname.endsWith("/scim/v2/Me")) {
14295
+ return handleMe(request);
14296
+ }
14297
+ if (method === "POST" && pathname.endsWith("/scim/v2/Bulk")) {
14298
+ return handleBulk(request);
14299
+ }
13401
14300
  if (method === "GET" && /\/scim\/v2\/Users\/?$/.test(pathname)) {
13402
14301
  return handleListUsers(request);
13403
14302
  }
@@ -13432,7 +14331,7 @@ function createScimModule(config, db) {
13432
14331
  // src/auth/scim-plugin.ts
13433
14332
  function scim(config) {
13434
14333
  return {
13435
- id: "kavach-scim",
14334
+ id: "theauth-scim",
13436
14335
  async init(ctx) {
13437
14336
  const module = createScimModule(config, ctx.db);
13438
14337
  ctx.addEndpoint({
@@ -13459,6 +14358,22 @@ function scim(config) {
13459
14358
  return await module.handleRequest(request);
13460
14359
  }
13461
14360
  });
14361
+ ctx.addEndpoint({
14362
+ method: "GET",
14363
+ path: "/scim/v2/Me",
14364
+ metadata: { description: "SCIM /Me: current user for the bearer token" },
14365
+ async handler(request) {
14366
+ return await module.handleRequest(request);
14367
+ }
14368
+ });
14369
+ ctx.addEndpoint({
14370
+ method: "POST",
14371
+ path: "/scim/v2/Bulk",
14372
+ metadata: { description: "SCIM /Bulk: returns 501 (bulk ops not supported)" },
14373
+ async handler(request) {
14374
+ return await module.handleRequest(request);
14375
+ }
14376
+ });
13462
14377
  ctx.addEndpoint({
13463
14378
  method: "GET",
13464
14379
  path: "/scim/v2/Users",
@@ -13728,7 +14643,7 @@ function createSiweModule(config) {
13728
14643
  }
13729
14644
  function siwe(config) {
13730
14645
  return {
13731
- id: "kavach-siwe",
14646
+ id: "theauth-siwe",
13732
14647
  async init(ctx) {
13733
14648
  const mod = createSiweModule(config);
13734
14649
  ctx.addEndpoint({
@@ -13736,7 +14651,7 @@ function siwe(config) {
13736
14651
  path: "/auth/siwe/nonce",
13737
14652
  metadata: {
13738
14653
  description: "Generate a SIWE nonce for wallet signing",
13739
- rateLimit: { window: 6e4, max: 60 }
14654
+ rateLimit: { window: 60, max: 60 }
13740
14655
  },
13741
14656
  async handler(_request, _endpointCtx) {
13742
14657
  const nonce = await mod.generateNonce();
@@ -13751,7 +14666,7 @@ function siwe(config) {
13751
14666
  path: "/auth/siwe/verify",
13752
14667
  metadata: {
13753
14668
  description: "Verify a SIWE-signed message and return the Ethereum address",
13754
- rateLimit: { window: 6e4, max: 20 }
14669
+ rateLimit: { window: 60, max: 20 }
13755
14670
  },
13756
14671
  async handler(request, _endpointCtx) {
13757
14672
  let body;
@@ -15114,7 +16029,7 @@ function createStripeModule(config, db) {
15114
16029
  const customer = await api("POST", "/customers", {
15115
16030
  email,
15116
16031
  ...name ? { name } : {},
15117
- metadata: { kavach_user_id: userId }
16032
+ metadata: { theauth_user_id: userId }
15118
16033
  });
15119
16034
  await db.update(users).set({ stripeCustomerId: customer.id, updatedAt: /* @__PURE__ */ new Date() }).where(eq(users.id, userId));
15120
16035
  return customer.id;
@@ -15268,7 +16183,7 @@ function createStripeModule(config, db) {
15268
16183
  // src/auth/stripe-plugin.ts
15269
16184
  function stripe(config) {
15270
16185
  return {
15271
- id: "kavach-stripe",
16186
+ id: "theauth-stripe",
15272
16187
  async init(ctx) {
15273
16188
  const module = createStripeModule(config, ctx.db);
15274
16189
  ctx.addEndpoint({
@@ -15639,7 +16554,7 @@ async function parseBody8(request) {
15639
16554
  }
15640
16555
  function twoFactor(config) {
15641
16556
  return {
15642
- id: "kavach-2fa",
16557
+ id: "theauth-2fa",
15643
16558
  async init(ctx) {
15644
16559
  const module = createTotpModule(config ?? {}, ctx.db);
15645
16560
  ctx.addEndpoint({
@@ -16089,9 +17004,9 @@ async function deliverOnce(endpoint, event, body, timestamp) {
16089
17004
  method: "POST",
16090
17005
  headers: {
16091
17006
  "Content-Type": "application/json",
16092
- "X-Kavach-Event": event,
16093
- "X-Kavach-Signature": `sha256=${signature}`,
16094
- "X-Kavach-Timestamp": timestamp
17007
+ "X-TheAuth-Event": event,
17008
+ "X-TheAuth-Signature": `sha256=${signature}`,
17009
+ "X-TheAuth-Timestamp": timestamp
16095
17010
  },
16096
17011
  body,
16097
17012
  signal: controller.signal