nucleus-core-ts 0.9.813 → 0.9.814

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.
@@ -5,6 +5,7 @@ import { useIntegrationsStore } from '../store';
5
5
  import { useLabels } from '../text/context';
6
6
  import { integrationsPageTheme } from '../theme';
7
7
  import { EndpointFormFields } from './EndpointFormFields';
8
+ import { asArray } from './jsonArray';
8
9
  import { formatJsonObject, parseJsonObject } from './jsonObjectField';
9
10
  import { PaginationFields } from './PaginationFields';
10
11
  import { ParamBindingRows } from './ParamBindingRows';
@@ -33,7 +34,7 @@ export function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted,
33
34
  pagination: endpoint?.pagination ?? {
34
35
  kind: 'none'
35
36
  },
36
- paramBindings: endpoint?.paramBindings ?? [],
37
+ paramBindings: asArray(endpoint?.paramBindings),
37
38
  queryParams: formatJsonObject(endpoint?.queryParams),
38
39
  requestBody: formatJsonObject(endpoint?.requestBody)
39
40
  });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Reading a jsonb column that is supposed to hold a list.
3
+ *
4
+ * It is not always a list. The schema generator wrote every object-shaped
5
+ * default — including `[]` — into Postgres as `'{}'`, which is an empty OBJECT.
6
+ * So every row that never had a value reads back as `{}`, and the first
7
+ * `for…of` over it throws "object is not iterable". That is every existing row
8
+ * the moment a new column is added to a table already in use, which is exactly
9
+ * when nobody is looking: the column is new, nothing has filled it in, and the
10
+ * screen that reads it dies on the next click.
11
+ *
12
+ * The generator is fixed. This exists because the rows it already wrote are
13
+ * still there, and because a value crossing the wire from a database is not
14
+ * something a component should have to trust.
15
+ */
16
+ export declare function asArray<T>(value: unknown): T[];
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Reading a jsonb column that is supposed to hold a list.
3
+ *
4
+ * It is not always a list. The schema generator wrote every object-shaped
5
+ * default — including `[]` — into Postgres as `'{}'`, which is an empty OBJECT.
6
+ * So every row that never had a value reads back as `{}`, and the first
7
+ * `for…of` over it throws "object is not iterable". That is every existing row
8
+ * the moment a new column is added to a table already in use, which is exactly
9
+ * when nobody is looking: the column is new, nothing has filled it in, and the
10
+ * screen that reads it dies on the next click.
11
+ *
12
+ * The generator is fixed. This exists because the rows it already wrote are
13
+ * still there, and because a value crossing the wire from a database is not
14
+ * something a component should have to trust.
15
+ */ export function asArray(value) {
16
+ return Array.isArray(value) ? value : [];
17
+ }
@@ -1,3 +1,4 @@
1
+ import { asArray } from './jsonArray';
1
2
  import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
2
3
  /** Upsert and ignore: the two choices that cannot lose a row by themselves. */ export function blankMappingDraft(endpointId) {
3
4
  return {
@@ -16,14 +17,14 @@ import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
16
17
  endpointId: mapping.endpointId,
17
18
  name: mapping.name,
18
19
  targetEntity: mapping.targetEntity,
19
- fieldMappings: mapping.fieldMappings,
20
+ fieldMappings: asArray(mapping.fieldMappings),
20
21
  dedupSourceField: mapping.dedupSourceField,
21
22
  dedupTargetField: mapping.dedupTargetField,
22
23
  writeMode: mapping.writeMode,
23
24
  missingRecordPolicy: mapping.missingRecordPolicy,
24
25
  missingRecordColumn: mapping.missingRecordColumn,
25
26
  missingRecordValue: mapping.missingRecordValue,
26
- referenceChecks: mapping.referenceChecks ?? [],
27
+ referenceChecks: asArray(mapping.referenceChecks),
27
28
  passwordTargetColumn: mapping.passwordTargetColumn ?? null,
28
29
  enabled: mapping.enabled
29
30
  };
@@ -38,9 +39,9 @@ import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
38
39
  if (draft.name.trim() === '') issues.push('Give the mapping a name.');
39
40
  if (draft.targetEntity.trim() === '') issues.push('Choose a target entity.');
40
41
  if (draft.endpointId.trim() === '') issues.push('Select the endpoint this mapping reads from.');
41
- const usable = draft.fieldMappings.some((row)=>row.target.trim() !== '' && (row.source.trim() !== '' || (row.constant ?? '').trim() !== ''));
42
+ const usable = asArray(draft.fieldMappings).some((row)=>row.target.trim() !== '' && (row.source.trim() !== '' || (row.constant ?? '').trim() !== ''));
42
43
  if (!usable) issues.push('Map at least one field to a target column.');
43
- for (const check of draft.referenceChecks){
44
+ for (const check of asArray(draft.referenceChecks)){
44
45
  if (check.column.trim() === '' || check.entity.trim() === '' || check.entityColumn.trim() === '') issues.push('Every reference needs a column, an entity and the column to match against.');
45
46
  }
46
47
  if (missingRecordFieldsIncomplete(draft.missingRecordPolicy, draft.missingRecordColumn ?? '', draft.missingRecordValue ?? '')) {
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { mappingDraftIssues, toMappingDraft } from './mappingDraft';
3
+ /**
4
+ * A jsonb column that is supposed to hold a list, and does not.
5
+ *
6
+ * The schema generator wrote every object-shaped default — `[]` included — into
7
+ * Postgres as `'{}'`, an empty OBJECT. So every row that never had a value reads
8
+ * back as `{}`. `for…of` over that throws "object is not iterable", which took
9
+ * the whole admin panel down to a blank error page the first time a mapping was
10
+ * opened after the column was added. The generator is fixed; these rows are
11
+ * still in the database.
12
+ */ describe('a list column that came back as an object', ()=>{
13
+ const mapping = (over)=>({
14
+ id: 'm1',
15
+ endpointId: 'e1',
16
+ name: 'Çalışanlar',
17
+ targetEntity: 'employees',
18
+ fieldMappings: [],
19
+ writeMode: 'upsert',
20
+ missingRecordPolicy: 'ignore',
21
+ ...over
22
+ });
23
+ it('reads an empty object as an empty list', ()=>{
24
+ const draft = toMappingDraft(mapping({
25
+ referenceChecks: {},
26
+ fieldMappings: {}
27
+ }));
28
+ expect(draft.referenceChecks).toEqual([]);
29
+ expect(draft.fieldMappings).toEqual([]);
30
+ });
31
+ it('keeps a real list', ()=>{
32
+ const checks = [
33
+ {
34
+ column: 'department',
35
+ entity: 'departments',
36
+ entityColumn: 'name',
37
+ onMissing: 'create'
38
+ }
39
+ ];
40
+ const draft = toMappingDraft(mapping({
41
+ referenceChecks: checks
42
+ }));
43
+ expect(draft.referenceChecks).toEqual(checks);
44
+ });
45
+ it('validates a draft whose lists arrived as objects instead of throwing', ()=>{
46
+ // This is the call that ran during render, so what it threw was not an error
47
+ // message on the screen — it was the screen.
48
+ const issues = mappingDraftIssues({
49
+ endpointId: 'e1',
50
+ name: 'Çalışanlar',
51
+ targetEntity: 'employees',
52
+ fieldMappings: {},
53
+ referenceChecks: {},
54
+ writeMode: 'upsert',
55
+ missingRecordPolicy: 'ignore'
56
+ });
57
+ expect(issues).toContain('Map at least one field to a target column.');
58
+ });
59
+ });
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ end
98
98
  redis.call('ZADD', KEYS[1], now, ARGV[4])
99
99
  redis.call('PEXPIRE', KEYS[1], windowMs + 1000)
100
100
  return {1, count + 1}
101
- `,RateLimiterBackendError,DEFAULT_AUTH_LOGIN,DEFAULT_AUTH_REGISTER,DEFAULT_AUTH_PASSWORD_RESET,DEFAULT_AUTH_MAGIC_LINK,DEFAULT_CONFIG4;var init_RateLimiter=__esm(()=>{RateLimiterBackendError=class RateLimiterBackendError extends Error{constructor(message){super(message);this.name="RateLimiterBackendError"}};DEFAULT_AUTH_LOGIN={window:"15m",max:5,blockDuration:"30m"},DEFAULT_AUTH_REGISTER={window:"1h",max:3,blockDuration:"1h"},DEFAULT_AUTH_PASSWORD_RESET={window:"1h",max:3,blockDuration:"1h"},DEFAULT_AUTH_MAGIC_LINK={window:"1h",max:5,blockDuration:"1h"},DEFAULT_CONFIG4={enabled:!0,strategy:"sliding-window",keyPrefix:"rl:",authRoutes:{window:"1m",max:10,login:DEFAULT_AUTH_LOGIN,register:DEFAULT_AUTH_REGISTER,passwordReset:DEFAULT_AUTH_PASSWORD_RESET,magicLink:DEFAULT_AUTH_MAGIC_LINK},publicRoutes:{window:"1m",max:100},privateRoutes:{window:"1m",max:60},byIp:!0,byUserId:!0,byEndpoint:!1,skipSuccessfulRequests:!1,headers:{remaining:"X-RateLimit-Remaining",reset:"X-RateLimit-Reset",limit:"X-RateLimit-Limit"},whitelist:[],blacklist:[]}});import{createCipheriv as createCipheriv2,createDecipheriv as createDecipheriv2,randomBytes as randomBytes4,scryptSync as scryptSync2,timingSafeEqual as timingSafeEqual2}from"crypto";var VERSION="v1",IV_LEN2=12,KEY_LEN=32,SCRYPT_SALT="nucleus-secrets-v1",keyCache,deriveKey2=(masterKey)=>{let cached=keyCache.get(masterKey);if(cached)return cached;let key2=scryptSync2(masterKey,SCRYPT_SALT,KEY_LEN);return keyCache.set(masterKey,key2),key2},encryptSecret=(plaintext,masterKey)=>{if(!masterKey)throw Error("secrets encryption key is empty");let iv=randomBytes4(IV_LEN2),cipher=createCipheriv2("aes-256-gcm",deriveKey2(masterKey),iv),ciphertext=Buffer.concat([cipher.update(plaintext,"utf-8"),cipher.final()]),tag=cipher.getAuthTag();return[VERSION,iv.toString("base64"),tag.toString("base64"),ciphertext.toString("base64")].join(":")},decryptSecret=(envelope,masterKey)=>{if(!masterKey)throw Error("secrets encryption key is empty");let parts=envelope.split(":");if(parts.length!==4||parts[0]!==VERSION)throw Error("not an encrypted secret envelope");let[,ivB64,tagB64,ctB64]=parts,decipher=createDecipheriv2("aes-256-gcm",deriveKey2(masterKey),Buffer.from(ivB64,"base64"));return decipher.setAuthTag(Buffer.from(tagB64,"base64")),Buffer.concat([decipher.update(Buffer.from(ctB64,"base64")),decipher.final()]).toString("utf-8")},previewOf=(plaintext)=>{let len=plaintext.length;if(len===0)return"";if(len<12)return`\u2022\u2022\u2022\u2022${len}`;return`\u2022\u2022\u2022\u2022${plaintext.slice(-4)}`};var init_crypto=__esm(()=>{keyCache=new Map});function discoverSlots(config){let slots=new Map,add=(slot)=>{let id=`${slot.scope}.${slot.key}`;if(!slots.has(id))slots.set(id,slot)},valueAt=(scope,key2)=>{let node=config;for(let segment of scope.split(".")){if(!isPlainObject(node))return;node=node[segment]}if(!isPlainObject(node))return;let value=node[key2];if(typeof value==="string")return value;if(Array.isArray(value)&&value.every((entry)=>typeof entry==="string"))return value.length>0?value.join(","):void 0;return};for(let descriptor of SECRET_SLOT_DESCRIPTORS)for(let scope of expandPath(config,descriptor.path,descriptor.always===!0)){let leaf=scope.split(".").pop()??descriptor.group,group=descriptor.path.endsWith("*")?`${descriptor.group}:${leaf}`:descriptor.group;for(let declared of descriptor.keys)add({scope,key:declared.key,kind:declared.kind??kindFor(declared.key),label:declared.label,group,required:declared.required??!1,requiredWhen:declared.requiredWhen,hint:declared.hint,secret:declared.secret??descriptor.secret??!0,configValue:valueAt(scope,declared.key)})}let walk=(node,path2,depth)=>{if(depth>6)return;for(let[key2,value]of Object.entries(node)){if(SKIP_SECTIONS.has(key2))continue;let childPath=path2?`${path2}.${key2}`:key2;if(isPlainObject(value)){walk(value,childPath,depth+1);continue}if(typeof value!=="string"&&value!==void 0&&value!==null)continue;if(!CREDENTIAL_KEY_PATTERN.test(key2))continue;if(NOT_A_CREDENTIAL.has(key2.toLowerCase()))continue;if(BOOT_ONLY_PATHS.has(`${path2}.${key2}`))continue;let owner=path2.split(".").pop(),label=owner&&owner.toLowerCase()!==key2.toLowerCase()?`${humanize(owner)} \u2014 ${humanize(key2)}`:humanize(key2);add({scope:path2,key:key2,kind:kindFor(key2),label,group:path2.split(".")[0]??"other",required:!1,secret:!0,configValue:typeof value==="string"?value:void 0})}};return walk(config,"",0),[...slots.values()].sort((a,b)=>a.group.localeCompare(b.group)||a.scope.localeCompare(b.scope)||a.key.localeCompare(b.key))}var CREDENTIAL_KEY_PATTERN,NOT_A_CREDENTIAL,BOOT_ONLY_PATHS,SKIP_SECTIONS,kindFor=(key2)=>{let k=key2.toLowerCase();if(k.includes("json"))return"json";if(k.includes("connection"))return"connection_string";if(k.includes("privatekey")||k.includes("private_key"))return"pem";if(k.includes("clientid")||k.includes("client_id"))return"text";return"password"},humanize=(key2)=>key2.replace(/[_-]+/g," ").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/\s+/g," ").trim().replace(/\b\w/g,(c)=>c.toUpperCase()),SECRET_SLOT_DESCRIPTORS,isPlainObject=(value)=>typeof value==="object"&&value!==null&&!Array.isArray(value),expandPath=(config,path2,always=!1)=>{let segments=path2.split("."),scopes2=[{path:"",node:config}];for(let segment of segments){let next=[];for(let current of scopes2){if(!isPlainObject(current.node))continue;if(segment==="*")for(let[childKey,childValue]of Object.entries(current.node))next.push({path:current.path?`${current.path}.${childKey}`:childKey,node:childValue});else if(segment in current.node)next.push({path:current.path?`${current.path}.${segment}`:segment,node:current.node[segment]});else if(always)next.push({path:current.path?`${current.path}.${segment}`:segment,node:{}})}scopes2=next}return scopes2.filter((s)=>isPlainObject(s.node)).map((s)=>s.path)},looksLikeEnvName=(value)=>!!value&&/^[A-Z][A-Z0-9_]{2,}$/.test(value);var init_registry=__esm(()=>{CREDENTIAL_KEY_PATTERN=/(secret|password|passwd|apikey|api_key|accesskey|access_key|privatekey|private_key|token|credential|connection_string|connectionstring|clientid|client_id|encryptionkey|encryption_key|servicekey|service_key|json_file_path|jsonfilepath)$/i,NOT_A_CREDENTIAL=new Set(["preventapikeymanagement","allowapplicationkeys","maxkeysperuser","tokenurl","accesstoken","refreshtoken","sessiontoken"]),BOOT_ONLY_PATHS=new Set(["authorization.godminPassword","authorization.godminEmail","authentication.accessToken.secret","authentication.refreshToken.secret","authentication.sessionToken.secret","backup.encryptionKey","notification.channels.telegram.botToken","notification.channels.webhook.headers","domains.cloudflare.apiToken"]),SKIP_SECTIONS=new Set(["entities","systemTables","system_tables","tables","columns","indexes","routes","cors","swagger","secrets"]),SECRET_SLOT_DESCRIPTORS=[{path:"authentication.oauth.providers.*",group:"oauth",label:"OAuth / SSO",keys:[{key:"clientId",kind:"text",label:"Client ID",required:!0},{key:"clientSecret",kind:"password",label:"Client Secret",required:!0},{key:"tenantId",kind:"text",label:"Tenant ID"},{key:"redirectUri",kind:"text",secret:!1,required:!0,label:"Y\xF6nlendirme adresi (Redirect URI)",hint:"Sa\u011Flay\u0131c\u0131ya kay\u0131tl\u0131 d\xF6n\xFC\u015F adresi. Bu adresin sa\u011Flay\u0131c\u0131 taraf\u0131nda da (\xF6r. Entra > Authentication > Redirect URIs) birebir ayn\u0131 yaz\u0131l\u0131 olmas\u0131 gerekir."}]},{always:!0,path:"email.gmail",group:"email",label:"Gmail (service account)",keys:[{key:"service_account_json",kind:"json",label:"Service Account JSON",required:!0,requiredWhen:"enabled"}]},{always:!0,path:"email.azure",group:"email",label:"Azure Communication Services",keys:[{key:"connection_string",kind:"connection_string",label:"Connection String",required:!0,requiredWhen:"enabled"}]},{always:!0,path:"storage.smb",group:"storage",label:"SMB / CIFS share",keys:[{key:"username",kind:"text",label:"Kullan\u0131c\u0131 ad\u0131",required:!0},{key:"password",kind:"password",label:"Parola",required:!0}]},{secret:!1,always:!0,path:"storage",group:"storage",label:"Dosya deposu",keys:[{key:"provider",kind:"text",label:"Depolama t\xFCr\xFC",hint:"local (sunucunun kendi diski) veya smb (kurumsal dosya sunucusu). De\u011Fi\u015Fiklik an\u0131nda ge\xE7erli olur."}]},{secret:!1,always:!0,path:"storage.smb",group:"storage",label:"SMB / CIFS share",keys:[{key:"url",kind:"text",label:"Payla\u015F\u0131m adresi",hint:"M\xFC\u015Fteriden geldi\u011Fi gibi yap\u0131\u015Ft\u0131r\u0131n: smb://sunucu/paylasim \u2014 sunucu ve payla\u015F\u0131m ad\u0131 buradan \xE7\xF6z\xFCl\xFCr."},{key:"domain",kind:"text",label:"Etki alan\u0131 (domain)",hint:"Kullan\u0131c\u0131 DOMAIN\\kullanici bi\xE7imindeyse domain k\u0131sm\u0131. Gerekmiyorsa bo\u015F b\u0131rak\u0131n."},{key:"base_path",kind:"text",label:"Payla\u015F\u0131m i\xE7i klas\xF6r",hint:"Y\xFCklemelerin toplanaca\u011F\u0131 alt klas\xF6r. Bo\u015Fsa payla\u015F\u0131m\u0131n k\xF6k\xFC kullan\u0131l\u0131r."}]},{path:"payment.providers.*",group:"payment",label:"\xD6deme sa\u011Flay\u0131c\u0131",keys:[{key:"apiKey",kind:"password",label:"API Key",required:!0},{key:"secretKey",kind:"password",label:"Secret Key",required:!0},{key:"webhookSecret",kind:"password",label:"Webhook Secret"}]},{path:"captcha",group:"captcha",label:"Captcha",keys:[{key:"secretKey",kind:"password",label:"Secret Key"}]},{secret:!1,path:"authentication",group:"portal",label:"Portal adresi",keys:[{key:"trustedAppOrigins",kind:"list",label:"G\xFCvenilen adresler",required:!0,hint:"Portal\u0131n kurulu oldu\u011Fu adres(ler). \u015Eifre s\u0131f\u0131rlama ve SSO d\xF6n\xFC\u015F linkleri buraya g\xF6re kurulur. Joker: *.firma.com"}]},{secret:!1,always:!0,path:"email",group:"email",label:"E-posta sa\u011Flay\u0131c\u0131",keys:[{key:"provider",kind:"text",label:"Kullan\u0131lacak sa\u011Flay\u0131c\u0131",hint:"azure veya gmail. Bo\u015F b\u0131rak\u0131l\u0131rsa a\xE7\u0131k olan sa\u011Flay\u0131c\u0131 kullan\u0131l\u0131r."}]},{secret:!1,always:!0,path:"email.azure",group:"email",label:"Azure Communication Services",keys:[{key:"enabled",kind:"boolean",label:"Azure e-posta a\xE7\u0131k",hint:"Ba\u011Flant\u0131 dizesini girmek tek ba\u015F\u0131na yetmez \u2014 g\xF6nderim bu anahtarla a\xE7\u0131l\u0131r."},{key:"sender_address",kind:"text",label:"G\xF6nderen adresi"}]},{secret:!1,always:!0,path:"email.gmail",group:"email",label:"Gmail (service account)",keys:[{key:"enabled",kind:"boolean",label:"Gmail e-posta a\xE7\u0131k"},{key:"from_email",kind:"text",label:"G\xF6nderen adresi"}]}]});import{and as and7,eq as eq12}from"drizzle-orm";class SecretsStore{db;resolveTable;masterKey;constructor(deps){this.db=deps.db,this.resolveTable=deps.resolveTable,this.masterKey=deps.masterKey}table(schemaName){return this.resolveTable(SECRETS_TABLE,schemaName)}async loadAll(schemaName){let values=new Map,failed=[],table=this.table(schemaName);if(!table)return{values,failed,tableMissing:!0};let rows;try{rows=await this.db.select().from(table).where(eq12(col6(table,"isActive"),!0))}catch(error){if(isMissingTableError(error))return{values,failed,tableMissing:!0};throw error}for(let row of rows)try{values.set(cacheKeyOf(row.scope,row.key),decryptSecret(row.valueEnc,this.masterKey))}catch{failed.push(`${row.scope}.${row.key}`)}return{values,failed}}async list(schemaName){let table=this.table(schemaName);if(!table)return[];try{return(await this.db.select().from(table)).filter((r)=>r.isActive!==!1).map(rowToMeta)}catch(error){if(isMissingTableError(error))return[];throw error}}async get(scope,key2,schemaName){let table=this.table(schemaName);if(!table)return null;return(await this.db.select().from(table).where(and7(eq12(col6(table,"scope"),scope),eq12(col6(table,"key"),key2))).limit(1))[0]??null}async set(params,schemaName){let table=this.table(schemaName);if(!table)return null;let existing=await this.get(params.scope,params.key,schemaName),valueEnc=encryptSecret(params.value,this.masterKey),preview=previewOf(params.value),now=new Date;if(existing)await this.db.update(table).set({valueEnc,previousEnc:existing.valueEnc,preview,kind:params.kind??existing.kind??"text",description:params.description??existing.description,version:(existing.version??1)+1,rotatedAt:now,isActive:!0,updatedAt:now,updatedBy:params.actorId??null}).where(eq12(col6(table,"id"),existing.id));else await this.db.insert(table).values({scope:params.scope,key:params.key,valueEnc,preview,kind:params.kind??"text",description:params.description??null,version:1,isActive:!0,createdBy:params.actorId??null,updatedBy:params.actorId??null});let saved=await this.get(params.scope,params.key,schemaName);return saved?rowToMeta(saved):null}async rollback(scope,key2,actorId,schemaName){let table=this.table(schemaName);if(!table)return null;let existing=await this.get(scope,key2,schemaName);if(!existing?.previousEnc)return null;let plaintext=decryptSecret(existing.previousEnc,this.masterKey),now=new Date;await this.db.update(table).set({valueEnc:existing.previousEnc,previousEnc:existing.valueEnc,preview:previewOf(plaintext),version:(existing.version??1)+1,rotatedAt:now,updatedAt:now,updatedBy:actorId??null}).where(eq12(col6(table,"id"),existing.id));let saved=await this.get(scope,key2,schemaName);return saved?rowToMeta(saved):null}async remove(scope,key2,schemaName){let table=this.table(schemaName);if(!table)return!1;let existing=await this.get(scope,key2,schemaName);if(!existing)return!1;return await this.db.delete(table).where(eq12(col6(table,"id"),existing.id)),!0}async touch(scope,key2,schemaName){let table=this.table(schemaName);if(!table)return;await this.db.update(table).set({lastUsedAt:new Date}).where(and7(eq12(col6(table,"scope"),scope),eq12(col6(table,"key"),key2)))}async reencryptAll(newMasterKey,schemaName){let table=this.table(schemaName);if(!table)return{migrated:0,failed:[]};let rows=await this.db.select().from(table),migrated=0,failed=[];for(let row of rows)try{let plaintext=decryptSecret(row.valueEnc,this.masterKey),previous=row.previousEnc?encryptSecret(decryptSecret(row.previousEnc,this.masterKey),newMasterKey):null;await this.db.update(table).set({valueEnc:encryptSecret(plaintext,newMasterKey),previousEnc:previous}).where(eq12(col6(table,"id"),row.id)),migrated++}catch{failed.push(`${row.scope}.${row.key}`)}if(failed.length===0)this.masterKey=newMasterKey;return{migrated,failed}}}var SECRETS_TABLE="nucleusSecrets",col6=(table,key2)=>table[key2],toIso=(value)=>value?new Date(value).toISOString():null,UNDEFINED_TABLE="42P01",isMissingTableError=(error)=>{let seen=new Set,current=error;while(current&&typeof current==="object"&&!seen.has(current)){seen.add(current);let record=current;if(record.code===UNDEFINED_TABLE)return!0;if(typeof record.message==="string"&&/relation .* does not exist/i.test(record.message))return!0;current=record.cause}return!1},rowToMeta=(row)=>({id:row.id,scope:row.scope,key:row.key,kind:row.kind??"text",preview:row.preview??"",description:row.description,version:row.version??1,canRollback:!!row.previousEnc,rotatedAt:toIso(row.rotatedAt),lastUsedAt:toIso(row.lastUsedAt),updatedAt:toIso(row.updatedAt),updatedBy:row.updatedBy}),cacheKeyOf=(scope,key2)=>`${scope}.${key2}`;var init_store=__esm(()=>{init_crypto()});class SecretsService{store;logger;getRedis;refreshIntervalMs;schemaName;cache=new Map;timer=null;localRevision=0;loaded=!1;tableMissing=!1;warnedTableMissing=!1;constructor(deps){this.store=deps.store,this.logger=deps.logger,this.getRedis=deps.getRedis,this.refreshIntervalMs=deps.refreshIntervalMs??DEFAULT_REFRESH_MS,this.schemaName=deps.schemaName}async start(){if(await this.refresh(),this.timer)return;this.timer=setInterval(()=>{this.refreshIfStale()},this.refreshIntervalMs),this.timer.unref?.()}stop(){if(this.timer)clearInterval(this.timer);this.timer=null}isReady(){return this.loaded}size(){return this.cache.size}isTableMissing(){return this.tableMissing}async refresh(){try{let{values,failed,tableMissing}=await this.store.loadAll(this.schemaName);if(tableMissing){if(this.tableMissing=!0,!this.warnedTableMissing)this.warnedTableMissing=!0,this.logger.warn("[Secrets] nucleus_secrets does not exist yet \u2014 env and literal values resolve normally; "+"the store activates automatically once the schema sync creates it");return}if(this.tableMissing)this.tableMissing=!1,this.warnedTableMissing=!1,this.logger.info("[Secrets] nucleus_secrets is now available \u2014 credential store active");if(this.cache=values,this.loaded=!0,failed.length>0)this.logger.error("[Secrets] Credentials failed to decrypt and were skipped",{slots:failed,hint:"master key changed without re-encrypting \u2014 POST /secrets/reencrypt with the old key"})}catch(error){this.logger.warn("[Secrets] Refresh failed, keeping cached values",{error:error instanceof Error?error.message:String(error)})}}async refreshIfStale(){let redis=this.getRedis?.();if(!redis)return this.refresh();try{let result=await redis.read(REVISION_KEY),remote=Number(result.data??0);if(Number.isFinite(remote)&&remote===this.localRevision&&this.loaded)return;await this.refresh(),this.localRevision=Number.isFinite(remote)?remote:this.localRevision}catch{await this.refresh()}}async bumpRevision(){let redis=this.getRedis?.();if(!redis)return;try{let next=Date.now();this.localRevision=next,await redis.create(REVISION_KEY,next)}catch{}}get(scope,key2){return this.cache.get(cacheKeyOf(scope,key2))}has(scope,key2){return this.cache.has(cacheKeyOf(scope,key2))}resolve(scope,key2,configValue){let fromDb=this.cache.get(cacheKeyOf(scope,key2));if(fromDb)return{value:fromDb,source:"db"};if(configValue){let fromEnv=process.env[configValue];if(fromEnv)return{value:fromEnv,source:"env"};if(looksLikeEnvName(configValue))return{value:void 0,source:"missing"};return{value:configValue,source:"literal"}}return{value:void 0,source:"missing"}}value(scope,key2,configValue){return this.resolve(scope,key2,configValue).value}listValue(scope,key2,configValue){let fallback=Array.isArray(configValue)?configValue.join(","):configValue,raw=this.value(scope,key2,fallback);if(!raw)return Array.isArray(configValue)?configValue:[];return raw.split(/[,;\n\r]+/).map((entry)=>entry.trim()).filter(Boolean)}flag(scope,key2,configValue){let raw=this.get(scope,key2);if(raw===void 0)return configValue===!0;return raw==="true"||raw==="1"}liveConfig(scope,node,keys){let live={...node};for(let key2 of keys){let declared=node[key2],configValue=typeof declared==="string"?declared:void 0;Object.defineProperty(live,key2,{get:()=>this.resolve(scope,key2,configValue).value,enumerable:!0,configurable:!0})}return live}async describe(config,slots){let discovered=slots??discoverSlots(config),stored=await this.store.list(this.schemaName),byId=new Map(stored.map((s)=>[cacheKeyOf(s.scope,s.key),s]));return discovered.map((slot)=>{let id=cacheKeyOf(slot.scope,slot.key),record=byId.get(id),{value,source}=this.resolve(slot.scope,slot.key,slot.configValue),envName=looksLikeEnvName(slot.configValue)?slot.configValue:void 0,gate=slot.requiredWhen?this.flag(slot.scope,slot.requiredWhen,configFlagAt(config,slot.scope,slot.requiredWhen)):!0;return{...slot,required:slot.required&&gate,configValue:envName,source,value:slot.secret?void 0:value,preview:slot.secret?source==="db"&&record?record.preview:value?previewOf(value):"":value??"",envName,version:record?.version,rotatedAt:record?.rotatedAt??null,canRollback:record?.canRollback??!1}})}async list(){return this.store.list(this.schemaName)}async set(params){let saved=await this.store.set(params,this.schemaName);return await this.refresh(),await this.bumpRevision(),this.logger.info("[Secrets] Credential stored",{scope:params.scope,key:params.key,version:saved?.version,actor:params.actorId}),saved}async rollback(scope,key2,actorId){let saved=await this.store.rollback(scope,key2,actorId,this.schemaName);if(!saved)return null;return await this.refresh(),await this.bumpRevision(),this.logger.info("[Secrets] Credential rolled back",{scope,key:key2,actor:actorId}),saved}async remove(scope,key2,actorId){if(!await this.store.remove(scope,key2,this.schemaName))return!1;return await this.refresh(),await this.bumpRevision(),this.logger.info("[Secrets] Credential removed",{scope,key:key2,actor:actorId}),!0}async reencryptAll(newMasterKey){let result=await this.store.reencryptAll(newMasterKey,this.schemaName);return await this.refresh(),await this.bumpRevision(),result}}var REVISION_KEY="nucleus:secrets:revision",DEFAULT_REFRESH_MS=30000,configFlagAt=(config,scope,key2)=>{let node=config;for(let segment of scope.split(".")){if(typeof node!=="object"||node===null)return;node=node[segment]}if(typeof node!=="object"||node===null)return;let value=node[key2];if(typeof value==="boolean")return value;if(typeof value==="string")return value==="true"||value==="1";return};var init_SecretsService=__esm(()=>{init_crypto();init_registry();init_store()});var resolveMasterKey=(configured)=>{if(configured){let fromEnv=process.env[configured];if(fromEnv)return fromEnv;if(!/^[A-Z][A-Z0-9_]{2,}$/.test(configured))return configured}return process.env.NUCLEUS_SECRETS_KEY||void 0};var init_Secrets=__esm(()=>{init_crypto();init_registry();init_SecretsService();init_store()});var exports_schema={};__export(exports_schema,{isAdditiveStatement:()=>isAdditiveStatement,ensureSchemaExists:()=>ensureSchemaExists,applySchemaPush:()=>applySchemaPush});import{sql as sql4}from"drizzle-orm";var validateIdentifier=(name)=>{if(!name||name.length>63)throw Error(`Invalid identifier: must be 1-63 characters, got ${name.length}`);if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))throw Error(`Invalid identifier: "${name}" contains unsafe characters`);return name},ensureSchemaExists=async(db,schemaName)=>{let safeName=validateIdentifier(schemaName),found=await db.execute(sql4`select 1 from pg_catalog.pg_namespace where nspname = ${safeName} limit 1`);if(rowCountOf(found)>0)return;await db.execute(sql4.raw(`CREATE SCHEMA IF NOT EXISTS "${safeName}"`))},rowCountOf=(result)=>{if(Array.isArray(result))return result.length;let rows=result?.rows;return Array.isArray(rows)?rows.length:0},ADDITIVE_PREFIXES,isAdditiveStatement=(statement)=>{let normalized=statement.trim().toUpperCase();if(/^ALTER TABLE .+ ADD COLUMN/.test(normalized))return!0;return ADDITIVE_PREFIXES.some((prefix)=>normalized.startsWith(prefix))},applySchemaPush=async(push,opts)=>{let{schemaName,allowDataLoss,logger:logger2,execute}=opts;if(push.hasDataLoss&&!allowDataLoss){let additive=push.statementsToExecute.filter(isAdditiveStatement),blocked=push.statementsToExecute.filter((s)=>!isAdditiveStatement(s));if(logger2.error(`[Schema] Destructive changes detected for schema "${schemaName}" but database.allowDataLoss is not enabled \u2014 ${blocked.length} statement(s) skipped. Set database.allowDataLoss: true to apply them.`,{warnings:push.warnings,statements:blocked}),additive.length===0||!execute)return!1;let applied=0;for(let statement of additive)try{await execute(statement),applied++}catch(error){logger2.warn("[Schema] Additive statement failed",{schema:schemaName,statement,error:error instanceof Error?error.message:String(error)})}return logger2.info(`[Schema] Applied ${applied}/${additive.length} additive statement(s) for "${schemaName}" despite the destructive changes being held back`),applied>0}if(push.hasDataLoss)logger2.warn(`[Schema] Applying destructive changes to schema "${schemaName}" (database.allowDataLoss=true)`,{warnings:push.warnings});return await push.apply(),!0};var init_schema=__esm(()=>{ADDITIVE_PREFIXES=["CREATE TABLE IF NOT EXISTS","CREATE TABLE","CREATE SCHEMA IF NOT EXISTS","CREATE SCHEMA","CREATE INDEX IF NOT EXISTS","CREATE INDEX","CREATE UNIQUE INDEX IF NOT EXISTS","CREATE UNIQUE INDEX","CREATE TYPE","DO $$"]});var rowToTenantRecord=(row)=>({id:String(row.id||""),subdomain:String(row.subdomain||""),schemaName:String(row.schemaName||row.schema_name||""),companyId:String(row.companyId||row.company_id||""),companyName:row.companyName!=null?String(row.companyName):row.company_name!=null?String(row.company_name):null,godAdminEmail:String(row.godAdminEmail||row.god_admin_email||""),status:parseStatus(row.status),plan:row.plan!=null?String(row.plan):null,domain:row.domain!=null?String(row.domain):null,settings:parseJsonbToConfig(row.settings),trustedSources:parseTrustedSources(row.trustedSources||row.trusted_sources),maxUsers:row.maxUsers!=null?Number(row.maxUsers):row.max_users!=null?Number(row.max_users):null,provisionedAt:row.provisionedAt!=null?String(row.provisionedAt):row.provisioned_at!=null?String(row.provisioned_at):null,suspendedAt:row.suspendedAt!=null?String(row.suspendedAt):row.suspended_at!=null?String(row.suspended_at):null,suspendedReason:row.suspendedReason!=null?String(row.suspendedReason):row.suspended_reason!=null?String(row.suspended_reason):null}),rowToFeatureRecord=(row,parseConfig)=>({id:String(row.id||""),tenantId:String(row.tenantId||row.tenant_id||""),featureName:String(row.featureName||row.feature_name||""),enabled:Boolean(row.enabled),featureConfig:parseConfig(row.config)}),parseStatus=(value)=>{let valid=["provisioning","active","suspended","archived"],str3=String(value||"provisioning");return valid.includes(str3)?str3:"provisioning"},parseJsonbToConfig=(value)=>{if(!value||typeof value!=="object")return{};let result={};for(let[k,v]of Object.entries(value))if(typeof v==="string"||typeof v==="number"||typeof v==="boolean")result[k]=v;return result},parseTrustedSources=(value)=>{if(!Array.isArray(value))return[];return value.map((item)=>{let entry=item;return{allowHeaderAuth:entry.allowHeaderAuth===!0||entry.allow_header_auth===!0,allowedIps:Array.isArray(entry.allowedIps||entry.allowed_ips)?entry.allowedIps||entry.allowed_ips:void 0,allowedServices:Array.isArray(entry.allowedServices||entry.allowed_services)?entry.allowedServices||entry.allowed_services:void 0}})},normalizeHost=(host)=>{return((host||"").split(":")[0]||"").toLowerCase().replace(/\.$/,"")},extractSubdomain=(host)=>{let hostWithoutPort=host.split(":")[0]||"";if(hostWithoutPort==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(hostWithoutPort))return null;let parts=hostWithoutPort.split(".");if(parts.length<3)return null;let subdomain=parts[0]||"";if(!subdomain||subdomain==="www")return null;return subdomain},isIpInCidr=(ip,cidr)=>{let[cidrIp,prefixStr]=cidr.split("/");if(!cidrIp||!prefixStr)return!1;let prefix=Number.parseInt(prefixStr,10);if(Number.isNaN(prefix))return!1;let ipParts=ip.split(".").map(Number),cidrParts=cidrIp.split(".").map(Number);if(ipParts.length!==4||cidrParts.length!==4)return!1;let ipNum=(ipParts[0]||0)<<24|(ipParts[1]||0)<<16|(ipParts[2]||0)<<8|(ipParts[3]||0),cidrNum=(cidrParts[0]||0)<<24|(cidrParts[1]||0)<<16|(cidrParts[2]||0)<<8|(cidrParts[3]||0),mask=~((1<<32-prefix)-1);return(ipNum&mask)===(cidrNum&mask)},isTrustedSource=(tenant,request,authMode,fallbackSources,vettedClientIp)=>{let ownSources=tenant.trustedSources,trustedSources=Array.isArray(ownSources)&&ownSources.length>0?ownSources:fallbackSources;if(!trustedSources||!Array.isArray(trustedSources)||trustedSources.length===0)return!1;let clientIp=vettedClientIp?.trim()||"",serviceId=request.headers.get("x-service-id")||"";for(let source of trustedSources){if(!source.allowHeaderAuth)continue;if(source.allowedIps&&source.allowedIps.length>0){if(source.allowedIps.some((allowedIp)=>{if(allowedIp.includes("/"))return isIpInCidr(clientIp,allowedIp);return clientIp===allowedIp}))return!0}if(source.allowedServices&&source.allowedServices.length>0){if(source.allowedServices.includes(serviceId))return!0}}return!1};function getDatabaseAuthMode(){return process.env.DATABASE_AUTH_MODE||"password"}function getRedisAuthMode(){return process.env.REDIS_AUTH_MODE||"password"}async function acquireToken(scope,label){let{DefaultAzureCredential,ManagedIdentityCredential,ClientSecretCredential}=await import("@azure/identity"),authMode=scope===PG_TOKEN_SCOPE?getDatabaseAuthMode():getRedisAuthMode(),clientId=process.env.AZURE_CLIENT_ID||"",tenantId=process.env.AZURE_TENANT_ID||"",clientSecret=process.env.AZURE_CLIENT_SECRET||"",credential;if(authMode==="managed_identity")credential=clientId?new ManagedIdentityCredential(clientId):new DefaultAzureCredential;else if(authMode==="service_principal"){if(!tenantId||!clientId||!clientSecret)throw Error("[azure-auth] service_principal auth requires AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET");credential=new ClientSecretCredential(tenantId,clientId,clientSecret)}else throw Error(`[azure-auth] Unsupported auth mode: ${authMode}`);let result=await credential.getToken(scope);if(!result)throw Error(`[azure-auth] Failed to acquire token for ${label}`);return logger.scoped("database.connect").info(`[azure-auth] ${label} token acquired`,{expiresAt:new Date(result.expiresOnTimestamp).toISOString(),authMode}),{token:result.token,expiresAt:result.expiresOnTimestamp}}async function getPostgresToken(){let now=Date.now();if(pgCachedToken&&now<pgTokenExpiresAt-REFRESH_BUFFER_MS)return pgCachedToken;let{token,expiresAt}=await acquireToken(PG_TOKEN_SCOPE,"PostgreSQL");return pgCachedToken=token,pgTokenExpiresAt=expiresAt,pgCachedToken}async function getRedisToken(){let now=Date.now();if(redisCachedToken&&now<redisTokenExpiresAt-REFRESH_BUFFER_MS)return redisCachedToken;let{token,expiresAt}=await acquireToken(REDIS_TOKEN_SCOPE,"Redis");return redisCachedToken=token,redisTokenExpiresAt=expiresAt,redisCachedToken}function getRedisTokenExpiresAt(){return redisTokenExpiresAt}var PG_TOKEN_SCOPE="https://ossrdbms-aad.database.windows.net/.default",REDIS_TOKEN_SCOPE="https://redis.azure.com/.default",REFRESH_BUFFER_MS=300000,pgCachedToken=null,pgTokenExpiresAt=0,redisCachedToken=null,redisTokenExpiresAt=0;var init_AzureTokenProvider=__esm(()=>{init_Logger2()});var exports_Azure={};__export(exports_Azure,{getRedisTokenExpiresAt:()=>getRedisTokenExpiresAt,getRedisToken:()=>getRedisToken,getRedisAuthMode:()=>getRedisAuthMode,getPostgresToken:()=>getPostgresToken,getDatabaseAuthMode:()=>getDatabaseAuthMode});var init_Azure=__esm(()=>{init_AzureTokenProvider()});import{access,mkdir as mkdir4}from"fs/promises";import{dirname as dirname3,resolve}from"path";var DEFAULT_CONFIG5,FILE_SIZE_UNITS,resolvePath=(path2)=>{if(!path2||typeof path2!=="string")throw createFileManagerError("INVALID_PATH","Path must be a non-empty string",path2,"resolvePath");return resolve(path2)},extractDirectoryPath=(filePath)=>{let resolvedPath=resolvePath(filePath);return dirname3(resolvedPath)},ensureDirectoryExists=async(dirPath)=>{let resolvedPath=resolve(dirPath);try{await mkdir4(resolvedPath,{recursive:!0})}catch(error){if(error.code!=="EEXIST")throw createFileManagerError("DIRECTORY_CREATE_FAILED",`Failed to create directory: ${resolvedPath}`,resolvedPath,"ensureDirectory")}},formatFileSize=(bytes)=>{let size=bytes,unitIndex=0;while(size>=1024&&unitIndex<FILE_SIZE_UNITS.length-1)size/=1024,unitIndex++;return`${size.toFixed(2)} ${FILE_SIZE_UNITS[unitIndex]}`},validateFileExtension=(fileName,expectedExtension)=>{return fileName.toLowerCase().endsWith(expectedExtension.toLowerCase())},ensureFileExtension=(fileName,extension)=>{let normalizedExtension=extension.startsWith(".")?extension:`.${extension}`;if(validateFileExtension(fileName,normalizedExtension))return fileName;return`${fileName}${normalizedExtension}`},createFileManagerError=(code,message,path2,operation)=>{return{code,message,path:path2,operation:operation||"unknown"}},safeJsonStringify=(data)=>{try{return JSON.stringify(data,null,2)}catch{return"{}"}},executeBulkOperation=async(items,operation,concurrency=DEFAULT_CONFIG5.maxConcurrency)=>{let results=[];for(let i=0;i<items.length;i+=concurrency){let batch3=items.slice(i,i+concurrency),batchPromises=[];for(let item of batch3)batchPromises.push(operation(item));let batchResults=await Promise.allSettled(batchPromises);results.push(...batchResults)}return results},validateConfig=(config,options={})=>{let errors=[],warnings=[],strict=options.strict??!0;if(config.defaultEncoding!==void 0){if(!["utf-8","utf8","ascii","base64","hex"].includes(config.defaultEncoding))errors.push(`Invalid defaultEncoding: ${config.defaultEncoding}`)}if(config.maxConcurrency!==void 0){if(!Number.isInteger(config.maxConcurrency)||config.maxConcurrency<1)errors.push("maxConcurrency must be a positive integer");if(config.maxConcurrency>50)warnings.push("maxConcurrency > 50 may cause performance issues")}if(config.defaultCreateDir!==void 0&&typeof config.defaultCreateDir!=="boolean")errors.push("defaultCreateDir must be a boolean");if(config.defaultRecursive!==void 0&&typeof config.defaultRecursive!=="boolean")errors.push("defaultRecursive must be a boolean");if(strict&&!options.allowUnknownKeys){let validKeys=["defaultEncoding","defaultCreateDir","defaultRecursive","maxConcurrency"],configKeys=Object.keys(config);for(let key2 of configKeys)if(!validKeys.includes(key2))errors.push(`Unknown configuration key: ${key2}`)}return{isValid:errors.length===0,errors,warnings}},mergeConfig=(partial,base=DEFAULT_CONFIG5)=>{let validation=validateConfig(partial);if(!validation.isValid)throw createFileManagerError("CONFIG_VALIDATION_FAILED",`Configuration validation failed: ${validation.errors.join(", ")}`,void 0,"mergeConfig");return{...base,...partial}},parsePermissions=(mode)=>{let parseOctal=(octal)=>({read:Boolean(octal&4),write:Boolean(octal&2),execute:Boolean(octal&1)}),ownerMode=mode>>6&7,groupMode=mode>>3&7,othersMode=mode&7;return{owner:parseOctal(ownerMode),group:parseOctal(groupMode),others:parseOctal(othersMode)}},validatePermissionMode=(mode)=>{return Number.isInteger(mode)&&mode>=0&&mode<=511};var init_utils4=__esm(()=>{DEFAULT_CONFIG5={defaultEncoding:"utf-8",defaultCreateDir:!0,defaultRecursive:!0,maxConcurrency:5},FILE_SIZE_UNITS=["B","KB","MB","GB","TB"]});import{copyFile,rename as rename2,unlink as unlink4}from"fs/promises";import{basename,dirname as dirname4,extname,join as join5}from"path";var DEFAULT_ATOMIC_CONFIG,generateTempPath=(originalPath,suffix=".tmp")=>{let resolvedPath=resolvePath(originalPath),timestamp=Date.now(),random=Math.random().toString(36).substring(2,8);return`${resolvedPath}${suffix}.${timestamp}.${random}`},generateBackupPath=(originalPath,backupDir,useTimestamp=!0)=>{let resolvedPath=resolvePath(originalPath),dir=backupDir?resolvePath(backupDir):dirname4(resolvedPath),name=basename(resolvedPath),ext=extname(name),nameWithoutExt=basename(name,ext),timestamp=useTimestamp?`.${new Date().toISOString().replace(/[:.]/g,"-")}`:"",backupName=`${nameWithoutExt}.backup${timestamp}${ext}`;return join5(dir,backupName)},atomicWrite=async({path:path2,data,tempSuffix=DEFAULT_ATOMIC_CONFIG.tempSuffix,backup=DEFAULT_ATOMIC_CONFIG.backup,sync=DEFAULT_ATOMIC_CONFIG.sync})=>{let resolvedPath=resolvePath(path2),tempPath=generateTempPath(resolvedPath,tempSuffix),backupPath;try{if(await ensureDirectoryExists(extractDirectoryPath(resolvedPath)),backup){if(await Bun.file(resolvedPath).exists())backupPath=generateBackupPath(resolvedPath),await copyFile(resolvedPath,backupPath)}let bytesWritten=await Bun.write(tempPath,data);return await rename2(tempPath,resolvedPath),{success:!0,bytesWritten,tempPath,backupPath}}catch(error){try{await unlink4(tempPath)}catch{}throw createFileManagerError("ATOMIC_WRITE_FAILED",`Atomic write failed: ${error}`,resolvedPath,"atomicWrite")}},atomicJsonWrite=async(path2,data,options={})=>{let jsonString=JSON.stringify(data,null,2);return atomicWrite({path:path2,data:jsonString,...options})},createBackup=async({sourcePath,backupDir,keepOriginal=!0,timestamp=DEFAULT_ATOMIC_CONFIG.timestamp})=>{let resolvedSource=resolvePath(sourcePath);if(!await Bun.file(resolvedSource).exists())throw createFileManagerError("SOURCE_NOT_FOUND",`Source file not found: ${sourcePath}`,resolvedSource,"createBackup");let backupPath=generateBackupPath(resolvedSource,backupDir,timestamp);if(await ensureDirectoryExists(dirname4(backupPath)),keepOriginal)await copyFile(resolvedSource,backupPath);else await rename2(resolvedSource,backupPath);return backupPath},restoreFromBackup=async(backupPath,targetPath,deleteBackup=!1)=>{let resolvedBackup=resolvePath(backupPath),resolvedTarget=resolvePath(targetPath);if(!await Bun.file(resolvedBackup).exists())throw createFileManagerError("BACKUP_NOT_FOUND",`Backup file not found: ${backupPath}`,resolvedBackup,"restoreFromBackup");try{if(await ensureDirectoryExists(extractDirectoryPath(resolvedTarget)),deleteBackup)await rename2(resolvedBackup,resolvedTarget);else await copyFile(resolvedBackup,resolvedTarget);return!0}catch(error){return logger.scoped("storage.fs").error(`Failed to restore from backup ${backupPath}`,error,{backupPath}),!1}},safeUpdate=async(path2,updateFunction,options={})=>{let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath),backupPath;try{if(await file.exists())backupPath=await createBackup({sourcePath:resolvedPath,keepOriginal:!0,timestamp:!0});let currentData=await file.exists()?await file.text():"",newData=await updateFunction(currentData),result=await atomicWrite({path:resolvedPath,data:newData,backup:!1,...options});return{success:result.success,bytesWritten:result.bytesWritten,tempPath:result.tempPath,backupPath}}catch(error){if(backupPath)try{await restoreFromBackup(backupPath,resolvedPath,!1)}catch(rollbackError){logger.scoped("storage.fs").error("Atomic write rollback failed",rollbackError,{backupPath})}throw error}},batchAtomicWrite=async(operations)=>{let successful=[],failed=[];for(let operation of operations)try{let result=await atomicWrite(operation);successful.push(result)}catch(error){failed.push({operation,error})}return{successful,failed}};var init_atomic=__esm(()=>{init_Logger2();init_utils4();DEFAULT_ATOMIC_CONFIG={tempSuffix:".tmp",backup:!1,sync:!0,timestamp:!0}});import{chmod,stat as stat3}from"fs/promises";var PERMISSION_MODES,setFilePermissions=async(path2,mode)=>{let resolvedPath=resolvePath(path2);if(!validatePermissionMode(mode))throw createFileManagerError("INVALID_PERMISSION_MODE",`Invalid permission mode: ${mode.toString(8)}`,resolvedPath,"setFilePermissions");try{return await chmod(resolvedPath,mode),!0}catch(error){return logger.scoped("storage.fs").error(`Failed to set permissions for ${path2}`,error,{path:path2,mode}),!1}},getFilePermissions=async(path2)=>{let resolvedPath=resolvePath(path2);try{let mode=(await stat3(resolvedPath)).mode&511,permissions=parsePermissions(mode);return{path:resolvedPath,mode,owner:permissions.owner,group:permissions.group,others:permissions.others}}catch(error){throw createFileManagerError("PERMISSION_READ_FAILED",`Failed to read permissions: ${error}`,resolvedPath,"getFilePermissions")}},hasPermissions=async(path2,requiredMode)=>{try{return((await getFilePermissions(path2)).mode&requiredMode)===requiredMode}catch{return!1}},makeReadable=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode|256;return setFilePermissions(path2,newMode)},makeWritable=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode|128;return setFilePermissions(path2,newMode)},makeExecutable=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode|64;return setFilePermissions(path2,newMode)},makeReadOnly=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode&-147;return setFilePermissions(path2,newMode)},setCommonPermissions=async(path2,pattern)=>{let mode=PERMISSION_MODES[pattern];return setFilePermissions(path2,mode)};var init_permissions=__esm(()=>{init_Logger2();init_utils4();PERMISSION_MODES={OWNER_READ_WRITE:384,OWNER_ALL:448,GROUP_READ:416,GROUP_READ_WRITE:432,ALL_READ:420,ALL_READ_WRITE:438,ALL_READ_EXECUTE:493,ALL_FULL:511,READ_ONLY:292,EXECUTABLE:493}});var DEFAULT_STREAM_CONFIG,createFileWriter=async(path2,options={})=>{let resolvedPath=resolvePath(path2),config={...DEFAULT_STREAM_CONFIG,...options};await ensureDirectoryExists(extractDirectoryPath(resolvedPath));let writer=Bun.file(resolvedPath).writer({highWaterMark:config.highWaterMark}),isClosed=!1;return{write:(chunk)=>{if(isClosed)throw createFileManagerError("WRITER_CLOSED","Cannot write to closed writer",resolvedPath,"streamWrite");try{let result=writer.write(chunk);if(config.autoFlush)writer.flush();return result}catch(error){throw createFileManagerError("WRITE_FAILED",`Failed to write chunk: ${error}`,resolvedPath,"streamWrite")}},flush:()=>{if(isClosed)return 0;try{return writer.flush()}catch(error){throw createFileManagerError("FLUSH_FAILED",`Failed to flush writer: ${error}`,resolvedPath,"streamFlush")}},end:async(error)=>{if(isClosed)return 0;try{let result=await writer.end(error);return isClosed=!0,result}catch(err){throw isClosed=!0,createFileManagerError("END_FAILED",`Failed to end writer: ${err}`,resolvedPath,"streamEnd")}},ref:()=>{if(!isClosed)writer.ref()},unref:()=>{if(!isClosed)writer.unref()}}},writeStream=async(path2,chunks,options={})=>{let writer=await createFileWriter(path2,options),totalBytes=0;try{for(let chunk of chunks){let bytesWritten=writer.write(chunk);totalBytes+=bytesWritten}return await writer.flush(),await writer.end(),totalBytes}catch(error){try{await writer.end(error)}catch{}throw error}},appendStream=async(path2,chunks,options={})=>{let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath),existingContent=await file.exists()?await file.arrayBuffer():new ArrayBuffer(0),allChunks=[];if(existingContent.byteLength>0)allChunks.push(existingContent);return allChunks.push(...chunks),writeStream(resolvedPath,allChunks,options)},copyFileStream=async(sourcePath,destinationPath,options={})=>{let resolvedSource=resolvePath(sourcePath),sourceFile=Bun.file(resolvedSource);if(!await sourceFile.exists())throw createFileManagerError("SOURCE_NOT_FOUND",`Source file not found: ${sourcePath}`,resolvedSource,"copyFileStream");let sourceStream=sourceFile.stream(),writer=await createFileWriter(destinationPath,options),totalBytes=0;try{let reader=sourceStream.getReader();while(!0){let{done,value}=await reader.read();if(done)break;let bytesWritten=writer.write(value);totalBytes+=bytesWritten}return await writer.flush(),await writer.end(),totalBytes}catch(error){try{await writer.end(error)}catch{}throw error}},readFileStream=async(path2,chunkProcessor)=>{let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath);if(!await file.exists())throw createFileManagerError("FILE_NOT_FOUND",`File not found: ${path2}`,resolvedPath,"readFileStream");let reader=file.stream().getReader();try{while(!0){let{done,value}=await reader.read();if(done)break;await chunkProcessor(value)}}finally{reader.releaseLock()}};var init_streaming=__esm(()=>{init_utils4();DEFAULT_STREAM_CONFIG={highWaterMark:1048576,autoFlush:!0,closeOnEnd:!0}});import{readdir as readdir2,rm,rmdir,stat as stat4}from"fs/promises";import{extname as extname2,join as join6}from"path";class BunFileManager{static instance;config;constructor(){this.config={...DEFAULT_CONFIG5}}static getInstance(){if(!BunFileManager.instance)BunFileManager.instance=new BunFileManager;return BunFileManager.instance}async createFile({dir,name,data,options={}}){let filePath=resolvePath(join6(dir,name));if(options.createDir!==!1)await ensureDirectoryExists(extractDirectoryPath(filePath));let fileData=options.type?new Blob([data],{type:options.type}):data;return await Bun.write(filePath,fileData)}async createJsonFile(dir,name,data){let fileName=ensureFileExtension(name,".json"),jsonString=safeJsonStringify(data);return this.createFile({dir,name:fileName,data:jsonString,options:{type:"application/json"}})}async createDirectory({path:path2}){await ensureDirectoryExists(path2)}async readFile({path:path2,format="text"}){let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath);if(!await file.exists())throw createFileManagerError("FILE_NOT_FOUND",`File not found: ${path2}`,resolvedPath,"readFile");switch(format){case"text":return await file.text();case"json":return await file.json();case"buffer":return await file.arrayBuffer();case"bytes":return await file.bytes();case"stream":return file.stream();default:return await file.text()}}async readJsonFile(path2){return this.readFile({path:path2,format:"json"})}async getFileInfo(path2){let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath),fileName=path2.split("/").pop()||path2,stats=null;try{stats=await stat4(resolvedPath)}catch{}return{name:fileName,path:resolvedPath,size:file.size,type:file.type,exists:await file.exists(),extension:extname2(fileName),createdAt:stats?.birthtime,modifiedAt:stats?.mtime}}async readDirectory({path:path2,recursive=!1}){let resolvedPath=resolvePath(path2);return await readdir2(resolvedPath,{recursive,encoding:"utf8"})}async getFilesByExtension(dir,extension){let files=await this.readDirectory({path:dir}),normalizedExt=extension.startsWith(".")?extension:`.${extension}`;return files.filter((file)=>file.endsWith(normalizedExt))}async updateFile({path:path2,data,mode="overwrite"}){let resolvedPath=resolvePath(path2);if(mode==="append"){let combinedData=await this.readFile({path:path2,format:"text"})+data;return await Bun.write(resolvedPath,combinedData)}return await Bun.write(resolvedPath,data)}async updateJsonFile(path2,data,merge=!1){let finalData=data;if(merge)try{let existingData=await this.readJsonFile(path2);if(typeof existingData==="object"&&existingData!==null&&!Array.isArray(existingData)&&typeof data==="object"&&data!==null&&!Array.isArray(data))finalData={...existingData,...data}}catch{}return this.updateFile({path:path2,data:safeJsonStringify(finalData),mode:"overwrite"})}async appendToFile(path2,data){return this.updateFile({path:path2,data,mode:"append"})}async deleteFile(path2){try{let resolvedPath=resolvePath(path2);return await Bun.file(resolvedPath).delete(),!0}catch(error){return logger.scoped("storage.fs").error(`Failed to delete file ${path2}`,error,{path:path2}),!1}}async deleteDirectory({path:path2,recursive=!1}){try{let resolvedPath=resolvePath(path2);if(recursive)await rm(resolvedPath,{recursive:!0,force:!0});else await rmdir(resolvedPath);return!0}catch(error){return logger.scoped("storage.fs").error(`Failed to delete directory ${path2}`,error,{path:path2}),!1}}async deleteFiles(paths){let results=await executeBulkOperation(paths,async(path2)=>{if(!await this.deleteFile(path2))throw Error(`Failed to delete: ${path2}`);return path2}),success=[],failed=[];for(let i=0;i<results.length;i++){let result=results[i],originalPath=paths[i];if(result?.status==="fulfilled")success.push(originalPath||"");else failed.push(originalPath||"")}return{success,failed}}async exists(path2){let resolvedPath=resolvePath(path2);return await Bun.file(resolvedPath).exists()}async copyFile(sourcePath,destinationPath){let resolvedSource=resolvePath(sourcePath),resolvedDestination=resolvePath(destinationPath),sourceFile=Bun.file(resolvedSource);if(!await sourceFile.exists())throw createFileManagerError("SOURCE_NOT_FOUND",`Source file not found: ${sourcePath}`,resolvedSource,"copyFile");return await ensureDirectoryExists(extractDirectoryPath(resolvedDestination)),await Bun.write(resolvedDestination,sourceFile)}async moveFile(sourcePath,destinationPath){try{return await this.copyFile(sourcePath,destinationPath),await this.deleteFile(sourcePath),!0}catch(error){return logger.scoped("storage.fs").error("Failed to move file",error,{sourcePath,destinationPath}),!1}}getFormattedFileSize(bytes){return formatFileSize(bytes)}getConfig(){return{...this.config}}updateConfig(newConfig){let validation=validateConfig(newConfig);if(validation.isValid){let mergedConfig=mergeConfig(newConfig,this.config);Object.assign(this.config,mergedConfig)}return validation}validateConfiguration(config){return validateConfig(config)}async createStreamWriter(path2,options={}){return createFileWriter(path2,options)}async writeStream(path2,chunks,options={}){return writeStream(path2,chunks,options)}async appendStream(path2,chunks,options={}){return appendStream(path2,chunks,options)}async copyFileStream(sourcePath,destinationPath,options={}){return copyFileStream(sourcePath,destinationPath,options)}async readFileStream(path2,chunkProcessor){return readFileStream(path2,chunkProcessor)}async setPermissions(path2,mode){return setFilePermissions(path2,mode)}async setPermissionsAdvanced(options){return setFilePermissions(options.path,options.mode)}async getPermissions(path2){return getFilePermissions(path2)}async checkPermissions(path2,requiredMode){return hasPermissions(path2,requiredMode)}async makeFileReadable(path2){return makeReadable(path2)}async makeFileWritable(path2){return makeWritable(path2)}async makeFileExecutable(path2){return makeExecutable(path2)}async makeFileReadOnly(path2){return makeReadOnly(path2)}async setCommonPermission(path2,pattern){return setCommonPermissions(path2,pattern)}async atomicWrite(options){return atomicWrite(options)}async atomicJsonWrite(path2,data,options={}){return atomicJsonWrite(path2,data,options)}async createFileBackup(options){return createBackup(options)}async restoreFileFromBackup(backupPath,targetPath,deleteBackup=!1){return restoreFromBackup(backupPath,targetPath,deleteBackup)}async safeFileUpdate(path2,updateFunction,options={}){return safeUpdate(path2,updateFunction,options)}async batchAtomicOperations(operations){return batchAtomicWrite(operations)}}var init_core=__esm(()=>{init_Logger2();init_atomic();init_permissions();init_streaming();init_utils4()});var fileManager;var init_File=__esm(()=>{init_core();init_utils4();init_core();fileManager=BunFileManager.getInstance()});import{Pool}from"pg";var init_Postgre=__esm(()=>{init_Logger2()});var init_Managers=__esm(()=>{init_Azure();init_Dapr();init_File();init_Postgre();init_Redis()});var exports_utils={};__export(exports_utils,{warnIfAccessTokenTooLargeForCookie:()=>warnIfAccessTokenTooLargeForCookie,validatePayload:()=>validatePayload,validateEnvVariables:()=>validateEnvVariables,toAudit:()=>toAudit,signNewAccessToken:()=>signNewAccessToken,sanitizePayload:()=>sanitizePayload,resolveDbPoolConfig:()=>resolveDbPoolConfig,resolveConfigCredential:()=>resolveConfigCredential,refreshAccessTokenWithLock:()=>refreshAccessTokenWithLock,redactSensitiveOutput:()=>redactSensitiveOutput,parseTokenValuesFromHeaders:()=>parseTokenValuesFromHeaders,parseTimeToSeconds:()=>parseTimeToSeconds2,parseQueryParams:()=>parseQueryParams,isSensitiveOutputKey:()=>isSensitiveOutputKey,initiateRedisManager:()=>initiateRedisManager,getRedisManager:()=>getRedisManager,ensureDatabaseExists:()=>ensureDatabaseExists,decodeUnverifiedSubject:()=>decodeUnverifiedSubject,createAuditLog:()=>createAuditLog,buildPaginationMeta:()=>buildPaginationMeta,COOKIE_SAFE_TOKEN_BYTES:()=>COOKIE_SAFE_TOKEN_BYTES});function parseTokenValuesFromHeaders(headers,tokenNames){let cookies=(headers.get("cookie")?.split(";")||[]).reduce((acc,cookie)=>{let trimmed=cookie.trim(),eqIndex=trimmed.indexOf("=");if(eqIndex>0)acc[trimmed.slice(0,eqIndex)]=trimmed.slice(eqIndex+1);return acc},{});return{access_token:cookies[tokenNames.access_token]||headers.get("authorization")?.split(" ")[1],refresh_token:cookies[tokenNames.refresh_token],session_token:cookies[tokenNames.session_token]}}async function initiateRedisManager(config){if(!config.redis){logger.info("[Redis] Not configured, skipping");return}let rawWithDapr=config.redis.withDapr,resolvedWithDapr=typeof rawWithDapr==="string"?process.env[rawWithDapr]?.toLowerCase()!=="false":rawWithDapr??!1,resolvedKeyPrefix=config.redis.keyPrefix?process.env[config.redis.keyPrefix]??config.redis.keyPrefix:void 0;if(resolvedWithDapr){redisManagerInstance=new RedisManager({withDapr:!0,stateStoreName:config.redis.stateStoreName,...resolvedKeyPrefix?{keyPrefix:resolvedKeyPrefix}:{}});return}let resolvedUrl=config.redis.url?process.env[config.redis.url]:void 0,resolvedHost=config.redis.host?process.env[config.redis.host]:void 0,resolvedPort=config.redis.port?parseInt(process.env[config.redis.port]||"",10):void 0;if((process.env.REDIS_AUTH_MODE||"password")!=="password"){let{getRedisToken:getRedisToken2,getRedisTokenExpiresAt:getRedisTokenExpiresAt2}=await Promise.resolve().then(() => (init_Azure(),exports_Azure)),clientId=process.env.AZURE_CLIENT_ID||"",initialToken=await getRedisToken2();redisManagerInstance=new RedisManager({host:resolvedHost,port:Number.isNaN(resolvedPort)?void 0:resolvedPort,password:initialToken,username:clientId,tls:!0,...resolvedKeyPrefix?{keyPrefix:resolvedKeyPrefix}:{}});let unref=(t)=>{t.unref?.()},scheduleRedisTokenRefresh=()=>{let expiresAt=getRedisTokenExpiresAt2(),refreshIn=Math.max(expiresAt-Date.now()-300000,30000);unref(setTimeout(async()=>{try{let newToken=await getRedisToken2();if(redisManagerInstance)await redisManagerInstance.reauthenticate(clientId,newToken),logger.info("[Redis] Entra ID token refreshed successfully");scheduleRedisTokenRefresh()}catch(err){logger.error("[Redis] Token refresh failed \u2014 retrying in 30s",err),unref(setTimeout(scheduleRedisTokenRefresh,30000))}},refreshIn))};scheduleRedisTokenRefresh()}else{let resolvedPassword=process.env.REDIS_PASSWORD||void 0;redisManagerInstance=new RedisManager({url:resolvedUrl,host:resolvedHost,port:Number.isNaN(resolvedPort)?void 0:resolvedPort,...resolvedPassword?{password:resolvedPassword}:{},...resolvedKeyPrefix?{keyPrefix:resolvedKeyPrefix}:{}})}}function getRedisManager(){return redisManagerInstance}function parseTimeToSeconds2(timeString){if(typeof timeString==="number")return timeString;if(!timeString||timeString.trim()==="")throw Error("Time string cannot be empty");let match=timeString.trim().match(/^(\d+(?:\.\d+)?)\s*([smhdwMy])$/);if(!match||!match[1]||!match[2])throw Error(`Invalid time format: "${timeString}". Expected format: "75s", "10m", "2h", "1d", "1w", "2M", "1y"`);let value=parseFloat(match[1]),unit=match[2],multiplier={s:1,m:60,h:3600,d:86400,w:604800,M:2592000,y:31536000}[unit];if(multiplier===void 0)throw Error(`Unknown time unit: "${unit}"`);let seconds=Math.floor(value*multiplier);if(seconds<=0)throw Error(`Time value must be positive: "${timeString}"`);return seconds}function warnIfAccessTokenTooLargeForCookie(token,jwtClaimsMode){let bytes=Buffer.byteLength(token,"utf-8");if(bytes<=COOKIE_SAFE_TOKEN_BYTES)return;let now=Date.now();if(now-lastOversizeWarnAt<60000)return;lastOversizeWarnAt=now,logger.warn(`[Auth] access_token is ${bytes} bytes \u2014 at/over the ~4KB per-cookie browser limit, so the cookie may be `+"silently dropped (symptom: appears logged-in but requests are unauthenticated). Shrink it: set authorization.jwtClaimsMode:'resolve' (token carries only roles; claims resolved from the Redis claims cache), or deliver the token off-cookie via authentication.accessToken.setHeadersEnabled:false. "+(jwtClaimsMode?`Current jwtClaimsMode='${jwtClaimsMode}'.`:""))}function signNewAccessToken({sessionData,options,refreshTokenId,roles,claims,tenant,claimScopes,resolveMode}){let secretEnvName=options.authentication?.accessToken?.secret;if(!secretEnvName)throw Error("Access token secret env name is not configured");let secret=process.env[secretEnvName];if(!secret)throw Error(`Access token secret env "${secretEnvName}" is not set`);let token=signJWT({subject:sessionData.userId,issuer:options.authentication?.accessToken?.issuer,audience:options.authentication?.accessToken?.audience,algorithm:options.authentication?.accessToken?.algorithm,expiresInSeconds:parseTimeToSeconds2(options.authentication?.accessToken?.expiresIn??"15m"),sessionId:sessionData.id,customClaims:{refreshTokenId,...roles&&roles.length>0?{roles}:{},...!resolveMode&&claims&&claims.length>0?{claims}:{},...!resolveMode&&claimScopes&&Object.keys(claimScopes).length>0?{claimScopes}:{},...tenant?{tenant}:{}}},secret);return warnIfAccessTokenTooLargeForCookie(token,resolveMode?"resolve":options.authorization?.jwtClaimsMode),token}function resolveDbPoolConfig(pool){return{max:pool?.max!=null&&pool.max>0?pool.max:10,idleTimeoutMillis:pool?.idleTimeoutMillis??30000,connectionTimeoutMillis:pool?.connectionTimeoutMillis??1e4,...pool?.maxUses!=null&&pool.maxUses>0?{maxUses:pool.maxUses}:{}}}function toAudit(payload,summary,opts){return payload?{entityName:payload.entity_name,entityId:payload.entity_id===" - "?null:payload.entity_id,operation:payload.operation_type,userId:opts&&"userId"in opts?opts.userId??null:payload.user_id==="unknown"?null:payload.user_id,summary,severity:opts?.severity,category:opts?.category,ipAddress:payload.ip_address,userAgent:payload.user_agent,path:payload.path,query:payload.query}:void 0}function decodeUnverifiedSubject(token){if(!token)return null;try{let parts=token.split(".");if(parts.length<2)return null;let payloadPart=parts[1];if(!payloadPart)return null;let json=Buffer.from(payloadPart,"base64url").toString("utf-8"),decoded=JSON.parse(json);return typeof decoded.sub==="string"&&decoded.sub?decoded.sub:null}catch{return null}}function reconstructBracketParams(query){let result={},arrayGroups={};for(let[key2,value]of Object.entries(query)){let match=key2.match(/^(\w+)\[\d*\]\[(\w+)\]$/),arrayName=match?.[1],prop=match?.[2];if(arrayName&&prop){if(!arrayGroups[arrayName])arrayGroups[arrayName]={};let group=arrayGroups[arrayName];if(!group[prop])group[prop]=[];let arr=group[prop];if(Array.isArray(value))for(let v of value)arr.push(v);else arr.push(value)}else result[key2]=value}for(let[arrayName,props]of Object.entries(arrayGroups)){let propNames=Object.keys(props);if(propNames.length===0)continue;let maxLen=Math.max(...propNames.map((p)=>(props[p]||[]).length)),items=[];for(let i=0;i<maxLen;i++){let item={};for(let p of propNames)item[p]=(props[p]||[])[i];items.push(item)}result[arrayName]=items}return result}function parseQueryParams(query){let q=reconstructBracketParams(query),parseJSONOrPassthrough=(value)=>{if(value===void 0||value===null)return;if(typeof value==="object")return value;if(typeof value==="string")try{return JSON.parse(value)}catch{return}return},DEFAULT_PAGE_SIZE=20,MAX_PAGE_SIZE=200,rawPage=q.page?parseInt(q.page,10):1,page=Number.isFinite(rawPage)&&rawPage>0?rawPage:1,rawLimit=q.limit?parseInt(q.limit,10):20,limit=Number.isFinite(rawLimit)&&rawLimit>0?Math.min(rawLimit,200):20,rawOffset=q.offset?parseInt(q.offset,10):(page-1)*limit,offset=Number.isFinite(rawOffset)&&rawOffset>=0?rawOffset:0;return{page,limit,offset,search:q.search,searchFields:q.searchFields?q.searchFields.split(","):void 0,filters:parseJSONOrPassthrough(q.filters),sort:parseJSONOrPassthrough(q.sort),select:q.select?q.select.split(","):void 0,with:parseJSONOrPassthrough(q.with),distinct:q.distinct==="true",distinctOn:q.distinctOn?q.distinctOn.split(","):void 0}}function buildPaginationMeta(page,limit,offset,totalItems){let totalPages=Math.ceil(totalItems/limit),hasNextPage=page<totalPages,hasPrevPage=page>1;return{page,limit,offset,totalItems,totalPages,hasNextPage,hasPrevPage,nextPage:hasNextPage?page+1:null,prevPage:hasPrevPage?page-1:null}}function getBaseTypeValidator(type){let stringTypes=["varchar","char","text","uuid","citext","bit","varbit"],numberTypes=["integer","smallint","bigint","serial","smallserial","bigserial","real","doublePrecision","numeric","decimal"],booleanTypes=["boolean"];if(stringTypes.includes(type))return(v)=>({valid:typeof v==="string",expectedType:"string"});if(numberTypes.includes(type))return(v)=>({valid:typeof v==="number",expectedType:"number"});if(booleanTypes.includes(type))return(v)=>({valid:typeof v==="boolean",expectedType:"boolean"});if(type==="json"||type==="jsonb")return(v)=>({valid:typeof v==="object",expectedType:"object"});return()=>({valid:!0,expectedType:"any"})}function validatePayload(payload,columns,isPartial=!1){let errors=[];for(let col7 of columns){let value=payload[col7.name]??payload[col7.name.replace(/_([a-z])/g,(_,l)=>l.toUpperCase())],hasDbDefault=col7.default!==void 0||!!col7.defaultRaw||!!col7.generatedByDefaultAsIdentity||!!col7.generatedAlwaysAsIdentity||!!col7.generatedAlwaysAs,isRequired=col7.notNull&&!col7.nullable&&!hasDbDefault;if(value===void 0||value===null){if(isRequired&&!isPartial)errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} is required`});continue}if(col7.array){let baseValidator=getBaseTypeValidator(col7.type),expectedType=baseValidator(void 0).expectedType;if(!Array.isArray(value)){errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be an array of ${expectedType}`});continue}if(value.some((el)=>el!==null&&!baseValidator(el).valid))errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be an array of ${expectedType}`});continue}let typeCheck=getBaseTypeValidator(col7.type)(value);if(!typeCheck.valid){errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be of type ${typeCheck.expectedType}`});continue}if(typeof value==="string"){let len=value.length;if(col7.length&&len>col7.length)errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} exceeds max length of ${col7.length}`});if(col7.validation?.minLength&&len<col7.validation.minLength)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at least ${col7.validation.minLength} characters`});if(col7.validation?.maxLength&&len>col7.validation.maxLength)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at most ${col7.validation.maxLength} characters`});if(col7.validation?.pattern){if(!new RegExp(col7.validation.pattern).test(value))errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} does not match required pattern`})}if(col7.validation?.format){let formatRegex=FORMAT_PATTERNS[col7.validation.format];if(formatRegex&&!formatRegex.test(value))errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be a valid ${col7.validation.format}`})}}if(typeof value==="number"){if(col7.validation?.min!==void 0&&value<col7.validation.min)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at least ${col7.validation.min}`});if(col7.validation?.max!==void 0&&value>col7.validation.max)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at most ${col7.validation.max}`})}if(col7.enumValues&&col7.enumValues.length>0){if(!col7.enumValues.includes(value))errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be one of: ${col7.enumValues.join(", ")}`})}}return{valid:errors.length===0,errors}}function escapeHtml(str3){return str3.replace(/[&<>"'`=/]/g,(char)=>HTML_ENTITIES[char]||char)}function stripTags(str3){return str3.replace(/<[^>]*>/g,"")}function normalizeEmail(email){let parts=email.split("@"),localPart=parts[0],domain=parts[1];if(!localPart||!domain)return email;let beforePlus=localPart.split("+")[0];if(!beforePlus)return email;return`${beforePlus.replace(/\./g,"")}@${domain.toLowerCase()}`}function slugify(str3){return str3.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")}function applySanitizer(value,sanitizer){if(value===null||value===void 0)return value;switch(sanitizer){case"trim":return typeof value==="string"?value.trim():value;case"lowercase":return typeof value==="string"?value.toLowerCase():value;case"uppercase":return typeof value==="string"?value.toUpperCase():value;case"escapeHtml":return typeof value==="string"?escapeHtml(value):value;case"stripTags":return typeof value==="string"?stripTags(value):value;case"normalizeEmail":return typeof value==="string"?normalizeEmail(value):value;case"toNumber":if(typeof value==="number")return value;if(typeof value==="string"){let num2=Number(value);return Number.isNaN(num2)?value:num2}return value;case"toBoolean":if(typeof value==="boolean")return value;if(typeof value==="string"){let lower=value.toLowerCase();if(lower==="true"||lower==="1"||lower==="yes")return!0;if(lower==="false"||lower==="0"||lower==="no")return!1}if(typeof value==="number")return value!==0;return value;case"slugify":return typeof value==="string"?slugify(value):value;default:return value}}function isSensitiveOutputKey(key2){return SENSITIVE_OUTPUT_KEYS.has(key2)}function redactSensitiveOutput(row){return scrubSensitive(row)}function scrubSensitive(value){if(value===null||typeof value!=="object")return value;if(Array.isArray(value))return value.map(scrubSensitive);let proto=Object.getPrototypeOf(value);if(proto!==Object.prototype&&proto!==null)return value;let out={};for(let[k,v]of Object.entries(value)){if(SENSITIVE_OUTPUT_KEYS.has(k))continue;out[k]=scrubSensitive(v)}return out}function sanitizePayload(payload,columns,opts){let sanitized={},toCamel2=(s)=>s.replace(/_([a-z])/g,(_,l)=>l.toUpperCase()),SERVER_MANAGED=new Set(["id","created_at","updated_at","created_by","updated_by","createdAt","updatedAt","createdBy","updatedBy"]),PROTECTED_SENSITIVE=new Set(["is_god","password","password_hash","email_verified","verified_at","email_verified_at","is_locked","locked_until","failed_login_attempts","login_count","last_login_at","email_verification_token","email_verification_sent_at","email_verification_expires_at","password_reset_token","password_reset_expires_at","password_reset_sent_at","magic_link_token","two_factor_secret"]),STORAGE_MANAGED=opts?.isFormData?new Set(["path","name","original_name","originalName","extension","size","mime_type","mimeType","uploaded_by","uploadedBy"]):null;for(let key2 of Object.keys(payload)){if(SERVER_MANAGED.has(key2))continue;if(STORAGE_MANAGED?.has(key2))continue;let value=payload[key2],snakeKey=key2.replace(/[A-Z]/g,(l)=>`_${l.toLowerCase()}`);if(PROTECTED_SENSITIVE.has(snakeKey))continue;let col7=columns.find((c)=>c.name===key2||c.name===snakeKey);if(col7?.readOnly)continue;if(col7?.sanitize&&col7.sanitize.length>0)for(let sanitizer of col7.sanitize)value=applySanitizer(value,sanitizer);if(col7&&(col7.type==="timestamp"||col7.type==="timestamptz"||col7.type==="date")&&typeof value==="string"){let parsed=new Date(value);if(!Number.isNaN(parsed.getTime()))value=parsed}let normalizedKey=key2.includes("_")?toCamel2(key2):key2;sanitized[normalizedKey]=value}return sanitized}function createAuditLog(db,auditTable,entry){if(!db||!auditTable)return;let logEntry={user_id:entry.user_id,entity_name:entry.entity_name,entity_id:entry.entity_id,operation:entry.operation,old_data:entry.old_data??null,new_data:entry.new_data??null,timestamp:new Date().toISOString()};db.insert(auditTable).values(logEntry).execute().catch((err)=>{logger.error("[Audit] Database audit write failed",err,{entity:logEntry.entity_name,operation:logEntry.operation})})}async function refreshAccessTokenWithLock(userId,sessionId,generateToken){let redis=new RedisManager,lockKey=`${REFRESH_LOCK_PREFIX}${userId}`,cacheKey=`${ACCESS_TOKEN_CACHE_PREFIX}${userId}:${sessionId}`,cachedResult=await redis.read(cacheKey);if(cachedResult.success&&cachedResult.data)return{success:!0,accessToken:cachedResult.data,fromCache:!0};let lockResult=await redis.acquireLock(lockKey,LOCK_TTL_SECONDS);if(!lockResult.success)return{success:!1,error:lockResult.error};if(lockResult.data)try{let newToken=generateToken();return await redis.create(cacheKey,newToken,ACCESS_TOKEN_CACHE_TTL_SECONDS),{success:!0,accessToken:newToken,fromCache:!1}}finally{await redis.releaseLock(lockKey)}let waitResult=await redis.waitForLock(lockKey,LOCK_WAIT_TIMEOUT_MS,50);if(!waitResult.success)return{success:!1,error:waitResult.error};if(!waitResult.data)return{success:!1,error:"Lock wait timeout"};let newCachedResult=await redis.read(cacheKey);if(newCachedResult.success&&newCachedResult.data)return{success:!0,accessToken:newCachedResult.data,fromCache:!0};let fallbackToken=generateToken();return await redis.create(cacheKey,fallbackToken,ACCESS_TOKEN_CACHE_TTL_SECONDS),{success:!0,accessToken:fallbackToken,fromCache:!1}}function resolveConfigCredential(value){if(!value)return;let fromEnv=process.env[value];if(fromEnv)return fromEnv;if(/^[A-Z][A-Z0-9_]{2,}$/.test(value))return;return value}function validateEnvVariables(config){let errors=[],resolved={},databaseAuthMode=process.env.DATABASE_AUTH_MODE||"password",redisAuthMode=process.env.REDIS_AUTH_MODE||"password";resolved.databaseAuthMode=databaseAuthMode,resolved.redisAuthMode=redisAuthMode;let entraIdModes=["managed_identity","service_principal"];if(entraIdModes.includes(databaseAuthMode)||entraIdModes.includes(redisAuthMode)){if(!process.env.AZURE_CLIENT_ID)logger.warn("[Config] AZURE_CLIENT_ID is not set. Required for user-assigned managed identity.");if([databaseAuthMode,redisAuthMode].filter((m)=>m==="service_principal").length>0){if(!process.env.AZURE_TENANT_ID)errors.push({field:"azure.tenantId",envName:"AZURE_TENANT_ID",message:"AZURE_TENANT_ID is required for service_principal auth mode."});if(!process.env.AZURE_CLIENT_SECRET)errors.push({field:"azure.clientSecret",envName:"AZURE_CLIENT_SECRET",message:"AZURE_CLIENT_SECRET is required for service_principal auth mode."})}}if(config.database?.url){let envValue=process.env[config.database.url];if(!envValue)errors.push({field:"database.url",envName:config.database.url,message:`Environment variable "${config.database.url}" is not set. Please set it in your .env file.`});else resolved.databaseUrl=envValue}let validationWithDapr=typeof config.redis?.withDapr==="string"?process.env[config.redis.withDapr]?.toLowerCase()!=="false":config.redis?.withDapr??!1;if(config.redis&&!validationWithDapr)if(config.redis.url){let envValue=process.env[config.redis.url];if(!envValue)errors.push({field:"redis.url",envName:config.redis.url,message:`Environment variable "${config.redis.url}" is not set. Please set it in your .env file.`});else resolved.redisUrl=envValue}else{if(config.redis.host){if(!process.env[config.redis.host])errors.push({field:"redis.host",envName:config.redis.host,message:`Environment variable "${config.redis.host}" is not set. Please set it in your .env file.`})}if(config.redis.port){if(!process.env[config.redis.port])errors.push({field:"redis.port",envName:config.redis.port,message:`Environment variable "${config.redis.port}" is not set. Please set it in your .env file.`})}}if(config.authentication?.enabled){if(!config.authentication.mode)errors.push({field:"authentication.mode",envName:"",message:'authentication.mode is required when authentication is enabled. Use "full" for IDP/standalone services, "consumer" for resource servers.'});if(config.authentication.accessToken?.secret){let envValue=process.env[config.authentication.accessToken.secret];if(!envValue)errors.push({field:"authentication.accessToken.secret",envName:config.authentication.accessToken.secret,message:`Environment variable "${config.authentication.accessToken.secret}" is not set. Please set it in your .env file.`});else resolved.accessTokenSecret=envValue}else errors.push({field:"authentication.accessToken.secret",envName:"",message:"authentication.accessToken.secret is required when authentication is enabled."});let isConsumerMode=config.authentication.mode==="consumer";if(config.authentication.refreshToken?.secret){let envValue=process.env[config.authentication.refreshToken.secret];if(!envValue)errors.push({field:"authentication.refreshToken.secret",envName:config.authentication.refreshToken.secret,message:`Environment variable "${config.authentication.refreshToken.secret}" is not set. Please set it in your .env file.`});else resolved.refreshTokenSecret=envValue}else if(!isConsumerMode)errors.push({field:"authentication.refreshToken.secret",envName:"",message:"authentication.refreshToken.secret is required when authentication is enabled."});if(config.authentication.sessionToken?.secret){let envValue=process.env[config.authentication.sessionToken.secret];if(!envValue)errors.push({field:"authentication.sessionToken.secret",envName:config.authentication.sessionToken.secret,message:`Environment variable "${config.authentication.sessionToken.secret}" is not set. Please set it in your .env file.`});else resolved.sessionTokenSecret=envValue}else if(!isConsumerMode)errors.push({field:"authentication.sessionToken.secret",envName:"",message:"authentication.sessionToken.secret is required when authentication is enabled."})}if(config.authentication?.oauth?.enabled&&config.authentication.oauth.providers){let resolvedProviders={};for(let[providerName,providerConfig]of Object.entries(config.authentication.oauth.providers)){if(!providerConfig)continue;let{clientId:clientIdEnvName,clientSecret:clientSecretEnvName,redirectUri:redirectUriEnvName,tenantId:tenantIdEnvName}=providerConfig,clientId=resolveConfigCredential(clientIdEnvName),clientSecret=resolveConfigCredential(clientSecretEnvName),redirectUri=resolveConfigCredential(redirectUriEnvName),tenantId=resolveConfigCredential(tenantIdEnvName),credentialsMayComeFromDb=config.secrets?.enabled===!0;if(!clientId&&!credentialsMayComeFromDb)errors.push({field:`authentication.oauth.providers.${providerName}.clientId`,envName:clientIdEnvName,message:`Environment variable "${clientIdEnvName}" is not set (OAuth ${providerName} clientId).`});if(!clientSecret&&!credentialsMayComeFromDb)errors.push({field:`authentication.oauth.providers.${providerName}.clientSecret`,envName:clientSecretEnvName,message:`Environment variable "${clientSecretEnvName}" is not set (OAuth ${providerName} clientSecret).`});if(clientId&&clientSecret||credentialsMayComeFromDb)resolvedProviders[providerName]={clientId:clientId??"",clientSecret:clientSecret??"",redirectUri:redirectUri??"",tenantId,scopes:providerConfig.scopes,authorizationUrl:providerConfig.authorizationUrl,tokenUrl:providerConfig.tokenUrl,userInfoUrl:providerConfig.userInfoUrl,extraAuthParams:providerConfig.extraAuthParams}}if(Object.keys(resolvedProviders).length>0)resolved.oauthProviders=resolvedProviders}return{valid:errors.length===0,errors,resolved}}async function ensureDatabaseExists(databaseUrl,logger2,authMode){let{Pool:Pool2}=await import("pg"),targetDb=new URL(databaseUrl).pathname.replace("/","");if(!targetDb)return;let adminUrl=new URL(databaseUrl);adminUrl.pathname="/postgres";let pool;if(authMode&&authMode!=="password"){let{getPostgresToken:getPostgresToken2}=await Promise.resolve().then(() => (init_Azure(),exports_Azure));pool=new Pool2({connectionString:adminUrl.toString(),password:getPostgresToken2,ssl:{rejectUnauthorized:!0}})}else pool=new Pool2({connectionString:adminUrl.toString()});try{if((await pool.query("SELECT 1 FROM pg_database WHERE datname = $1",[targetDb])).rowCount===0)logger2.info(`[Database] Creating database "${targetDb}"...`),await pool.query(`CREATE DATABASE "${targetDb}" TEMPLATE template0`),logger2.info(`[Database] Database "${targetDb}" created successfully`);else logger2.info(`[Database] Database "${targetDb}" exists`)}catch(err){let message=err instanceof Error?err.message:String(err);logger2.warn(`[Database] Could not auto-create database: ${message}`)}finally{await pool.end()}}var redisManagerInstance=null,COOKIE_SAFE_TOKEN_BYTES=3500,lastOversizeWarnAt=0,FORMAT_PATTERNS,HTML_ENTITIES,SENSITIVE_OUTPUT_KEYS,ACCESS_TOKEN_CACHE_PREFIX="access_token:",REFRESH_LOCK_PREFIX="refresh_lock:",LOCK_TTL_SECONDS=5,LOCK_WAIT_TIMEOUT_MS=3000,ACCESS_TOKEN_CACHE_TTL_SECONDS=60;var init_utils5=__esm(()=>{init_Managers();init_Services();init_Logger2();FORMAT_PATTERNS={email:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,url:/^https?:\/\/.+/,uuid:/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,date:/^\d{4}-\d{2}-\d{2}$/,datetime:/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,time:/^\d{2}:\d{2}:\d{2}$/,uri:/^[a-z][a-z0-9+.-]*:/i,ipv4:/^(\d{1,3}\.){3}\d{1,3}$/,ipv6:/^([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$/i};HTML_ENTITIES={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;","`":"&#96;","=":"&#x3D;"};SENSITIVE_OUTPUT_KEYS=new Set(["password","passwordHash","password_hash","emailVerificationToken","email_verification_token","emailVerificationTokenExpiresAt","email_verification_token_expires_at","passwordResetToken","password_reset_token","refreshTokenHash","refresh_token_hash"])});import{eq as eq13}from"drizzle-orm";import{pgSchema}from"drizzle-orm/pg-core";class TenantRegistry{db;logger;mainSchemaName;mainSchemaTables;mainSchemaRelations;createAllTablesForSchema;createAllRelationsForSchema;appId;authMode;tenantResolution;tenantHeader;redisCacheTtlSeconds;defaultTrustedSources;idpUrl;allowDataLoss;onTenantProvisioned;tenantsBySubdomain=new Map;tenantsBySchemaName=new Map;tenantsById=new Map;schemaContexts=new Map;tenantFeatures=new Map;tenantsByDomainHostname=new Map;constructor(config){this.db=config.db,this.logger=config.logger,this.mainSchemaName=config.mainSchemaName,this.mainSchemaTables=config.mainSchemaTables,this.mainSchemaRelations=config.mainSchemaRelations,this.createAllTablesForSchema=config.createAllTablesForSchema,this.createAllRelationsForSchema=config.createAllRelationsForSchema,this.appId=config.appId,this.authMode=config.authMode,this.tenantResolution=config.tenantResolution,this.tenantHeader=config.tenantHeader,this.redisCacheTtlSeconds=config.redisCacheTtlSeconds,this.defaultTrustedSources=config.defaultTrustedSources,this.idpUrl=config.idpUrl,this.allowDataLoss=config.allowDataLoss===!0,this.onTenantProvisioned=config.onTenantProvisioned}async initialize(){this.logger.info("[TenantRegistry] Initializing..."),this.schemaContexts.set(this.mainSchemaName,{schemaName:this.mainSchemaName,schemaTables:this.mainSchemaTables,schemaRelations:this.mainSchemaRelations,tenant:null});let tenants=await this.loadTenantsFromDb();this.logger.info(`[TenantRegistry] Loaded ${tenants.length} tenants from database`);let features=await this.loadTenantFeaturesFromDb();this.logger.info(`[TenantRegistry] Loaded ${features.length} tenant feature mappings`);for(let tenant of tenants)this.indexTenant(tenant);for(let feature of features){let existing=this.tenantFeatures.get(feature.tenantId)||[];existing.push(feature),this.tenantFeatures.set(feature.tenantId,existing)}let activeTenants=tenants.filter((t)=>t.status==="active");for(let tenant of activeTenants)await this.buildSchemaContext(tenant);await this.loadDomainHostnames(),this.logger.info(`[TenantRegistry] Initialized with ${activeTenants.length} active tenant schemas + main schema`)}resolveFromRequest(request,vettedClientIp){let url=new URL(request.url),host=request.headers.get("host")||url.host||"",normalizedHost=normalizeHost(host);if(normalizedHost){let byHostname=this.tenantsByDomainHostname.get(normalizedHost);if(byHostname)return this.validateAndReturnTenant(byHostname)}if(this.tenantResolution==="subdomain"||this.tenantResolution==="both"){let subdomain=extractSubdomain(host);if(subdomain){let tenant=this.tenantsBySubdomain.get(subdomain);if(tenant)return this.validateAndReturnTenant(tenant);return{resolved:!1,error:`Tenant not found for subdomain: ${subdomain}`,statusCode:404}}}if(this.tenantResolution==="header"||this.tenantResolution==="both"){let tenantIdOrSubdomain=request.headers.get(this.tenantHeader);if(tenantIdOrSubdomain){let result=this.resolveFromHeader(tenantIdOrSubdomain,request,vettedClientIp);if(result)return result}}let mainContext=this.schemaContexts.get(this.mainSchemaName);if(mainContext)return{resolved:!0,context:mainContext};return{resolved:!1,error:"No tenant could be resolved and main schema is unavailable",statusCode:500}}getSchemaContext(schemaName){return this.schemaContexts.get(schemaName)}getMainContext(){let ctx=this.schemaContexts.get(this.mainSchemaName);if(!ctx)throw Error("[TenantRegistry] Main schema context not initialized");return ctx}getActiveTenants(){return Array.from(this.tenantsById.values()).filter((t)=>t.status==="active")}getTenantById(id){return this.tenantsById.get(id)}getTenantBySchemaName(schemaName){return this.tenantsBySchemaName.get(schemaName)}getTenantFeatures(tenantId){return this.tenantFeatures.get(tenantId)||[]}isTenantFeatureEnabled(tenantId,featureName){return(this.tenantFeatures.get(tenantId)||[]).find((f)=>f.featureName===featureName)?.enabled??!1}getTenantIdsWithFeature(featureName){let result=[];for(let[tenantId,features]of this.tenantFeatures)if(features.find((f)=>f.featureName===featureName&&f.enabled))result.push(tenantId);return result}getSchemaNamesWithFeature(featureName){let tenantIds=this.getTenantIdsWithFeature(featureName),schemaNames=[];for(let tenantId of tenantIds){let tenant=this.tenantsById.get(tenantId);if(tenant&&tenant.status==="active")schemaNames.push(tenant.schemaName)}return schemaNames}getAllSchemaNames(){return Array.from(this.schemaContexts.keys())}async provisionTenant(tenant){this.logger.info(`[TenantRegistry] Provisioning tenant: ${tenant.subdomain} \u2192 ${tenant.schemaName}`),await ensureSchemaExists(this.db,tenant.schemaName);let context=await this.buildSchemaContext(tenant);if(this.indexTenant(tenant),await this.syncSchemaToDb(context),this.onTenantProvisioned)try{await this.onTenantProvisioned(context)}catch(err){let msg=err instanceof Error?err.message:String(err);this.logger.warn(`[TenantRegistry] onTenantProvisioned hook failed for ${tenant.schemaName}: ${msg}`)}return this.logger.info(`[TenantRegistry] Tenant provisioned: ${tenant.subdomain}`),context}async syncSchemaToDb(context){let{pushSchema}=await import("drizzle-kit/api"),{applySchemaPush:applySchemaPush2}=await Promise.resolve().then(() => (init_schema(),exports_schema)),targetSchema=pgSchema(context.schemaName);try{let push=await pushSchema({schema:targetSchema,...context.schemaTables},this.db,[context.schemaName]);if(await applySchemaPush2(push,{schemaName:context.schemaName,allowDataLoss:this.allowDataLoss,logger:this.logger}))this.logger.info(`[TenantRegistry] Schema sync completed for: ${context.schemaName}`)}catch(err){let msg=err instanceof Error?err.message:String(err);this.logger.warn(`[TenantRegistry] Schema sync warning for ${context.schemaName}: ${msg}`)}}async syncAllSchemas(){for(let[schemaName,context]of this.schemaContexts)this.logger.info(`[TenantRegistry] Syncing schema: ${schemaName}`),await ensureSchemaExists(this.db,schemaName),await this.syncSchemaToDb(context)}async invalidateCache(subdomain){try{let{getRedisManager:getRedisManager2}=await Promise.resolve().then(() => (init_utils5(),exports_utils)),redis=getRedisManager2();if(redis)await redis.remove(`tenant:${subdomain}`),this.logger.info(`[TenantRegistry] Cache invalidated for: ${subdomain}`)}catch{}}async refreshTenant(tenantId){let tenantsTable=this.mainSchemaTables.tenants;if(!tenantsTable)return;let row=(await this.db.select().from(tenantsTable).where(eq13(tenantsTable.id,tenantId)).limit(1))[0];if(!row)return;let tenant=rowToTenantRecord(row),oldTenant=this.tenantsById.get(tenantId);if(oldTenant)this.tenantsBySubdomain.delete(oldTenant.subdomain),this.tenantsBySchemaName.delete(oldTenant.schemaName);if(this.indexTenant(tenant),tenant.status==="active")await this.buildSchemaContext(tenant);else this.schemaContexts.delete(tenant.schemaName);if(oldTenant)await this.invalidateCache(oldTenant.subdomain);await this.invalidateCache(tenant.subdomain)}async initializeFromIdp(){if(!this.idpUrl){this.logger.error("[TenantRegistry] Consumer multi-tenant requires idpUrl (IDP_URL)");return}this.logger.info(`[TenantRegistry] Initializing from IDP: ${this.idpUrl}`),this.schemaContexts.set(this.mainSchemaName,{schemaName:this.mainSchemaName,schemaTables:this.mainSchemaTables,schemaRelations:this.mainSchemaRelations,tenant:null});let tenants=await this.loadTenantsFromIdp();this.logger.info(`[TenantRegistry] Fetched ${tenants.length} tenants from IDP`);for(let tenant of tenants)this.indexTenant(tenant);let activeTenants=tenants.filter((t)=>t.status==="active");for(let tenant of activeTenants)await this.buildSchemaContext(tenant);await this.loadDomainHostnames(),this.logger.info(`[TenantRegistry] Consumer initialized with ${activeTenants.length} active tenant schemas + main schema`)}isConsumerMode(){return this.authMode==="consumer"&&!!this.idpUrl}async syncFromIdp(){if(!this.isConsumerMode())return{added:[],removed:[],total:this.tenantsById.size};let tenants=await this.loadTenantsFromIdp(),added=[],removed=[],seenSchemaNames=new Set;for(let tenant of tenants){seenSchemaNames.add(tenant.schemaName);let known=this.tenantsById.get(tenant.id);if(known)this.tenantsBySubdomain.delete(known.subdomain),this.tenantsBySchemaName.delete(known.schemaName);if(this.indexTenant(tenant),tenant.status==="active"){if(!this.schemaContexts.has(tenant.schemaName)){await ensureSchemaExists(this.db,tenant.schemaName);let ctx=await this.buildSchemaContext(tenant);await this.syncSchemaToDb(ctx),added.push(tenant.schemaName),await this.invalidateCache(tenant.subdomain),this.logger.info(`[TenantRegistry] Synced new tenant from IDP: ${tenant.subdomain}`)}}else if(this.schemaContexts.has(tenant.schemaName))this.schemaContexts.delete(tenant.schemaName),removed.push(tenant.schemaName),await this.invalidateCache(tenant.subdomain),this.logger.info(`[TenantRegistry] Deactivated tenant context: ${tenant.subdomain}`)}for(let[schemaName,ctx]of this.schemaContexts){if(schemaName===this.mainSchemaName)continue;if(!seenSchemaNames.has(schemaName)){if(this.schemaContexts.delete(schemaName),removed.push(schemaName),ctx.tenant)this.tenantsBySubdomain.delete(ctx.tenant.subdomain),this.tenantsById.delete(ctx.tenant.id),this.tenantsBySchemaName.delete(schemaName),await this.invalidateCache(ctx.tenant.subdomain)}}return{added,removed,total:tenants.length}}registerDomainHostname(hostname,tenantId){let tenant=this.tenantsById.get(tenantId);if(!tenant)return;this.tenantsByDomainHostname.set(normalizeHost(hostname),tenant)}unregisterDomainHostname(hostname){this.tenantsByDomainHostname.delete(normalizeHost(hostname))}async loadDomainHostnames(){let table=this.mainSchemaTables.domainHostnames;if(!table)return 0;try{let rows=await this.db.select().from(table),count=0;this.tenantsByDomainHostname.clear();for(let row of rows){let status=String(row.status??""),tenantId=String(row.tenantId??row.tenant_id??""),hostname=String(row.normalizedHostname??row.normalized_hostname??"");if(status!=="active"||!tenantId||!hostname)continue;let tenant=this.tenantsById.get(tenantId);if(!tenant)continue;this.tenantsByDomainHostname.set(normalizeHost(hostname),tenant),count++}if(count>0)this.logger.info(`[TenantRegistry] Indexed ${count} active custom hostnames`);return count}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to load custom hostnames: ${msg}`),0}}async loadTenantsFromIdp(){if(!this.idpUrl)return[];try{let response=await fetch(`${this.idpUrl}/tenants`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!response.ok)return this.logger.warn(`[TenantRegistry] IDP tenant fetch failed: ${response.status} ${response.statusText}`),[];return((await response.json())?.data?.items||[]).map((item)=>rowToTenantRecord(item))}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to fetch tenants from IDP: ${msg}`),[]}}async loadTenantsFromDb(){let tenantsTable=this.mainSchemaTables.tenants;if(!tenantsTable)return this.logger.warn("[TenantRegistry] No tenants table found in main schema"),[];try{return(await this.db.select().from(tenantsTable)).map((row)=>rowToTenantRecord(row))}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to load tenants: ${msg}`),[]}}async loadTenantFeaturesFromDb(){let featuresTable=this.mainSchemaTables.tenantFeatures;if(!featuresTable)return this.logger.warn("[TenantRegistry] No tenant_features table found in main schema"),[];try{return(await this.db.select().from(featuresTable)).map((row)=>rowToFeatureRecord(row,parseJsonbToConfig))}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to load tenant features: ${msg}`),[]}}indexTenant(tenant){if(this.tenantsById.set(tenant.id,tenant),this.tenantsBySubdomain.set(tenant.subdomain,tenant),this.tenantsBySchemaName.set(tenant.schemaName,tenant),tenant.domain)this.tenantsBySubdomain.set(tenant.domain,tenant)}async buildSchemaContext(tenant){let schema=pgSchema(tenant.schemaName),schemaTables=this.createAllTablesForSchema(schema),schemaRelations=this.createAllRelationsForSchema?this.createAllRelationsForSchema(schema):{},context={schemaName:tenant.schemaName,schemaTables,schemaRelations,tenant};return this.schemaContexts.set(tenant.schemaName,context),context}resolveFromHeader(tenantIdOrSubdomain,request,vettedClientIp){let tenant=this.tenantsBySubdomain.get(tenantIdOrSubdomain);if(!tenant)tenant=this.tenantsById.get(tenantIdOrSubdomain);if(!tenant)tenant=this.tenantsBySchemaName.get(tenantIdOrSubdomain);if(!tenant)return{resolved:!1,error:`Tenant not found for header value: ${tenantIdOrSubdomain}`,statusCode:404};if(!isTrustedSource(tenant,request,this.authMode,this.defaultTrustedSources,vettedClientIp))return{resolved:!1,error:`Untrusted source for tenant: ${tenant.subdomain}`,statusCode:403};return this.validateAndReturnTenant(tenant)}validateAndReturnTenant(tenant){if(tenant.status==="suspended")return{resolved:!1,error:`Tenant ${tenant.subdomain} is suspended: ${tenant.suspendedReason||"No reason provided"}`,statusCode:403};if(tenant.status==="provisioning")return{resolved:!1,error:`Tenant ${tenant.subdomain} is still being provisioned`,statusCode:503};if(tenant.status==="archived")return{resolved:!1,error:`Tenant ${tenant.subdomain} has been archived`,statusCode:410};let context=this.schemaContexts.get(tenant.schemaName);if(!context)return{resolved:!1,error:`Schema context not found for tenant: ${tenant.subdomain}`,statusCode:500};return{resolved:!0,context}}}var init_TenantRegistry=__esm(()=>{init_schema()});var init_Tenant=__esm(()=>{init_schema();init_TenantRegistry()});var init_types5=()=>{};import{and as and8,desc as desc2,eq as eq14,inArray as inArray2}from"drizzle-orm";function toCamel2(obj){let result={};for(let[key2,value]of Object.entries(obj)){let camelKey=key2.replace(/_([a-z])/g,(_,c)=>c.toUpperCase());result[camelKey]=value}return result}function fromCamel2(obj){let result={};for(let[key2,value]of Object.entries(obj)){let snakeKey=key2.replace(/[A-Z]/g,(c)=>`_${c.toLowerCase()}`);result[snakeKey]=value}return result}class VerificationService{db;schemaTables;config;logger;onNotificationTrigger;constructor(serviceConfig){this.db=serviceConfig.db,this.schemaTables=serviceConfig.schemaTables,this.config=serviceConfig.config,this.logger=serviceConfig.logger}setNotificationHandler(handler){this.onNotificationTrigger=handler}getConnectedNotificationNodeIds(stepNodeId,steps,edges){let result=new Set,getNeighbors=(nid)=>{let neighbors=[];for(let edge of edges){let{sourceNodeId:src,targetNodeId:tgt}=edge;if(src===nid)neighbors.push(tgt);else if(tgt===nid)neighbors.push(src)}return neighbors},directNeighbors=getNeighbors(stepNodeId);for(let neighborId of directNeighbors)if(steps.find((s)=>s.nodeId===neighborId)?.nodeType==="notification")result.add(neighborId);for(let neighborId of directNeighbors)if(steps.find((s)=>s.nodeId===neighborId)?.nodeType==="verifier"){let verifierNeighbors=getNeighbors(neighborId);for(let vNeighborId of verifierNeighbors){if(vNeighborId===stepNodeId)continue;if(steps.find((s)=>s.nodeId===vNeighborId)?.nodeType==="notification")result.add(vNeighborId)}}let ids=[...result];return this.logger.info(`[Verification] Connected notification nodes for step ${stepNodeId}: [${ids.join(", ")}]`),ids}getTable(name){return resolveSchemaTable(this.schemaTables,name,this.logger)}getCol(table,col7){return table[col7]}async listFlows(entityName){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return[];return(await(entityName?this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"entityName"),entityName)):this.db.select().from(flowsTable))).map((r)=>fromCamel2(r))}async getFlow(flowId){let flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),notifRulesTable=this.getTable("verificationNotificationRules"),notifRecipientsTable=this.getTable("verificationNotificationRecipients"),notifChannelsTable=this.getTable("verificationNotificationChannels");if(!flowsTable)return null;let flowRow=(await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),flowId)).limit(1))[0];if(!flowRow)return null;let flow=fromCamel2(flowRow),steps=(stepsTable?await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),edges=(edgesTable?await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),verifierConfigs=(verifierConfigsTable?await this.db.select().from(verifierConfigsTable).where(eq14(this.getCol(verifierConfigsTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),notifRules=(notifRulesTable?await this.db.select().from(notifRulesTable).where(eq14(this.getCol(notifRulesTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),ruleIds=notifRules.map((r)=>r.id),notifRecipients=(notifRecipientsTable&&ruleIds.length>0?await this.db.select().from(notifRecipientsTable).where(inArray2(this.getCol(notifRecipientsTable,"ruleId"),ruleIds)):[]).map((r)=>fromCamel2(r)),notifChannels=(notifChannelsTable&&ruleIds.length>0?await this.db.select().from(notifChannelsTable).where(inArray2(this.getCol(notifChannelsTable,"ruleId"),ruleIds)):[]).map((r)=>fromCamel2(r));return{flow,graph:{steps,edges,verifier_configs:verifierConfigs,notification_rules:notifRules,notification_recipients:notifRecipients,notification_channels:notifChannels}}}async saveFlow(params){let flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),notifRulesTable=this.getTable("verificationNotificationRules"),notifRecipientsTable=this.getTable("verificationNotificationRecipients"),notifChannelsTable=this.getTable("verificationNotificationChannels");if(!flowsTable||!stepsTable||!edgesTable)throw Error("Verification tables not configured");let existingFlows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),params.flow_id)).limit(1),flowId=params.flow_id;if(existingFlows.length>0)await this.db.update(flowsTable).set(toCamel2({entity_name:params.entity_name,name:params.name,description:params.description||null,trigger_on:params.trigger_on,trigger_fields:params.trigger_fields||null,is_draft:params.is_draft,viewport:params.viewport||null})).where(eq14(this.getCol(flowsTable,"id"),flowId));else{let[newFlow]=await this.db.insert(flowsTable).values(toCamel2({id:flowId,entity_name:params.entity_name,name:params.name,description:params.description||null,trigger_on:params.trigger_on,trigger_fields:params.trigger_fields||null,is_draft:params.is_draft,viewport:params.viewport||null})).returning();flowId=newFlow.id}if(await this.db.delete(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),flowId)),edgesTable)await this.db.delete(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),flowId));if(verifierConfigsTable)await this.db.delete(verifierConfigsTable).where(eq14(this.getCol(verifierConfigsTable,"flowId"),flowId));if(notifRulesTable){let oldRuleIds=(await this.db.select().from(notifRulesTable).where(eq14(this.getCol(notifRulesTable,"flowId"),flowId))).map((r)=>r.id);if(oldRuleIds.length>0){if(notifRecipientsTable)for(let rid of oldRuleIds)await this.db.delete(notifRecipientsTable).where(eq14(this.getCol(notifRecipientsTable,"ruleId"),rid));if(notifChannelsTable)for(let rid of oldRuleIds)await this.db.delete(notifChannelsTable).where(eq14(this.getCol(notifChannelsTable,"ruleId"),rid))}await this.db.delete(notifRulesTable).where(eq14(this.getCol(notifRulesTable,"flowId"),flowId))}let{graph}=params;if(graph.steps.length>0)await this.db.insert(stepsTable).values(graph.steps.map((s)=>toCamel2({flow_id:flowId,entity_name:params.entity_name,node_id:s.node_id,node_type:s.node_type,step_order:s.step_order,name:s.name||null,description:s.description||null,position_x:s.position_x,position_y:s.position_y,width:s.width||null,height:s.height||null,style:s.style||null,data:s.data||null})));if(graph.edges.length>0&&edgesTable)await this.db.insert(edgesTable).values(graph.edges.map((e)=>toCamel2({flow_id:flowId,edge_id:e.edge_id,source_node_id:e.source_node_id,target_node_id:e.target_node_id,source_handle:e.source_handle||null,target_handle:e.target_handle||null,edge_type:e.edge_type,label:e.label||null,condition:e.condition||null,style:e.style||null,animated:e.animated})));if(graph.verifier_configs.length>0&&verifierConfigsTable)await this.db.insert(verifierConfigsTable).values(graph.verifier_configs.map((vc)=>toCamel2({flow_id:flowId,node_id:vc.node_id,verifier_type:vc.verifier_type,verifier_user_id:vc.verifier_user_id||null,verifier_role:vc.verifier_role||null,require_signature:vc.require_signature,all_must_approve:vc.all_must_approve})));if(this.logger.info(`[Verification] Save: ${graph.notification_rules.length} rules, ${graph.notification_recipients.length} recipients, ${graph.notification_channels.length} channels`),graph.notification_rules.length>0&&notifRulesTable)for(let rule of graph.notification_rules){this.logger.info(`[Verification] Saving notification rule: node_id=${rule.node_id}, trigger=${rule.trigger}, title_template=${JSON.stringify(rule.title_template)}, body_template=${JSON.stringify(rule.body_template)}`);let[insertedRule]=await this.db.insert(notifRulesTable).values(toCamel2({flow_id:flowId,node_id:rule.node_id,trigger:rule.trigger,title_template:rule.title_template||null,body_template:rule.body_template||null,starts_at:rule.starts_at||null,expires_at:rule.expires_at||null})).returning(),ruleId=insertedRule.id,ruleRecipients=graph.notification_recipients.filter((r)=>r.rule_id===rule.node_id);if(ruleRecipients.length>0&&notifRecipientsTable)await this.db.insert(notifRecipientsTable).values(ruleRecipients.map((r)=>toCamel2({rule_id:ruleId,recipient_type:r.recipient_type,recipient_user_id:r.recipient_user_id||null,recipient_role:r.recipient_role||null})));let ruleChannels=graph.notification_channels.filter((c)=>c.rule_id===rule.node_id);if(ruleChannels.length>0&&notifChannelsTable)await this.db.insert(notifChannelsTable).values(ruleChannels.map((c)=>toCamel2({rule_id:ruleId,channel:c.channel})))}return this.logger.info(`[Verification] Flow saved: ${flowId} for ${params.entity_name}`),{success:!0,flow_id:flowId}}async publishFlow(flowId){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return{success:!1,message:"Flow table not configured"};return await this.db.update(flowsTable).set(toCamel2({is_draft:!1,published_at:new Date})).where(eq14(this.getCol(flowsTable,"id"),flowId)),this.logger.info(`[Verification] Flow published: ${flowId}`),{success:!0,message:"Flow published"}}async deleteFlow(flowId){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return{success:!1,message:"Flow table not configured"};return await this.db.delete(flowsTable).where(eq14(this.getCol(flowsTable,"id"),flowId)),this.logger.info(`[Verification] Flow deleted: ${flowId}`),{success:!0,message:"Flow deleted"}}async startFlow(params){let flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),instancesTable=this.getTable("verificationInstances"),requirementsTable=this.getTable("verificationRequirements"),userRolesTable=this.getTable("userRoles");if(!flowsTable||!stepsTable||!edgesTable||!instancesTable||!requirementsTable)return{success:!1,message:"Verification tables not configured"};let flow=(await this.db.select().from(flowsTable).where(and8(eq14(this.getCol(flowsTable,"id"),params.flow_id),eq14(this.getCol(flowsTable,"isDraft"),!1))).limit(1))[0];if(!flow)return{success:!1,message:"Published flow not found"};if((await this.db.select().from(instancesTable).where(and8(eq14(this.getCol(instancesTable,"entityName"),params.entity_name),eq14(this.getCol(instancesTable,"entityId"),params.entity_id),eq14(this.getCol(instancesTable,"status"),"active"))).limit(1)).length>0)return{success:!1,message:"An active verification instance already exists for this entity"};let steps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),params.flow_id)),edges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),params.flow_id)),stepNodes=steps.filter((s)=>s.nodeType==="step");if(stepNodes.sort((a,b)=>a.stepOrder-b.stepOrder),stepNodes.length===0)return{success:!1,message:"Flow has no step nodes"};let[instance]=await this.db.insert(instancesTable).values(toCamel2({flow_id:params.flow_id,entity_name:params.entity_name,entity_id:params.entity_id,started_by:params.started_by||null,status:"active",current_step_order:1,started_at:new Date})).returning(),instanceId=instance.id;if(await this.materializeRequirementsForStep(instanceId,params.flow_id,params.entity_name,params.entity_id,1,steps,edges,verifierConfigsTable,requirementsTable,userRolesTable),this.onNotificationTrigger){let firstStep=stepNodes[0],firstStepNodeId=firstStep?.nodeId,connectedNotifIds=firstStepNodeId?this.getConnectedNotificationNodeIds(firstStepNodeId,steps,edges):[],ctx={flow_name:flow.name,step_name:firstStep?.name||"Step 1",step_order:1,total_steps:stepNodes.length};if(connectedNotifIds.length>0)for(let notifNodeId of connectedNotifIds)await this.onNotificationTrigger({trigger:"on_flow_started",flow_id:params.flow_id,entity_name:params.entity_name,entity_id:params.entity_id,node_id:notifNodeId,context:ctx});else await this.onNotificationTrigger({trigger:"on_flow_started",flow_id:params.flow_id,entity_name:params.entity_name,entity_id:params.entity_id,context:ctx})}return this.logger.info(`[Verification] Flow started: instance ${instanceId} for ${params.entity_name}:${params.entity_id}`),{success:!0,instance_id:instanceId,message:"Flow started"}}async materializeRequirementsForStep(instanceId,flowId,entityName,entityId,stepOrder,steps,edges,verifierConfigsTable,requirementsTable,userRolesTable){if(!requirementsTable)return;let stepNode=steps.find((s)=>s.nodeType==="step"&&s.stepOrder===stepOrder);if(!stepNode)return;let stepNodeId=stepNode.nodeId,verifierNodeIds=edges.filter((e)=>e.targetNodeId===stepNodeId).map((e)=>e.sourceNodeId),verifierNodes=steps.filter((s)=>s.nodeType==="verifier"&&verifierNodeIds.includes(s.nodeId));if(verifierNodes.length===0)return;let verifierConfigs=[];if(verifierConfigsTable)verifierConfigs=await this.db.select().from(verifierConfigsTable).where(eq14(this.getCol(verifierConfigsTable,"flowId"),flowId));for(let verifierNode of verifierNodes){let nodeId=verifierNode.nodeId,config=verifierConfigs.find((vc)=>vc.nodeId===nodeId);if(!config)continue;let{verifierType,allMustApprove}=config;if(verifierType==="role"&&allMustApprove&&userRolesTable){let rolesTable=this.getTable("roles");if(rolesTable){let rolesCols=rolesTable,userRolesCols=userRolesTable,role=(await this.db.select().from(rolesTable).where(eq14(rolesCols.name,config.verifierRole)).limit(1))[0];if(role){let usersWithRole=await this.db.select({user_id:userRolesCols.userId}).from(userRolesTable).where(eq14(userRolesCols.roleId,role.id));for(let userRole of usersWithRole)await this.db.insert(requirementsTable).values(toCamel2({instance_id:instanceId,step_node_id:stepNodeId,verifier_node_id:nodeId,entity_name:entityName,entity_id:entityId,verifier_type:"user",verifier_user_id:userRole.user_id,verifier_role:config.verifierRole||null,require_signature:config.requireSignature,all_must_approve:!0,step_order:stepOrder,status:"pending"}))}}}else await this.db.insert(requirementsTable).values(toCamel2({instance_id:instanceId,step_node_id:stepNodeId,verifier_node_id:nodeId,entity_name:entityName,entity_id:entityId,verifier_type:verifierType,verifier_user_id:config.verifierUserId||null,verifier_role:config.verifierRole||null,require_signature:config.requireSignature,all_must_approve:allMustApprove,step_order:stepOrder,status:"pending"}))}if(this.onNotificationTrigger){let connectedNotifIds=this.getConnectedNotificationNodeIds(stepNodeId,steps,edges);if(this.logger.info(`[Verification] on_step_reached: stepNodeId=${stepNodeId}, connectedNotifIds=[${connectedNotifIds.join(", ")}]`),connectedNotifIds.length>0)for(let notifNodeId of connectedNotifIds)this.logger.info(`[Verification] Triggering on_step_reached for notifNodeId=${notifNodeId}`),await this.onNotificationTrigger({trigger:"on_step_reached",flow_id:flowId,entity_name:entityName,entity_id:entityId,node_id:notifNodeId,context:{step_name:stepNode.name||`Step ${stepOrder}`,step_order:stepOrder}});else await this.onNotificationTrigger({trigger:"on_step_reached",flow_id:flowId,entity_name:entityName,entity_id:entityId,context:{step_name:stepNode.name||`Step ${stepOrder}`,step_order:stepOrder}})}}async getStatus(entityName,entityId){let instancesTable=this.getTable("verificationInstances"),flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),verificationsTable=this.getTable("verifications"),requirementsTable=this.getTable("verificationRequirements"),emptyStatus={entity_name:entityName,entity_id:entityId,instance:null,flow:null,current_step:0,total_steps:0,is_completed:!1,is_rejected:!1,verifications:[],pending_requirements:[]};if(!instancesTable||!flowsTable||!verificationsTable||!requirementsTable)return this.logger.error("[Verification] Required tables not found"),emptyStatus;let instanceRow=(await this.db.select().from(instancesTable).where(and8(eq14(this.getCol(instancesTable,"entityName"),entityName),eq14(this.getCol(instancesTable,"entityId"),entityId))).orderBy(desc2(this.getCol(instancesTable,"createdAt"))).limit(1))[0];if(!instanceRow)return emptyStatus;let instance=fromCamel2(instanceRow),flows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),instance.flow_id)).limit(1),flow=flows[0]?fromCamel2(flows[0]):void 0,totalSteps=(stepsTable?await this.db.select().from(stepsTable).where(and8(eq14(this.getCol(stepsTable,"flowId"),instance.flow_id),eq14(this.getCol(stepsTable,"nodeType"),"step"))):[]).length,verifications=(await this.db.select().from(verificationsTable).where(eq14(this.getCol(verificationsTable,"instanceId"),instance.id)).orderBy(desc2(this.getCol(verificationsTable,"createdAt")))).map((r)=>fromCamel2(r)),pendingRequirements=(await this.db.select().from(requirementsTable).where(and8(eq14(this.getCol(requirementsTable,"instanceId"),instance.id),eq14(this.getCol(requirementsTable,"status"),"pending")))).map((r)=>fromCamel2(r));return{entity_name:entityName,entity_id:entityId,instance,flow:flow||null,current_step:instance.current_step_order,total_steps:totalSteps,is_completed:instance.status==="completed",is_rejected:instance.status==="rejected",verifications,pending_requirements:pendingRequirements}}async decide(params){let{entity_name,entity_id,user_id,decision,reason,signature_id,diff}=params,verificationsTable=this.getTable("verifications"),requirementsTable=this.getTable("verificationRequirements"),instancesTable=this.getTable("verificationInstances"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),userRolesTable=this.getTable("userRoles"),rolesTable=this.getTable("roles");if(!verificationsTable||!requirementsTable||!instancesTable)return{success:!1,message:"Verification tables not configured"};let status=await this.getStatus(entity_name,entity_id);if(!status.instance||status.instance.status!=="active")return{success:!1,message:"No active verification instance"};let currentPending=status.pending_requirements.filter((r)=>r.step_order===status.current_step);if(currentPending.length===0)return{success:!1,message:"No pending requirements for current step"};let matchedReq=null;for(let req of currentPending){if(req.verifier_type==="user"&&req.verifier_user_id===user_id){matchedReq=req;break}if(req.verifier_type==="role"&&req.verifier_role&&userRolesTable&&rolesTable){let userRolesCols=userRolesTable,rolesCols=rolesTable;if((await this.db.select({role_name:rolesCols.name}).from(userRolesTable).innerJoin(rolesTable,eq14(userRolesCols.roleId,rolesCols.id)).where(eq14(userRolesCols.userId,user_id))).some((ur)=>ur.role_name===req.verifier_role)){matchedReq=req;break}}}if(!matchedReq)return{success:!1,message:"User is not authorized to verify at this step"};if(matchedReq.require_signature){if(!signature_id)return{success:!1,message:"Signature is required for this verification step"};let filesTable=resolveSchemaTable(this.schemaTables,"files",this.logger);if(filesTable){if((await this.db.select().from(filesTable).where(and8(eq14(this.getCol(filesTable,"id"),signature_id),eq14(this.getCol(filesTable,"uploadedBy"),user_id))).limit(1)).length===0)return{success:!1,message:"Signature file not found or not owned by the verifier"}}}if((await this.db.update(requirementsTable).set({status:decision}).where(and8(eq14(this.getCol(requirementsTable,"id"),matchedReq.id),eq14(this.getCol(requirementsTable,"status"),"pending"))).returning()).length===0)return{success:!1,message:"This requirement has already been decided"};let[newVerification]=await this.db.insert(verificationsTable).values(toCamel2({instance_id:status.instance.id,requirement_id:matchedReq.id,verifier_id:user_id,signature_id:signature_id||null,entity_name,entity_id,step_order:status.current_step,decision,reason:reason||null,diff:diff||null})).returning();if(this.onNotificationTrigger&&status.instance){let stepName=`Step ${status.current_step}`,allSteps=[],allEdges=[];if(stepsTable){allSteps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),status.instance.flow_id));let stepRow=allSteps.find((s)=>s.nodeId===matchedReq.step_node_id);if(stepRow?.name)stepName=stepRow.name}if(edgesTable)allEdges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),status.instance.flow_id));let triggerName=decision==="approved"?"on_approved":"on_rejected",ctx={flow_name:status.flow?.name||"",step_name:stepName,step_order:status.current_step,total_steps:status.total_steps,decision},connectedNotifIds=this.getConnectedNotificationNodeIds(matchedReq.step_node_id,allSteps,allEdges);if(connectedNotifIds.length>0)for(let notifNodeId of connectedNotifIds)await this.onNotificationTrigger({trigger:triggerName,flow_id:status.instance.flow_id,entity_name,entity_id,node_id:notifNodeId,verifier_id:user_id,decision,context:ctx});else await this.onNotificationTrigger({trigger:triggerName,flow_id:status.instance.flow_id,entity_name,entity_id,verifier_id:user_id,decision,context:ctx})}if(decision==="rejected"){await this.db.update(instancesTable).set(toCamel2({status:"rejected",completed_at:new Date})).where(eq14(this.getCol(instancesTable,"id"),status.instance.id));let newInstanceId;if(this.config.autoResetOnRejection){this.logger.info(`[Verification] Flow rejected for ${entity_name}:${entity_id}, auto-restarting from step 1`);let restartResult=await this.startFlow({flow_id:status.instance.flow_id,entity_name,entity_id,started_by:status.instance.started_by});if(restartResult.success)newInstanceId=restartResult.instance_id}return{success:!0,message:this.config.autoResetOnRejection?"Verification rejected \u2014 flow restarted from step 1":"Verification rejected",verification:newVerification,flow_completed:!1,new_instance_id:newInstanceId}}let matchedReqId=matchedReq.id,remainingPending=currentPending.filter((r)=>r.id!==matchedReqId);if(remainingPending.length>0)return{success:!0,message:`Step ${status.current_step} partially approved, ${remainingPending.length} verifier(s) remaining`,verification:newVerification,flow_completed:!1};let nextStep=status.current_step+1;if(nextStep>status.total_steps){if(await this.db.update(instancesTable).set(toCamel2({status:"completed",completed_at:new Date})).where(eq14(this.getCol(instancesTable,"id"),status.instance.id)),this.onNotificationTrigger){let completedSteps=[],completedEdges=[];if(stepsTable)completedSteps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),status.instance.flow_id));if(edgesTable)completedEdges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),status.instance.flow_id));let lastStepNodeId=matchedReq.step_node_id,completedNotifIds=this.getConnectedNotificationNodeIds(lastStepNodeId,completedSteps,completedEdges),completedCtx={flow_name:status.flow?.name||"",step_order:status.current_step,total_steps:status.total_steps};if(completedNotifIds.length>0)for(let notifNodeId of completedNotifIds)await this.onNotificationTrigger({trigger:"on_flow_completed",flow_id:status.instance.flow_id,entity_name,entity_id,node_id:notifNodeId,context:completedCtx});else await this.onNotificationTrigger({trigger:"on_flow_completed",flow_id:status.instance.flow_id,entity_name,entity_id,context:completedCtx})}return{success:!0,message:"Verification flow completed",verification:newVerification,flow_completed:!0}}if(await this.db.update(instancesTable).set(toCamel2({current_step_order:nextStep})).where(eq14(this.getCol(instancesTable,"id"),status.instance.id)),stepsTable&&edgesTable){let steps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),status.instance.flow_id)),edges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),status.instance.flow_id));await this.materializeRequirementsForStep(status.instance.id,status.instance.flow_id,entity_name,entity_id,nextStep,steps,edges,verifierConfigsTable,requirementsTable,userRolesTable)}return{success:!0,message:`Step ${status.current_step} approved, moving to step ${nextStep}`,verification:newVerification,flow_completed:!1,next_step:nextStep}}async startFlowForEntity(params){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return{success:!1,message:"Flow table not configured"};let flow=(await this.db.select().from(flowsTable).where(and8(eq14(this.getCol(flowsTable,"entityName"),params.entity_name),eq14(this.getCol(flowsTable,"isDraft"),!1))).limit(1))[0];if(!flow)return{success:!1,message:`No published flow found for entity '${params.entity_name}'`};return this.startFlow({flow_id:flow.id,entity_name:params.entity_name,entity_id:params.entity_id,started_by:params.started_by})}async listEntityStatuses(params){let instancesTable=this.getTable("verificationInstances"),flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),emptyResult={items:[],total:0,page:1,limit:20};if(!instancesTable||!flowsTable)return emptyResult;let page=params.page||1,limit=params.limit||20,offset=(page-1)*limit,conditions=[eq14(this.getCol(instancesTable,"entityName"),params.entity_name)];if(params.status)conditions.push(eq14(this.getCol(instancesTable,"status"),params.status));let whereClause=conditions.length===1?conditions[0]:and8(...conditions),allInstances=(await this.db.select().from(instancesTable).where(whereClause).orderBy(desc2(this.getCol(instancesTable,"createdAt")))).map((r)=>fromCamel2(r)),total=allInstances.length,paged=allInstances.slice(offset,offset+limit),items=[];for(let inst of paged){let flowId=inst.flow_id,flows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),flowId)).limit(1),flow=flows[0]?fromCamel2(flows[0]):void 0,totalSteps=0;if(stepsTable)totalSteps=(await this.db.select().from(stepsTable).where(and8(eq14(this.getCol(stepsTable,"flowId"),flowId),eq14(this.getCol(stepsTable,"nodeType"),"step")))).length;items.push({instance_id:inst.id,entity_name:inst.entity_name,entity_id:inst.entity_id,flow_id:flowId,flow_name:flow?.name||"Unknown",status:inst.status,current_step_order:inst.current_step_order,total_steps:totalSteps,started_by:inst.started_by,started_at:inst.started_at,completed_at:inst.completed_at})}return{items,total,page,limit}}async getPending(userId){let requirementsTable=this.getTable("verificationRequirements"),instancesTable=this.getTable("verificationInstances"),flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),userRolesTable=this.getTable("userRoles"),rolesTable=this.getTable("roles");if(this.logger.info(`[Verification.getPending] userId=${userId}, tables: req=${!!requirementsTable} inst=${!!instancesTable} flow=${!!flowsTable} steps=${!!stepsTable} roles=${!!rolesTable} userRoles=${!!userRolesTable}`),!requirementsTable||!instancesTable||!flowsTable)return this.logger.warn("[Verification.getPending] Missing required tables, returning empty"),[];let userRolesCols=userRolesTable,rolesCols=rolesTable,userRoleNames=(userRolesTable&&rolesTable?await this.db.select({role_name:rolesCols.name}).from(userRolesTable).innerJoin(rolesTable,eq14(userRolesCols.roleId,rolesCols.id)).where(eq14(userRolesCols.userId,userId)):[]).map((ur)=>ur.role_name),pendingReqs=(await this.db.select().from(requirementsTable).where(eq14(this.getCol(requirementsTable,"status"),"pending"))).map((r)=>fromCamel2(r));this.logger.info(`[Verification.getPending] Found ${pendingReqs.length} pending requirements, userRoles=${JSON.stringify(userRoleNames)}`);for(let req of pendingReqs)this.logger.info(`[Verification.getPending] Req: verifier_type=${req.verifier_type} verifier_user_id=${req.verifier_user_id} verifier_role=${req.verifier_role} step_order=${req.step_order} entity=${req.entity_name}/${req.entity_id}`);let pendingItems=[];for(let req of pendingReqs){if(!(req.verifier_type==="user"&&req.verifier_user_id===userId||req.verifier_type==="role"&&userRoleNames.includes(req.verifier_role))){this.logger.info(`[Verification.getPending] Skipping req: canVerify=false (type=${req.verifier_type}, reqUserId=${req.verifier_user_id}, loggedInUserId=${userId})`);continue}let instances=await this.db.select().from(instancesTable).where(and8(eq14(this.getCol(instancesTable,"id"),req.instance_id),eq14(this.getCol(instancesTable,"status"),"active"))).limit(1),instance=instances[0]?fromCamel2(instances[0]):void 0;if(!instance)continue;if(instance.current_step_order!==req.step_order)continue;let flows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),instance.flow_id)).limit(1),flow=flows[0]?fromCamel2(flows[0]):void 0;if(!flow)continue;let stepName;if(stepsTable&&req.step_node_id){let stepRow=(await this.db.select().from(stepsTable).where(and8(eq14(this.getCol(stepsTable,"flowId"),instance.flow_id),eq14(this.getCol(stepsTable,"nodeId"),req.step_node_id))).limit(1))[0];if(stepRow?.name)stepName=stepRow.name}pendingItems.push({instance_id:instance.id,entity_name:req.entity_name,entity_id:req.entity_id,flow_name:flow.name,step_order:req.step_order,step_name:stepName,require_signature:req.require_signature,created_at:req.created_at})}return pendingItems}}var init_Verification=__esm(()=>{init_types5()});var genLookup=(target)=>{let lookupTemp=typeof Uint8Array>"u"?[]:new Uint8Array(256),len=64;for(let i=0;i<64;i++)lookupTemp[target.charCodeAt(i)]=i;return lookupTemp},lookup,lookupUrl,base64UrlPattern,base64Pattern,base64,base64_default;var init_base64=__esm(()=>{lookup=genLookup("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"),lookupUrl=genLookup("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"),base64UrlPattern=/^[-A-Za-z0-9\-_]*$/,base64Pattern=/^[-A-Za-z0-9+/]*={0,3}$/,base64={};base64.toArrayBuffer=(data,urlMode)=>{let len=data.length,bufferLength=data.length*0.75,i,p=0,encoded1,encoded2,encoded3,encoded4;if(data[data.length-1]==="="){if(bufferLength--,data[data.length-2]==="=")bufferLength--}let arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer),target=urlMode?lookupUrl:lookup;for(i=0;i<len;i+=4)encoded1=target[data.charCodeAt(i)],encoded2=target[data.charCodeAt(i+1)],encoded3=target[data.charCodeAt(i+2)],encoded4=target[data.charCodeAt(i+3)],bytes[p++]=encoded1<<2|encoded2>>4,bytes[p++]=(encoded2&15)<<4|encoded3>>2,bytes[p++]=(encoded3&3)<<6|encoded4&63;return arraybuffer};base64.fromArrayBuffer=(arrBuf,urlMode)=>{let bytes=new Uint8Array(arrBuf),i,result="",len=bytes.length,target=urlMode?"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(i=0;i<len;i+=3)result+=target[bytes[i]>>2],result+=target[(bytes[i]&3)<<4|bytes[i+1]>>4],result+=target[(bytes[i+1]&15)<<2|bytes[i+2]>>6],result+=target[bytes[i+2]&63];let remainder=len%3;if(remainder===2)result=result.substring(0,result.length-1)+(urlMode?"":"=");else if(remainder===1)result=result.substring(0,result.length-2)+(urlMode?"":"==");return result};base64.toString=(str3,urlMode)=>{return new TextDecoder().decode(base64.toArrayBuffer(str3,urlMode))};base64.fromString=(str3,urlMode)=>{return base64.fromArrayBuffer(new TextEncoder().encode(str3),urlMode)};base64.validate=(encoded,urlMode)=>{if(!(typeof encoded==="string"||encoded instanceof String))return!1;try{return urlMode?base64UrlPattern.test(encoded):base64Pattern.test(encoded)}catch(_e){return!1}};base64.base64=base64;base64_default=base64});var exports_isoBase64URL={};__export(exports_isoBase64URL,{trimPadding:()=>trimPadding,toUTF8String:()=>toUTF8String,toBuffer:()=>toBuffer,toBase64:()=>toBase64,isBase64URL:()=>isBase64URL,isBase64:()=>isBase64,fromUTF8String:()=>fromUTF8String,fromBuffer:()=>fromBuffer});function toBuffer(base64urlString,from="base64url"){let _buffer=base64_default.toArrayBuffer(base64urlString,from==="base64url");return new Uint8Array(_buffer)}function fromBuffer(buffer,to="base64url"){let _normalized=new Uint8Array(buffer);return base64_default.fromArrayBuffer(_normalized.buffer,to==="base64url")}function toBase64(base64urlString){let fromBase64Url=base64_default.toArrayBuffer(base64urlString,!0);return base64_default.fromArrayBuffer(fromBase64Url)}function fromUTF8String(utf8String){return base64_default.fromString(utf8String,!0)}function toUTF8String(base64urlString){return base64_default.toString(base64urlString,!0)}function isBase64(input){return base64_default.validate(input,!1)}function isBase64URL(input){return input=trimPadding(input),base64_default.validate(input,!0)}function trimPadding(input){return input.replace(/=/g,"")}var init_isoBase64URL=__esm(()=>{init_base64()});function decodeLength(data,argument,index){if(argument<24)return[argument,1];let remainingDataLength=data.byteLength-index-1,view=new DataView(data.buffer,index+1),output,bytes=0;switch(argument){case 24:{if(remainingDataLength>0)output=view.getUint8(0),bytes=2;break}case 25:{if(remainingDataLength>1)output=view.getUint16(0,!1),bytes=3;break}case 26:{if(remainingDataLength>3)output=view.getUint32(0,!1),bytes=5;break}case 27:{if(remainingDataLength>7){let bigOutput=view.getBigUint64(0,!1);if(bigOutput>=24n&&bigOutput<=Number.MAX_SAFE_INTEGER)return[Number(bigOutput),9]}break}}if(output&&output>=24)return[output,bytes];throw Error("Length not supported or not well formed")}function encodeLength(major,argument){let majorEncoded=major<<5;if(argument<0)throw Error("CBOR Data Item argument must not be negative");let bigintArgument;if(typeof argument=="number"){if(!Number.isInteger(argument))throw Error("CBOR Data Item argument must be an integer");bigintArgument=BigInt(argument)}else bigintArgument=argument;if(major==MAJOR_TYPE_NEGATIVE_INTEGER){if(bigintArgument==0n)throw Error("CBOR Data Item argument cannot be zero when negative");bigintArgument=bigintArgument-1n}if(bigintArgument>18446744073709551615n)throw Error("CBOR number out of range");let buffer=new Uint8Array(8);if(new DataView(buffer.buffer).setBigUint64(0,bigintArgument,!1),bigintArgument<=23)return[majorEncoded|buffer[7]];else if(bigintArgument<=255)return[majorEncoded|24,buffer[7]];else if(bigintArgument<=65535)return[majorEncoded|25,...buffer.slice(6)];else if(bigintArgument<=4294967295)return[majorEncoded|26,...buffer.slice(4)];else return[majorEncoded|27,...buffer]}var MAJOR_TYPE_UNSIGNED_INTEGER=0,MAJOR_TYPE_NEGATIVE_INTEGER=1,MAJOR_TYPE_BYTE_STRING=2,MAJOR_TYPE_TEXT_STRING=3,MAJOR_TYPE_ARRAY=4,MAJOR_TYPE_MAP=5,MAJOR_TYPE_TAG=6,MAJOR_TYPE_SIMPLE_OR_FLOAT=7;class CBORTag{constructor(tag,value){Object.defineProperty(this,"tagId",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"tagValue",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.tagId=tag,this.tagValue=value}get tag(){return this.tagId}get value(){return this.tagValue}}function decodeUnsignedInteger(data,argument,index){return decodeLength(data,argument,index)}function decodeNegativeInteger(data,argument,index){let[value,length]=decodeUnsignedInteger(data,argument,index);return[-value-1,length]}function decodeByteString(data,argument,index){let[lengthValue,lengthConsumed]=decodeLength(data,argument,index),dataStartIndex=index+lengthConsumed;return[new Uint8Array(data.buffer.slice(dataStartIndex,dataStartIndex+lengthValue)),lengthConsumed+lengthValue]}function decodeString(data,argument,index){let[value,length]=decodeByteString(data,argument,index);return[TEXT_DECODER.decode(value),length]}function decodeArray(data,argument,index){if(argument===0)return[[],1];let[length,lengthConsumed]=decodeLength(data,argument,index),consumedLength=lengthConsumed,value=[];for(let i=0;i<length;i++){if(data.byteLength-index-consumedLength<=0)throw Error("array is not supported or well formed");let[decodedValue,consumed]=decodeNext(data,index+consumedLength);value.push(decodedValue),consumedLength+=consumed}return[value,consumedLength]}function decodeMap(data,argument,index){if(argument===0)return[new Map,1];let[length,lengthConsumed]=decodeLength(data,argument,index),consumedLength=lengthConsumed,result=new Map;for(let i=0;i<length;i++){let remainingDataLength=data.byteLength-index-consumedLength;if(remainingDataLength<=0)throw Error(MAP_ERROR);let[key2,keyConsumed]=decodeNext(data,index+consumedLength);if(consumedLength+=keyConsumed,remainingDataLength-=keyConsumed,remainingDataLength<=0)throw Error(MAP_ERROR);if(typeof key2!=="string"&&typeof key2!=="number")throw Error(MAP_ERROR);if(result.has(key2))throw Error(MAP_ERROR);let[value,valueConsumed]=decodeNext(data,index+consumedLength);consumedLength+=valueConsumed,result.set(key2,value)}return[result,consumedLength]}function decodeFloat16(data,index){if(index+3>data.byteLength)throw Error("CBOR stream ended before end of Float 16");let result=data.getUint16(index+1,!1);if(result==31744)return[1/0,3];else if(result==32256)return[NaN,3];else if(result==64512)return[-1/0,3];throw Error("Float16 data is unsupported")}function decodeFloat32(data,index){if(index+5>data.byteLength)throw Error("CBOR stream ended before end of Float 32");return[data.getFloat32(index+1,!1),5]}function decodeFloat64(data,index){if(index+9>data.byteLength)throw Error("CBOR stream ended before end of Float 64");return[data.getFloat64(index+1,!1),9]}function decodeTag(data,argument,index){let[tag,tagBytes]=decodeLength(data,argument,index),[value,valueBytes]=decodeNext(data,index+tagBytes);return[new CBORTag(tag,value),tagBytes+valueBytes]}function decodeNext(data,index){if(index>=data.byteLength)throw Error("CBOR stream ended before tag value");let byte=data.getUint8(index),majorType=byte>>5,argument=byte&31;switch(majorType){case MAJOR_TYPE_UNSIGNED_INTEGER:return decodeUnsignedInteger(data,argument,index);case MAJOR_TYPE_NEGATIVE_INTEGER:return decodeNegativeInteger(data,argument,index);case MAJOR_TYPE_BYTE_STRING:return decodeByteString(data,argument,index);case MAJOR_TYPE_TEXT_STRING:return decodeString(data,argument,index);case MAJOR_TYPE_ARRAY:return decodeArray(data,argument,index);case MAJOR_TYPE_MAP:return decodeMap(data,argument,index);case MAJOR_TYPE_TAG:return decodeTag(data,argument,index);case MAJOR_TYPE_SIMPLE_OR_FLOAT:switch(argument){case 20:return[!1,1];case 21:return[!0,1];case 22:return[null,1];case 23:return[void 0,1];case 25:return decodeFloat16(data,index);case 26:return decodeFloat32(data,index);case 27:return decodeFloat64(data,index)}}throw Error(`Unsupported or not well formed at ${index}`)}function encodeSimple(data){if(data===!0)return 245;else if(data===!1)return 244;else if(data===null)return 246;return 247}function encodeFloat(data){if(Math.fround(data)==data||!Number.isFinite(data)||Number.isNaN(data)){let output=new Uint8Array(5);return output[0]=250,new DataView(output.buffer).setFloat32(1,data,!1),output}else{let output=new Uint8Array(9);return output[0]=251,new DataView(output.buffer).setFloat64(1,data,!1),output}}function encodeNumber(data){if(typeof data=="number"){if(Number.isSafeInteger(data))if(data<0)return encodeLength(MAJOR_TYPE_NEGATIVE_INTEGER,Math.abs(data));else return encodeLength(MAJOR_TYPE_UNSIGNED_INTEGER,data);return[encodeFloat(data)]}else if(data<0n)return encodeLength(MAJOR_TYPE_NEGATIVE_INTEGER,data*-1n);else return encodeLength(MAJOR_TYPE_UNSIGNED_INTEGER,data)}function encodeString(data,output){output.push(...encodeLength(MAJOR_TYPE_TEXT_STRING,data.length)),output.push(ENCODER.encode(data))}function encodeBytes(data,output){output.push(...encodeLength(MAJOR_TYPE_BYTE_STRING,data.length)),output.push(data)}function encodeArray(data,output){output.push(...encodeLength(MAJOR_TYPE_ARRAY,data.length));for(let element of data)encodePartialCBOR(element,output)}function encodeMap(data,output){output.push(new Uint8Array(encodeLength(MAJOR_TYPE_MAP,data.size)));for(let[key2,value]of data.entries())encodePartialCBOR(key2,output),encodePartialCBOR(value,output)}function encodeTag(tag,output){output.push(...encodeLength(MAJOR_TYPE_TAG,tag.tag)),encodePartialCBOR(tag.value,output)}function encodePartialCBOR(data,output){if(typeof data=="boolean"||data===null||data==null){output.push(encodeSimple(data));return}if(typeof data=="number"||typeof data=="bigint"){output.push(...encodeNumber(data));return}if(typeof data=="string"){encodeString(data,output);return}if(data instanceof Uint8Array){encodeBytes(data,output);return}if(Array.isArray(data)){encodeArray(data,output);return}if(data instanceof Map){encodeMap(data,output);return}if(data instanceof CBORTag){encodeTag(data,output);return}throw Error("Not implemented")}function decodePartialCBOR(data,index){if(data.byteLength===0||data.byteLength<=index||index<0)throw Error("No data");if(data instanceof Uint8Array)return decodeNext(new DataView(data.buffer),index);else if(data instanceof ArrayBuffer)return decodeNext(new DataView(data),index);return decodeNext(data,index)}function encodeCBOR(data){let results=[];encodePartialCBOR(data,results);let length=0;for(let result of results)if(typeof result=="number")length+=1;else length+=result.length;let output=new Uint8Array(length),index=0;for(let result of results)if(typeof result=="number")output[index]=result,index+=1;else output.set(result,index),index+=result.length;return output}var TEXT_DECODER,MAP_ERROR="Map is not supported or well formed",ENCODER;var init_cbor=__esm(()=>{TEXT_DECODER=new TextDecoder;ENCODER=new TextEncoder});var init_esm=__esm(()=>{init_cbor()});var exports_isoCBOR={};__export(exports_isoCBOR,{encode:()=>encode,decodeFirst:()=>decodeFirst});function decodeFirst(input){let _input=new Uint8Array(input),decoded=decodePartialCBOR(_input,0),[first]=decoded;return first}function encode(input){return encodeCBOR(input)}var init_isoCBOR=__esm(()=>{init_esm()});function isCOSEPublicKeyOKP(cosePublicKey){let kty=cosePublicKey.get(COSEKEYS.kty);return isCOSEKty(kty)&&kty===COSEKTY.OKP}function isCOSEPublicKeyEC2(cosePublicKey){let kty=cosePublicKey.get(COSEKEYS.kty);return isCOSEKty(kty)&&kty===COSEKTY.EC2}function isCOSEPublicKeyRSA(cosePublicKey){let kty=cosePublicKey.get(COSEKEYS.kty);return isCOSEKty(kty)&&kty===COSEKTY.RSA}function isCOSEKty(kty){return Object.values(COSEKTY).indexOf(kty)>=0}function isCOSECrv(crv){return Object.values(COSECRV).indexOf(crv)>=0}function isCOSEAlg(alg){return Object.values(COSEALG).indexOf(alg)>=0}var COSEKEYS,COSEKTY,COSECRV,COSEALG;var init_cose=__esm(()=>{(function(COSEKEYS2){COSEKEYS2[COSEKEYS2.kty=1]="kty",COSEKEYS2[COSEKEYS2.alg=3]="alg",COSEKEYS2[COSEKEYS2.crv=-1]="crv",COSEKEYS2[COSEKEYS2.x=-2]="x",COSEKEYS2[COSEKEYS2.y=-3]="y",COSEKEYS2[COSEKEYS2.n=-1]="n",COSEKEYS2[COSEKEYS2.e=-2]="e"})(COSEKEYS||(COSEKEYS={}));(function(COSEKTY2){COSEKTY2[COSEKTY2.OKP=1]="OKP",COSEKTY2[COSEKTY2.EC2=2]="EC2",COSEKTY2[COSEKTY2.RSA=3]="RSA"})(COSEKTY||(COSEKTY={}));(function(COSECRV2){COSECRV2[COSECRV2.P256=1]="P256",COSECRV2[COSECRV2.P384=2]="P384",COSECRV2[COSECRV2.P521=3]="P521",COSECRV2[COSECRV2.ED25519=6]="ED25519",COSECRV2[COSECRV2.SECP256K1=8]="SECP256K1"})(COSECRV||(COSECRV={}));(function(COSEALG2){COSEALG2[COSEALG2.ES256=-7]="ES256",COSEALG2[COSEALG2.EdDSA=-8]="EdDSA",COSEALG2[COSEALG2.ES384=-35]="ES384",COSEALG2[COSEALG2.ES512=-36]="ES512",COSEALG2[COSEALG2.PS256=-37]="PS256",COSEALG2[COSEALG2.PS384=-38]="PS384",COSEALG2[COSEALG2.PS512=-39]="PS512",COSEALG2[COSEALG2.ES256K=-47]="ES256K",COSEALG2[COSEALG2.RS256=-257]="RS256",COSEALG2[COSEALG2.RS384=-258]="RS384",COSEALG2[COSEALG2.RS512=-259]="RS512",COSEALG2[COSEALG2.RS1=-65535]="RS1"})(COSEALG||(COSEALG={}))});function mapCoseAlgToWebCryptoAlg(alg){if([COSEALG.RS1].indexOf(alg)>=0)return"SHA-1";else if([COSEALG.ES256,COSEALG.PS256,COSEALG.RS256].indexOf(alg)>=0)return"SHA-256";else if([COSEALG.ES384,COSEALG.PS384,COSEALG.RS384].indexOf(alg)>=0)return"SHA-384";else if([COSEALG.ES512,COSEALG.PS512,COSEALG.RS512,COSEALG.EdDSA].indexOf(alg)>=0)return"SHA-512";throw Error(`Could not map COSE alg value of ${alg} to a WebCrypto alg`)}var init_mapCoseAlgToWebCryptoAlg=__esm(()=>{init_cose()});function getWebCrypto(){return new Promise((resolve2,reject)=>{if(webCrypto)return resolve2(webCrypto);let _globalThisCrypto=_getWebCryptoInternals.stubThisGlobalThisCrypto();if(_globalThisCrypto)return webCrypto=_globalThisCrypto,resolve2(webCrypto);return reject(new MissingWebCrypto)})}var webCrypto=void 0,MissingWebCrypto,_getWebCryptoInternals;var init_getWebCrypto=__esm(()=>{MissingWebCrypto=class MissingWebCrypto extends Error{constructor(){super("An instance of the Crypto API could not be located");this.name="MissingWebCrypto"}};_getWebCryptoInternals={stubThisGlobalThisCrypto:()=>globalThis.crypto,setCachedCrypto:(newCrypto)=>{webCrypto=newCrypto}}});async function digest(data,algorithm){let WebCrypto=await getWebCrypto(),subtleAlgorithm=mapCoseAlgToWebCryptoAlg(algorithm),hashed=await WebCrypto.subtle.digest(subtleAlgorithm,data);return new Uint8Array(hashed)}var init_digest=__esm(()=>{init_mapCoseAlgToWebCryptoAlg();init_getWebCrypto()});async function getRandomValues(array){return(await getWebCrypto()).getRandomValues(array),array}var init_getRandomValues=__esm(()=>{init_getWebCrypto()});async function importKey(opts){let WebCrypto=await getWebCrypto(),{keyData,algorithm}=opts;return WebCrypto.subtle.importKey("jwk",keyData,algorithm,!1,["verify"])}var init_importKey=__esm(()=>{init_getWebCrypto()});async function verifyEC2(opts){let{cosePublicKey,signature,data,shaHashOverride}=opts,WebCrypto=await getWebCrypto(),alg=cosePublicKey.get(COSEKEYS.alg),crv=cosePublicKey.get(COSEKEYS.crv),x=cosePublicKey.get(COSEKEYS.x),y=cosePublicKey.get(COSEKEYS.y);if(!alg)throw Error("Public key was missing alg (EC2)");if(!crv)throw Error("Public key was missing crv (EC2)");if(!x)throw Error("Public key was missing x (EC2)");if(!y)throw Error("Public key was missing y (EC2)");let _crv;if(crv===COSECRV.P256)_crv="P-256";else if(crv===COSECRV.P384)_crv="P-384";else if(crv===COSECRV.P521)_crv="P-521";else throw Error(`Unexpected COSE crv value of ${crv} (EC2)`);let keyData={kty:"EC",crv:_crv,x:exports_isoBase64URL.fromBuffer(x),y:exports_isoBase64URL.fromBuffer(y),ext:!1},key2=await importKey({keyData,algorithm:{name:"ECDSA",namedCurve:_crv}}),subtleAlg=mapCoseAlgToWebCryptoAlg(alg);if(shaHashOverride)subtleAlg=mapCoseAlgToWebCryptoAlg(shaHashOverride);let verifyAlgorithm={name:"ECDSA",hash:{name:subtleAlg}};return WebCrypto.subtle.verify(verifyAlgorithm,key2,signature,data)}var init_verifyEC2=__esm(()=>{init_cose();init_mapCoseAlgToWebCryptoAlg();init_importKey();init_iso();init_getWebCrypto()});function mapCoseAlgToWebCryptoKeyAlgName(alg){if([COSEALG.EdDSA].indexOf(alg)>=0)return"Ed25519";else if([COSEALG.ES256,COSEALG.ES384,COSEALG.ES512,COSEALG.ES256K].indexOf(alg)>=0)return"ECDSA";else if([COSEALG.RS256,COSEALG.RS384,COSEALG.RS512,COSEALG.RS1].indexOf(alg)>=0)return"RSASSA-PKCS1-v1_5";else if([COSEALG.PS256,COSEALG.PS384,COSEALG.PS512].indexOf(alg)>=0)return"RSA-PSS";throw Error(`Could not map COSE alg value of ${alg} to a WebCrypto key alg name`)}var init_mapCoseAlgToWebCryptoKeyAlgName=__esm(()=>{init_cose()});async function verifyRSA(opts){let{cosePublicKey,signature,data,shaHashOverride}=opts,WebCrypto=await getWebCrypto(),alg=cosePublicKey.get(COSEKEYS.alg),n=cosePublicKey.get(COSEKEYS.n),e=cosePublicKey.get(COSEKEYS.e);if(!alg)throw Error("Public key was missing alg (RSA)");if(!isCOSEAlg(alg))throw Error(`Public key had invalid alg ${alg} (RSA)`);if(!n)throw Error("Public key was missing n (RSA)");if(!e)throw Error("Public key was missing e (RSA)");let keyData={kty:"RSA",alg:"",n:exports_isoBase64URL.fromBuffer(n),e:exports_isoBase64URL.fromBuffer(e),ext:!1},keyAlgorithm={name:mapCoseAlgToWebCryptoKeyAlgName(alg),hash:{name:mapCoseAlgToWebCryptoAlg(alg)}},verifyAlgorithm={name:mapCoseAlgToWebCryptoKeyAlgName(alg)};if(shaHashOverride)keyAlgorithm.hash.name=mapCoseAlgToWebCryptoAlg(shaHashOverride);if(keyAlgorithm.name==="RSASSA-PKCS1-v1_5"){if(keyAlgorithm.hash.name==="SHA-256")keyData.alg="RS256";else if(keyAlgorithm.hash.name==="SHA-384")keyData.alg="RS384";else if(keyAlgorithm.hash.name==="SHA-512")keyData.alg="RS512";else if(keyAlgorithm.hash.name==="SHA-1")keyData.alg="RS1"}else if(keyAlgorithm.name==="RSA-PSS"){let saltLength=0;if(keyAlgorithm.hash.name==="SHA-256")keyData.alg="PS256",saltLength=32;else if(keyAlgorithm.hash.name==="SHA-384")keyData.alg="PS384",saltLength=48;else if(keyAlgorithm.hash.name==="SHA-512")keyData.alg="PS512",saltLength=64;verifyAlgorithm.saltLength=saltLength}else throw Error(`Unexpected RSA key algorithm ${alg} (${keyAlgorithm.name})`);let key2=await importKey({keyData,algorithm:keyAlgorithm});return WebCrypto.subtle.verify(verifyAlgorithm,key2,signature,data)}var init_verifyRSA=__esm(()=>{init_cose();init_mapCoseAlgToWebCryptoAlg();init_importKey();init_iso();init_mapCoseAlgToWebCryptoKeyAlgName();init_getWebCrypto()});function convertAAGUIDToString(aaguid){let hex=exports_isoUint8Array.toHex(aaguid);return[hex.slice(0,8),hex.slice(8,12),hex.slice(12,16),hex.slice(16,20),hex.slice(20,32)].join("-")}var init_convertAAGUIDToString=__esm(()=>{init_iso()});function convertCertBufferToPEM(certBuffer){let b64cert;if(typeof certBuffer==="string")if(exports_isoBase64URL.isBase64URL(certBuffer))b64cert=exports_isoBase64URL.toBase64(certBuffer);else if(exports_isoBase64URL.isBase64(certBuffer))b64cert=certBuffer;else throw Error("Certificate is not a valid base64 or base64url string");else b64cert=exports_isoBase64URL.fromBuffer(certBuffer,"base64");let PEMKey="";for(let i=0;i<Math.ceil(b64cert.length/64);i+=1){let start=64*i;PEMKey+=`${b64cert.substr(start,64)}
101
+ `,RateLimiterBackendError,DEFAULT_AUTH_LOGIN,DEFAULT_AUTH_REGISTER,DEFAULT_AUTH_PASSWORD_RESET,DEFAULT_AUTH_MAGIC_LINK,DEFAULT_CONFIG4;var init_RateLimiter=__esm(()=>{RateLimiterBackendError=class RateLimiterBackendError extends Error{constructor(message){super(message);this.name="RateLimiterBackendError"}};DEFAULT_AUTH_LOGIN={window:"15m",max:5,blockDuration:"30m"},DEFAULT_AUTH_REGISTER={window:"1h",max:3,blockDuration:"1h"},DEFAULT_AUTH_PASSWORD_RESET={window:"1h",max:3,blockDuration:"1h"},DEFAULT_AUTH_MAGIC_LINK={window:"1h",max:5,blockDuration:"1h"},DEFAULT_CONFIG4={enabled:!0,strategy:"sliding-window",keyPrefix:"rl:",authRoutes:{window:"1m",max:10,login:DEFAULT_AUTH_LOGIN,register:DEFAULT_AUTH_REGISTER,passwordReset:DEFAULT_AUTH_PASSWORD_RESET,magicLink:DEFAULT_AUTH_MAGIC_LINK},publicRoutes:{window:"1m",max:100},privateRoutes:{window:"1m",max:60},byIp:!0,byUserId:!0,byEndpoint:!1,skipSuccessfulRequests:!1,headers:{remaining:"X-RateLimit-Remaining",reset:"X-RateLimit-Reset",limit:"X-RateLimit-Limit"},whitelist:[],blacklist:[]}});import{createCipheriv as createCipheriv2,createDecipheriv as createDecipheriv2,randomBytes as randomBytes4,scryptSync as scryptSync2,timingSafeEqual as timingSafeEqual2}from"crypto";var VERSION="v1",IV_LEN2=12,KEY_LEN=32,SCRYPT_SALT="nucleus-secrets-v1",keyCache,deriveKey2=(masterKey)=>{let cached=keyCache.get(masterKey);if(cached)return cached;let key2=scryptSync2(masterKey,SCRYPT_SALT,KEY_LEN);return keyCache.set(masterKey,key2),key2},encryptSecret=(plaintext,masterKey)=>{if(!masterKey)throw Error("secrets encryption key is empty");let iv=randomBytes4(IV_LEN2),cipher=createCipheriv2("aes-256-gcm",deriveKey2(masterKey),iv),ciphertext=Buffer.concat([cipher.update(plaintext,"utf-8"),cipher.final()]),tag=cipher.getAuthTag();return[VERSION,iv.toString("base64"),tag.toString("base64"),ciphertext.toString("base64")].join(":")},decryptSecret=(envelope,masterKey)=>{if(!masterKey)throw Error("secrets encryption key is empty");let parts=envelope.split(":");if(parts.length!==4||parts[0]!==VERSION)throw Error("not an encrypted secret envelope");let[,ivB64,tagB64,ctB64]=parts,decipher=createDecipheriv2("aes-256-gcm",deriveKey2(masterKey),Buffer.from(ivB64,"base64"));return decipher.setAuthTag(Buffer.from(tagB64,"base64")),Buffer.concat([decipher.update(Buffer.from(ctB64,"base64")),decipher.final()]).toString("utf-8")},previewOf=(plaintext)=>{let len=plaintext.length;if(len===0)return"";if(len<12)return`\u2022\u2022\u2022\u2022${len}`;return`\u2022\u2022\u2022\u2022${plaintext.slice(-4)}`};var init_crypto=__esm(()=>{keyCache=new Map});function discoverSlots(config){let slots=new Map,add=(slot)=>{let id=`${slot.scope}.${slot.key}`;if(!slots.has(id))slots.set(id,slot)},valueAt=(scope,key2)=>{let node=config;for(let segment of scope.split(".")){if(!isPlainObject(node))return;node=node[segment]}if(!isPlainObject(node))return;let value=node[key2];if(typeof value==="string")return value;if(Array.isArray(value)&&value.every((entry)=>typeof entry==="string"))return value.length>0?value.join(","):void 0;return};for(let descriptor of SECRET_SLOT_DESCRIPTORS)for(let scope of expandPath(config,descriptor.path,descriptor.always===!0)){let leaf=scope.split(".").pop()??descriptor.group,group=descriptor.path.endsWith("*")?`${descriptor.group}:${leaf}`:descriptor.group;for(let declared of descriptor.keys)add({scope,key:declared.key,kind:declared.kind??kindFor(declared.key),label:declared.label,group,required:declared.required??!1,requiredWhen:declared.requiredWhen,hint:declared.hint,secret:declared.secret??descriptor.secret??!0,configValue:valueAt(scope,declared.key)})}let walk=(node,path2,depth)=>{if(depth>6)return;for(let[key2,value]of Object.entries(node)){if(SKIP_SECTIONS.has(key2))continue;let childPath=path2?`${path2}.${key2}`:key2;if(isPlainObject(value)){walk(value,childPath,depth+1);continue}if(typeof value!=="string"&&value!==void 0&&value!==null)continue;if(!CREDENTIAL_KEY_PATTERN.test(key2))continue;if(NOT_A_CREDENTIAL.has(key2.toLowerCase()))continue;if(BOOT_ONLY_PATHS.has(`${path2}.${key2}`))continue;let owner=path2.split(".").pop(),label=owner&&owner.toLowerCase()!==key2.toLowerCase()?`${humanize(owner)} \u2014 ${humanize(key2)}`:humanize(key2);add({scope:path2,key:key2,kind:kindFor(key2),label,group:path2.split(".")[0]??"other",required:!1,secret:!0,configValue:typeof value==="string"?value:void 0})}};return walk(config,"",0),[...slots.values()].sort((a,b)=>a.group.localeCompare(b.group)||a.scope.localeCompare(b.scope)||a.key.localeCompare(b.key))}var CREDENTIAL_KEY_PATTERN,NOT_A_CREDENTIAL,BOOT_ONLY_PATHS,SKIP_SECTIONS,kindFor=(key2)=>{let k=key2.toLowerCase();if(k.includes("json"))return"json";if(k.includes("connection"))return"connection_string";if(k.includes("privatekey")||k.includes("private_key"))return"pem";if(k.includes("clientid")||k.includes("client_id"))return"text";return"password"},humanize=(key2)=>key2.replace(/[_-]+/g," ").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/\s+/g," ").trim().replace(/\b\w/g,(c)=>c.toUpperCase()),SECRET_SLOT_DESCRIPTORS,isPlainObject=(value)=>typeof value==="object"&&value!==null&&!Array.isArray(value),expandPath=(config,path2,always=!1)=>{let segments=path2.split("."),scopes2=[{path:"",node:config}];for(let segment of segments){let next=[];for(let current of scopes2){if(!isPlainObject(current.node))continue;if(segment==="*")for(let[childKey,childValue]of Object.entries(current.node))next.push({path:current.path?`${current.path}.${childKey}`:childKey,node:childValue});else if(segment in current.node)next.push({path:current.path?`${current.path}.${segment}`:segment,node:current.node[segment]});else if(always)next.push({path:current.path?`${current.path}.${segment}`:segment,node:{}})}scopes2=next}return scopes2.filter((s)=>isPlainObject(s.node)).map((s)=>s.path)},looksLikeEnvName=(value)=>!!value&&/^[A-Z][A-Z0-9_]{2,}$/.test(value);var init_registry=__esm(()=>{CREDENTIAL_KEY_PATTERN=/(secret|password|passwd|apikey|api_key|accesskey|access_key|privatekey|private_key|token|credential|connection_string|connectionstring|clientid|client_id|encryptionkey|encryption_key|servicekey|service_key|json_file_path|jsonfilepath)$/i,NOT_A_CREDENTIAL=new Set(["preventapikeymanagement","allowapplicationkeys","maxkeysperuser","tokenurl","accesstoken","refreshtoken","sessiontoken"]),BOOT_ONLY_PATHS=new Set(["authorization.godminPassword","authorization.godminEmail","authentication.accessToken.secret","authentication.refreshToken.secret","authentication.sessionToken.secret","backup.encryptionKey","notification.channels.telegram.botToken","notification.channels.webhook.headers","domains.cloudflare.apiToken"]),SKIP_SECTIONS=new Set(["entities","systemTables","system_tables","tables","columns","indexes","routes","cors","swagger","secrets"]),SECRET_SLOT_DESCRIPTORS=[{path:"authentication.oauth.providers.*",group:"oauth",label:"OAuth / SSO",keys:[{key:"clientId",kind:"text",label:"Client ID",required:!0},{key:"clientSecret",kind:"password",label:"Client Secret",required:!0},{key:"tenantId",kind:"text",label:"Tenant ID"},{key:"redirectUri",kind:"text",secret:!1,required:!0,label:"Y\xF6nlendirme adresi (Redirect URI)",hint:"Sa\u011Flay\u0131c\u0131ya kay\u0131tl\u0131 d\xF6n\xFC\u015F adresi. Bu adresin sa\u011Flay\u0131c\u0131 taraf\u0131nda da (\xF6r. Entra > Authentication > Redirect URIs) birebir ayn\u0131 yaz\u0131l\u0131 olmas\u0131 gerekir."}]},{always:!0,path:"email.gmail",group:"email",label:"Gmail (service account)",keys:[{key:"service_account_json",kind:"json",label:"Service Account JSON",required:!0,requiredWhen:"enabled"}]},{always:!0,path:"email.azure",group:"email",label:"Azure Communication Services",keys:[{key:"connection_string",kind:"connection_string",label:"Connection String",required:!0,requiredWhen:"enabled"}]},{always:!0,path:"storage.smb",group:"storage",label:"SMB / CIFS share",keys:[{key:"username",kind:"text",label:"Kullan\u0131c\u0131 ad\u0131",required:!0},{key:"password",kind:"password",label:"Parola",required:!0}]},{secret:!1,always:!0,path:"storage",group:"storage",label:"Dosya deposu",keys:[{key:"provider",kind:"text",label:"Depolama t\xFCr\xFC",hint:"local (sunucunun kendi diski) veya smb (kurumsal dosya sunucusu). De\u011Fi\u015Fiklik an\u0131nda ge\xE7erli olur."}]},{secret:!1,always:!0,path:"storage.smb",group:"storage",label:"SMB / CIFS share",keys:[{key:"url",kind:"text",label:"Payla\u015F\u0131m adresi",hint:"M\xFC\u015Fteriden geldi\u011Fi gibi yap\u0131\u015Ft\u0131r\u0131n: smb://sunucu/paylasim \u2014 sunucu ve payla\u015F\u0131m ad\u0131 buradan \xE7\xF6z\xFCl\xFCr."},{key:"domain",kind:"text",label:"Etki alan\u0131 (domain)",hint:"Kullan\u0131c\u0131 DOMAIN\\kullanici bi\xE7imindeyse domain k\u0131sm\u0131. Gerekmiyorsa bo\u015F b\u0131rak\u0131n."},{key:"base_path",kind:"text",label:"Payla\u015F\u0131m i\xE7i klas\xF6r",hint:"Y\xFCklemelerin toplanaca\u011F\u0131 alt klas\xF6r. Bo\u015Fsa payla\u015F\u0131m\u0131n k\xF6k\xFC kullan\u0131l\u0131r."}]},{path:"payment.providers.*",group:"payment",label:"\xD6deme sa\u011Flay\u0131c\u0131",keys:[{key:"apiKey",kind:"password",label:"API Key",required:!0},{key:"secretKey",kind:"password",label:"Secret Key",required:!0},{key:"webhookSecret",kind:"password",label:"Webhook Secret"}]},{path:"captcha",group:"captcha",label:"Captcha",keys:[{key:"secretKey",kind:"password",label:"Secret Key"}]},{secret:!1,path:"authentication",group:"portal",label:"Portal adresi",keys:[{key:"trustedAppOrigins",kind:"list",label:"G\xFCvenilen adresler",required:!0,hint:"Portal\u0131n kurulu oldu\u011Fu adres(ler). \u015Eifre s\u0131f\u0131rlama ve SSO d\xF6n\xFC\u015F linkleri buraya g\xF6re kurulur. Joker: *.firma.com"}]},{secret:!1,always:!0,path:"email",group:"email",label:"E-posta sa\u011Flay\u0131c\u0131",keys:[{key:"provider",kind:"text",label:"Kullan\u0131lacak sa\u011Flay\u0131c\u0131",hint:"azure veya gmail. Bo\u015F b\u0131rak\u0131l\u0131rsa a\xE7\u0131k olan sa\u011Flay\u0131c\u0131 kullan\u0131l\u0131r."}]},{secret:!1,always:!0,path:"email.azure",group:"email",label:"Azure Communication Services",keys:[{key:"enabled",kind:"boolean",label:"Azure e-posta a\xE7\u0131k",hint:"Ba\u011Flant\u0131 dizesini girmek tek ba\u015F\u0131na yetmez \u2014 g\xF6nderim bu anahtarla a\xE7\u0131l\u0131r."},{key:"sender_address",kind:"text",label:"G\xF6nderen adresi"}]},{secret:!1,always:!0,path:"email.gmail",group:"email",label:"Gmail (service account)",keys:[{key:"enabled",kind:"boolean",label:"Gmail e-posta a\xE7\u0131k"},{key:"from_email",kind:"text",label:"G\xF6nderen adresi"}]}]});import{and as and7,eq as eq12}from"drizzle-orm";class SecretsStore{db;resolveTable;masterKey;constructor(deps){this.db=deps.db,this.resolveTable=deps.resolveTable,this.masterKey=deps.masterKey}table(schemaName){return this.resolveTable(SECRETS_TABLE,schemaName)}async loadAll(schemaName){let values=new Map,failed=[],table=this.table(schemaName);if(!table)return{values,failed,tableMissing:!0};let rows;try{rows=await this.db.select().from(table).where(eq12(col6(table,"isActive"),!0))}catch(error){if(isMissingTableError(error))return{values,failed,tableMissing:!0};throw error}for(let row of rows)try{values.set(cacheKeyOf(row.scope,row.key),decryptSecret(row.valueEnc,this.masterKey))}catch{failed.push(`${row.scope}.${row.key}`)}return{values,failed}}async list(schemaName){let table=this.table(schemaName);if(!table)return[];try{return(await this.db.select().from(table)).filter((r)=>r.isActive!==!1).map(rowToMeta)}catch(error){if(isMissingTableError(error))return[];throw error}}async get(scope,key2,schemaName){let table=this.table(schemaName);if(!table)return null;return(await this.db.select().from(table).where(and7(eq12(col6(table,"scope"),scope),eq12(col6(table,"key"),key2))).limit(1))[0]??null}async set(params,schemaName){let table=this.table(schemaName);if(!table)return null;let existing=await this.get(params.scope,params.key,schemaName),valueEnc=encryptSecret(params.value,this.masterKey),preview=previewOf(params.value),now=new Date;if(existing)await this.db.update(table).set({valueEnc,previousEnc:existing.valueEnc,preview,kind:params.kind??existing.kind??"text",description:params.description??existing.description,version:(existing.version??1)+1,rotatedAt:now,isActive:!0,updatedAt:now,updatedBy:params.actorId??null}).where(eq12(col6(table,"id"),existing.id));else await this.db.insert(table).values({scope:params.scope,key:params.key,valueEnc,preview,kind:params.kind??"text",description:params.description??null,version:1,isActive:!0,createdBy:params.actorId??null,updatedBy:params.actorId??null});let saved=await this.get(params.scope,params.key,schemaName);return saved?rowToMeta(saved):null}async rollback(scope,key2,actorId,schemaName){let table=this.table(schemaName);if(!table)return null;let existing=await this.get(scope,key2,schemaName);if(!existing?.previousEnc)return null;let plaintext=decryptSecret(existing.previousEnc,this.masterKey),now=new Date;await this.db.update(table).set({valueEnc:existing.previousEnc,previousEnc:existing.valueEnc,preview:previewOf(plaintext),version:(existing.version??1)+1,rotatedAt:now,updatedAt:now,updatedBy:actorId??null}).where(eq12(col6(table,"id"),existing.id));let saved=await this.get(scope,key2,schemaName);return saved?rowToMeta(saved):null}async remove(scope,key2,schemaName){let table=this.table(schemaName);if(!table)return!1;let existing=await this.get(scope,key2,schemaName);if(!existing)return!1;return await this.db.delete(table).where(eq12(col6(table,"id"),existing.id)),!0}async touch(scope,key2,schemaName){let table=this.table(schemaName);if(!table)return;await this.db.update(table).set({lastUsedAt:new Date}).where(and7(eq12(col6(table,"scope"),scope),eq12(col6(table,"key"),key2)))}async reencryptAll(newMasterKey,schemaName){let table=this.table(schemaName);if(!table)return{migrated:0,failed:[]};let rows=await this.db.select().from(table),migrated=0,failed=[];for(let row of rows)try{let plaintext=decryptSecret(row.valueEnc,this.masterKey),previous=row.previousEnc?encryptSecret(decryptSecret(row.previousEnc,this.masterKey),newMasterKey):null;await this.db.update(table).set({valueEnc:encryptSecret(plaintext,newMasterKey),previousEnc:previous}).where(eq12(col6(table,"id"),row.id)),migrated++}catch{failed.push(`${row.scope}.${row.key}`)}if(failed.length===0)this.masterKey=newMasterKey;return{migrated,failed}}}var SECRETS_TABLE="nucleusSecrets",col6=(table,key2)=>table[key2],toIso=(value)=>value?new Date(value).toISOString():null,UNDEFINED_TABLE="42P01",isMissingTableError=(error)=>{let seen=new Set,current=error;while(current&&typeof current==="object"&&!seen.has(current)){seen.add(current);let record=current;if(record.code===UNDEFINED_TABLE)return!0;if(typeof record.message==="string"&&/relation .* does not exist/i.test(record.message))return!0;current=record.cause}return!1},rowToMeta=(row)=>({id:row.id,scope:row.scope,key:row.key,kind:row.kind??"text",preview:row.preview??"",description:row.description,version:row.version??1,canRollback:!!row.previousEnc,rotatedAt:toIso(row.rotatedAt),lastUsedAt:toIso(row.lastUsedAt),updatedAt:toIso(row.updatedAt),updatedBy:row.updatedBy}),cacheKeyOf=(scope,key2)=>`${scope}.${key2}`;var init_store=__esm(()=>{init_crypto()});class SecretsService{store;logger;getRedis;refreshIntervalMs;schemaName;cache=new Map;timer=null;localRevision=0;loaded=!1;tableMissing=!1;warnedTableMissing=!1;constructor(deps){this.store=deps.store,this.logger=deps.logger,this.getRedis=deps.getRedis,this.refreshIntervalMs=deps.refreshIntervalMs??DEFAULT_REFRESH_MS,this.schemaName=deps.schemaName}async start(){if(await this.refresh(),this.timer)return;this.timer=setInterval(()=>{this.refreshIfStale()},this.refreshIntervalMs),this.timer.unref?.()}stop(){if(this.timer)clearInterval(this.timer);this.timer=null}isReady(){return this.loaded}size(){return this.cache.size}isTableMissing(){return this.tableMissing}async refresh(){try{let{values,failed,tableMissing}=await this.store.loadAll(this.schemaName);if(tableMissing){if(this.tableMissing=!0,!this.warnedTableMissing)this.warnedTableMissing=!0,this.logger.warn("[Secrets] nucleus_secrets does not exist yet \u2014 env and literal values resolve normally; "+"the store activates automatically once the schema sync creates it");return}if(this.tableMissing)this.tableMissing=!1,this.warnedTableMissing=!1,this.logger.info("[Secrets] nucleus_secrets is now available \u2014 credential store active");if(this.cache=values,this.loaded=!0,failed.length>0)this.logger.error("[Secrets] Credentials failed to decrypt and were skipped",{slots:failed,hint:"master key changed without re-encrypting \u2014 POST /secrets/reencrypt with the old key"})}catch(error){this.logger.warn("[Secrets] Refresh failed, keeping cached values",{error:error instanceof Error?error.message:String(error)})}}async refreshIfStale(){let redis=this.getRedis?.();if(!redis)return this.refresh();try{let result=await redis.read(REVISION_KEY),remote=Number(result.data??0);if(Number.isFinite(remote)&&remote===this.localRevision&&this.loaded)return;await this.refresh(),this.localRevision=Number.isFinite(remote)?remote:this.localRevision}catch{await this.refresh()}}async bumpRevision(){let redis=this.getRedis?.();if(!redis)return;try{let next=Date.now();this.localRevision=next,await redis.create(REVISION_KEY,next)}catch{}}get(scope,key2){return this.cache.get(cacheKeyOf(scope,key2))}has(scope,key2){return this.cache.has(cacheKeyOf(scope,key2))}resolve(scope,key2,configValue){let fromDb=this.cache.get(cacheKeyOf(scope,key2));if(fromDb)return{value:fromDb,source:"db"};if(configValue){let fromEnv=process.env[configValue];if(fromEnv)return{value:fromEnv,source:"env"};if(looksLikeEnvName(configValue))return{value:void 0,source:"missing"};return{value:configValue,source:"literal"}}return{value:void 0,source:"missing"}}value(scope,key2,configValue){return this.resolve(scope,key2,configValue).value}listValue(scope,key2,configValue){let fallback=Array.isArray(configValue)?configValue.join(","):configValue,raw=this.value(scope,key2,fallback);if(!raw)return Array.isArray(configValue)?configValue:[];return raw.split(/[,;\n\r]+/).map((entry)=>entry.trim()).filter(Boolean)}flag(scope,key2,configValue){let raw=this.get(scope,key2);if(raw===void 0)return configValue===!0;return raw==="true"||raw==="1"}liveConfig(scope,node,keys){let live={...node};for(let key2 of keys){let declared=node[key2],configValue=typeof declared==="string"?declared:void 0;Object.defineProperty(live,key2,{get:()=>this.resolve(scope,key2,configValue).value,enumerable:!0,configurable:!0})}return live}async describe(config,slots){let discovered=slots??discoverSlots(config),stored=await this.store.list(this.schemaName),byId=new Map(stored.map((s)=>[cacheKeyOf(s.scope,s.key),s]));return discovered.map((slot)=>{let id=cacheKeyOf(slot.scope,slot.key),record=byId.get(id),{value,source}=this.resolve(slot.scope,slot.key,slot.configValue),envName=looksLikeEnvName(slot.configValue)?slot.configValue:void 0,gate=slot.requiredWhen?this.flag(slot.scope,slot.requiredWhen,configFlagAt(config,slot.scope,slot.requiredWhen)):!0;return{...slot,required:slot.required&&gate,configValue:envName,source,value:slot.secret?void 0:value,preview:slot.secret?source==="db"&&record?record.preview:value?previewOf(value):"":value??"",envName,version:record?.version,rotatedAt:record?.rotatedAt??null,canRollback:record?.canRollback??!1}})}async list(){return this.store.list(this.schemaName)}async set(params){let saved=await this.store.set(params,this.schemaName);return await this.refresh(),await this.bumpRevision(),this.logger.info("[Secrets] Credential stored",{scope:params.scope,key:params.key,version:saved?.version,actor:params.actorId}),saved}async rollback(scope,key2,actorId){let saved=await this.store.rollback(scope,key2,actorId,this.schemaName);if(!saved)return null;return await this.refresh(),await this.bumpRevision(),this.logger.info("[Secrets] Credential rolled back",{scope,key:key2,actor:actorId}),saved}async remove(scope,key2,actorId){if(!await this.store.remove(scope,key2,this.schemaName))return!1;return await this.refresh(),await this.bumpRevision(),this.logger.info("[Secrets] Credential removed",{scope,key:key2,actor:actorId}),!0}async reencryptAll(newMasterKey){let result=await this.store.reencryptAll(newMasterKey,this.schemaName);return await this.refresh(),await this.bumpRevision(),result}}var REVISION_KEY="nucleus:secrets:revision",DEFAULT_REFRESH_MS=30000,configFlagAt=(config,scope,key2)=>{let node=config;for(let segment of scope.split(".")){if(typeof node!=="object"||node===null)return;node=node[segment]}if(typeof node!=="object"||node===null)return;let value=node[key2];if(typeof value==="boolean")return value;if(typeof value==="string")return value==="true"||value==="1";return};var init_SecretsService=__esm(()=>{init_crypto();init_registry();init_store()});var resolveMasterKey=(configured)=>{if(configured){let fromEnv=process.env[configured];if(fromEnv)return fromEnv;if(!/^[A-Z][A-Z0-9_]{2,}$/.test(configured))return configured}return process.env.NUCLEUS_SECRETS_KEY||void 0};var init_Secrets=__esm(()=>{init_crypto();init_registry();init_SecretsService();init_store()});var exports_schema={};__export(exports_schema,{isAdditiveStatement:()=>isAdditiveStatement,ensureSchemaExists:()=>ensureSchemaExists,applySchemaPush:()=>applySchemaPush});import{sql as sql4}from"drizzle-orm";var validateIdentifier=(name)=>{if(!name||name.length>63)throw Error(`Invalid identifier: must be 1-63 characters, got ${name.length}`);if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name))throw Error(`Invalid identifier: "${name}" contains unsafe characters`);return name},ensureSchemaExists=async(db,schemaName)=>{let safeName=validateIdentifier(schemaName),found=await db.execute(sql4`select 1 from pg_catalog.pg_namespace where nspname = ${safeName} limit 1`);if(rowCountOf(found)>0)return;await db.execute(sql4.raw(`CREATE SCHEMA IF NOT EXISTS "${safeName}"`))},rowCountOf=(result)=>{if(Array.isArray(result))return result.length;let rows=result?.rows;return Array.isArray(rows)?rows.length:0},ADDITIVE_PREFIXES,isAdditiveStatement=(statement)=>{let normalized=statement.trim().toUpperCase();if(/^ALTER TABLE .+ ADD COLUMN/.test(normalized))return!0;if(/^ALTER TABLE .+ ALTER COLUMN .+ SET DEFAULT /.test(normalized))return!0;return ADDITIVE_PREFIXES.some((prefix)=>normalized.startsWith(prefix))},applySchemaPush=async(push,opts)=>{let{schemaName,allowDataLoss,logger:logger2,execute}=opts;if(push.hasDataLoss&&!allowDataLoss){let additive=push.statementsToExecute.filter(isAdditiveStatement),blocked=push.statementsToExecute.filter((s)=>!isAdditiveStatement(s));if(logger2.error(`[Schema] Destructive changes detected for schema "${schemaName}" but database.allowDataLoss is not enabled \u2014 ${blocked.length} statement(s) skipped. Set database.allowDataLoss: true to apply them.`,{warnings:push.warnings,statements:blocked}),additive.length===0||!execute)return!1;let applied=0;for(let statement of additive)try{await execute(statement),applied++}catch(error){logger2.warn("[Schema] Additive statement failed",{schema:schemaName,statement,error:error instanceof Error?error.message:String(error)})}return logger2.info(`[Schema] Applied ${applied}/${additive.length} additive statement(s) for "${schemaName}" despite the destructive changes being held back`),applied>0}if(push.hasDataLoss)logger2.warn(`[Schema] Applying destructive changes to schema "${schemaName}" (database.allowDataLoss=true)`,{warnings:push.warnings});return await push.apply(),!0};var init_schema=__esm(()=>{ADDITIVE_PREFIXES=["CREATE TABLE IF NOT EXISTS","CREATE TABLE","CREATE SCHEMA IF NOT EXISTS","CREATE SCHEMA","CREATE INDEX IF NOT EXISTS","CREATE INDEX","CREATE UNIQUE INDEX IF NOT EXISTS","CREATE UNIQUE INDEX","CREATE TYPE","DO $$"]});var rowToTenantRecord=(row)=>({id:String(row.id||""),subdomain:String(row.subdomain||""),schemaName:String(row.schemaName||row.schema_name||""),companyId:String(row.companyId||row.company_id||""),companyName:row.companyName!=null?String(row.companyName):row.company_name!=null?String(row.company_name):null,godAdminEmail:String(row.godAdminEmail||row.god_admin_email||""),status:parseStatus(row.status),plan:row.plan!=null?String(row.plan):null,domain:row.domain!=null?String(row.domain):null,settings:parseJsonbToConfig(row.settings),trustedSources:parseTrustedSources(row.trustedSources||row.trusted_sources),maxUsers:row.maxUsers!=null?Number(row.maxUsers):row.max_users!=null?Number(row.max_users):null,provisionedAt:row.provisionedAt!=null?String(row.provisionedAt):row.provisioned_at!=null?String(row.provisioned_at):null,suspendedAt:row.suspendedAt!=null?String(row.suspendedAt):row.suspended_at!=null?String(row.suspended_at):null,suspendedReason:row.suspendedReason!=null?String(row.suspendedReason):row.suspended_reason!=null?String(row.suspended_reason):null}),rowToFeatureRecord=(row,parseConfig)=>({id:String(row.id||""),tenantId:String(row.tenantId||row.tenant_id||""),featureName:String(row.featureName||row.feature_name||""),enabled:Boolean(row.enabled),featureConfig:parseConfig(row.config)}),parseStatus=(value)=>{let valid=["provisioning","active","suspended","archived"],str3=String(value||"provisioning");return valid.includes(str3)?str3:"provisioning"},parseJsonbToConfig=(value)=>{if(!value||typeof value!=="object")return{};let result={};for(let[k,v]of Object.entries(value))if(typeof v==="string"||typeof v==="number"||typeof v==="boolean")result[k]=v;return result},parseTrustedSources=(value)=>{if(!Array.isArray(value))return[];return value.map((item)=>{let entry=item;return{allowHeaderAuth:entry.allowHeaderAuth===!0||entry.allow_header_auth===!0,allowedIps:Array.isArray(entry.allowedIps||entry.allowed_ips)?entry.allowedIps||entry.allowed_ips:void 0,allowedServices:Array.isArray(entry.allowedServices||entry.allowed_services)?entry.allowedServices||entry.allowed_services:void 0}})},normalizeHost=(host)=>{return((host||"").split(":")[0]||"").toLowerCase().replace(/\.$/,"")},extractSubdomain=(host)=>{let hostWithoutPort=host.split(":")[0]||"";if(hostWithoutPort==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(hostWithoutPort))return null;let parts=hostWithoutPort.split(".");if(parts.length<3)return null;let subdomain=parts[0]||"";if(!subdomain||subdomain==="www")return null;return subdomain},isIpInCidr=(ip,cidr)=>{let[cidrIp,prefixStr]=cidr.split("/");if(!cidrIp||!prefixStr)return!1;let prefix=Number.parseInt(prefixStr,10);if(Number.isNaN(prefix))return!1;let ipParts=ip.split(".").map(Number),cidrParts=cidrIp.split(".").map(Number);if(ipParts.length!==4||cidrParts.length!==4)return!1;let ipNum=(ipParts[0]||0)<<24|(ipParts[1]||0)<<16|(ipParts[2]||0)<<8|(ipParts[3]||0),cidrNum=(cidrParts[0]||0)<<24|(cidrParts[1]||0)<<16|(cidrParts[2]||0)<<8|(cidrParts[3]||0),mask=~((1<<32-prefix)-1);return(ipNum&mask)===(cidrNum&mask)},isTrustedSource=(tenant,request,authMode,fallbackSources,vettedClientIp)=>{let ownSources=tenant.trustedSources,trustedSources=Array.isArray(ownSources)&&ownSources.length>0?ownSources:fallbackSources;if(!trustedSources||!Array.isArray(trustedSources)||trustedSources.length===0)return!1;let clientIp=vettedClientIp?.trim()||"",serviceId=request.headers.get("x-service-id")||"";for(let source of trustedSources){if(!source.allowHeaderAuth)continue;if(source.allowedIps&&source.allowedIps.length>0){if(source.allowedIps.some((allowedIp)=>{if(allowedIp.includes("/"))return isIpInCidr(clientIp,allowedIp);return clientIp===allowedIp}))return!0}if(source.allowedServices&&source.allowedServices.length>0){if(source.allowedServices.includes(serviceId))return!0}}return!1};function getDatabaseAuthMode(){return process.env.DATABASE_AUTH_MODE||"password"}function getRedisAuthMode(){return process.env.REDIS_AUTH_MODE||"password"}async function acquireToken(scope,label){let{DefaultAzureCredential,ManagedIdentityCredential,ClientSecretCredential}=await import("@azure/identity"),authMode=scope===PG_TOKEN_SCOPE?getDatabaseAuthMode():getRedisAuthMode(),clientId=process.env.AZURE_CLIENT_ID||"",tenantId=process.env.AZURE_TENANT_ID||"",clientSecret=process.env.AZURE_CLIENT_SECRET||"",credential;if(authMode==="managed_identity")credential=clientId?new ManagedIdentityCredential(clientId):new DefaultAzureCredential;else if(authMode==="service_principal"){if(!tenantId||!clientId||!clientSecret)throw Error("[azure-auth] service_principal auth requires AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET");credential=new ClientSecretCredential(tenantId,clientId,clientSecret)}else throw Error(`[azure-auth] Unsupported auth mode: ${authMode}`);let result=await credential.getToken(scope);if(!result)throw Error(`[azure-auth] Failed to acquire token for ${label}`);return logger.scoped("database.connect").info(`[azure-auth] ${label} token acquired`,{expiresAt:new Date(result.expiresOnTimestamp).toISOString(),authMode}),{token:result.token,expiresAt:result.expiresOnTimestamp}}async function getPostgresToken(){let now=Date.now();if(pgCachedToken&&now<pgTokenExpiresAt-REFRESH_BUFFER_MS)return pgCachedToken;let{token,expiresAt}=await acquireToken(PG_TOKEN_SCOPE,"PostgreSQL");return pgCachedToken=token,pgTokenExpiresAt=expiresAt,pgCachedToken}async function getRedisToken(){let now=Date.now();if(redisCachedToken&&now<redisTokenExpiresAt-REFRESH_BUFFER_MS)return redisCachedToken;let{token,expiresAt}=await acquireToken(REDIS_TOKEN_SCOPE,"Redis");return redisCachedToken=token,redisTokenExpiresAt=expiresAt,redisCachedToken}function getRedisTokenExpiresAt(){return redisTokenExpiresAt}var PG_TOKEN_SCOPE="https://ossrdbms-aad.database.windows.net/.default",REDIS_TOKEN_SCOPE="https://redis.azure.com/.default",REFRESH_BUFFER_MS=300000,pgCachedToken=null,pgTokenExpiresAt=0,redisCachedToken=null,redisTokenExpiresAt=0;var init_AzureTokenProvider=__esm(()=>{init_Logger2()});var exports_Azure={};__export(exports_Azure,{getRedisTokenExpiresAt:()=>getRedisTokenExpiresAt,getRedisToken:()=>getRedisToken,getRedisAuthMode:()=>getRedisAuthMode,getPostgresToken:()=>getPostgresToken,getDatabaseAuthMode:()=>getDatabaseAuthMode});var init_Azure=__esm(()=>{init_AzureTokenProvider()});import{access,mkdir as mkdir4}from"fs/promises";import{dirname as dirname3,resolve}from"path";var DEFAULT_CONFIG5,FILE_SIZE_UNITS,resolvePath=(path2)=>{if(!path2||typeof path2!=="string")throw createFileManagerError("INVALID_PATH","Path must be a non-empty string",path2,"resolvePath");return resolve(path2)},extractDirectoryPath=(filePath)=>{let resolvedPath=resolvePath(filePath);return dirname3(resolvedPath)},ensureDirectoryExists=async(dirPath)=>{let resolvedPath=resolve(dirPath);try{await mkdir4(resolvedPath,{recursive:!0})}catch(error){if(error.code!=="EEXIST")throw createFileManagerError("DIRECTORY_CREATE_FAILED",`Failed to create directory: ${resolvedPath}`,resolvedPath,"ensureDirectory")}},formatFileSize=(bytes)=>{let size=bytes,unitIndex=0;while(size>=1024&&unitIndex<FILE_SIZE_UNITS.length-1)size/=1024,unitIndex++;return`${size.toFixed(2)} ${FILE_SIZE_UNITS[unitIndex]}`},validateFileExtension=(fileName,expectedExtension)=>{return fileName.toLowerCase().endsWith(expectedExtension.toLowerCase())},ensureFileExtension=(fileName,extension)=>{let normalizedExtension=extension.startsWith(".")?extension:`.${extension}`;if(validateFileExtension(fileName,normalizedExtension))return fileName;return`${fileName}${normalizedExtension}`},createFileManagerError=(code,message,path2,operation)=>{return{code,message,path:path2,operation:operation||"unknown"}},safeJsonStringify=(data)=>{try{return JSON.stringify(data,null,2)}catch{return"{}"}},executeBulkOperation=async(items,operation,concurrency=DEFAULT_CONFIG5.maxConcurrency)=>{let results=[];for(let i=0;i<items.length;i+=concurrency){let batch3=items.slice(i,i+concurrency),batchPromises=[];for(let item of batch3)batchPromises.push(operation(item));let batchResults=await Promise.allSettled(batchPromises);results.push(...batchResults)}return results},validateConfig=(config,options={})=>{let errors=[],warnings=[],strict=options.strict??!0;if(config.defaultEncoding!==void 0){if(!["utf-8","utf8","ascii","base64","hex"].includes(config.defaultEncoding))errors.push(`Invalid defaultEncoding: ${config.defaultEncoding}`)}if(config.maxConcurrency!==void 0){if(!Number.isInteger(config.maxConcurrency)||config.maxConcurrency<1)errors.push("maxConcurrency must be a positive integer");if(config.maxConcurrency>50)warnings.push("maxConcurrency > 50 may cause performance issues")}if(config.defaultCreateDir!==void 0&&typeof config.defaultCreateDir!=="boolean")errors.push("defaultCreateDir must be a boolean");if(config.defaultRecursive!==void 0&&typeof config.defaultRecursive!=="boolean")errors.push("defaultRecursive must be a boolean");if(strict&&!options.allowUnknownKeys){let validKeys=["defaultEncoding","defaultCreateDir","defaultRecursive","maxConcurrency"],configKeys=Object.keys(config);for(let key2 of configKeys)if(!validKeys.includes(key2))errors.push(`Unknown configuration key: ${key2}`)}return{isValid:errors.length===0,errors,warnings}},mergeConfig=(partial,base=DEFAULT_CONFIG5)=>{let validation=validateConfig(partial);if(!validation.isValid)throw createFileManagerError("CONFIG_VALIDATION_FAILED",`Configuration validation failed: ${validation.errors.join(", ")}`,void 0,"mergeConfig");return{...base,...partial}},parsePermissions=(mode)=>{let parseOctal=(octal)=>({read:Boolean(octal&4),write:Boolean(octal&2),execute:Boolean(octal&1)}),ownerMode=mode>>6&7,groupMode=mode>>3&7,othersMode=mode&7;return{owner:parseOctal(ownerMode),group:parseOctal(groupMode),others:parseOctal(othersMode)}},validatePermissionMode=(mode)=>{return Number.isInteger(mode)&&mode>=0&&mode<=511};var init_utils4=__esm(()=>{DEFAULT_CONFIG5={defaultEncoding:"utf-8",defaultCreateDir:!0,defaultRecursive:!0,maxConcurrency:5},FILE_SIZE_UNITS=["B","KB","MB","GB","TB"]});import{copyFile,rename as rename2,unlink as unlink4}from"fs/promises";import{basename,dirname as dirname4,extname,join as join5}from"path";var DEFAULT_ATOMIC_CONFIG,generateTempPath=(originalPath,suffix=".tmp")=>{let resolvedPath=resolvePath(originalPath),timestamp=Date.now(),random=Math.random().toString(36).substring(2,8);return`${resolvedPath}${suffix}.${timestamp}.${random}`},generateBackupPath=(originalPath,backupDir,useTimestamp=!0)=>{let resolvedPath=resolvePath(originalPath),dir=backupDir?resolvePath(backupDir):dirname4(resolvedPath),name=basename(resolvedPath),ext=extname(name),nameWithoutExt=basename(name,ext),timestamp=useTimestamp?`.${new Date().toISOString().replace(/[:.]/g,"-")}`:"",backupName=`${nameWithoutExt}.backup${timestamp}${ext}`;return join5(dir,backupName)},atomicWrite=async({path:path2,data,tempSuffix=DEFAULT_ATOMIC_CONFIG.tempSuffix,backup=DEFAULT_ATOMIC_CONFIG.backup,sync=DEFAULT_ATOMIC_CONFIG.sync})=>{let resolvedPath=resolvePath(path2),tempPath=generateTempPath(resolvedPath,tempSuffix),backupPath;try{if(await ensureDirectoryExists(extractDirectoryPath(resolvedPath)),backup){if(await Bun.file(resolvedPath).exists())backupPath=generateBackupPath(resolvedPath),await copyFile(resolvedPath,backupPath)}let bytesWritten=await Bun.write(tempPath,data);return await rename2(tempPath,resolvedPath),{success:!0,bytesWritten,tempPath,backupPath}}catch(error){try{await unlink4(tempPath)}catch{}throw createFileManagerError("ATOMIC_WRITE_FAILED",`Atomic write failed: ${error}`,resolvedPath,"atomicWrite")}},atomicJsonWrite=async(path2,data,options={})=>{let jsonString=JSON.stringify(data,null,2);return atomicWrite({path:path2,data:jsonString,...options})},createBackup=async({sourcePath,backupDir,keepOriginal=!0,timestamp=DEFAULT_ATOMIC_CONFIG.timestamp})=>{let resolvedSource=resolvePath(sourcePath);if(!await Bun.file(resolvedSource).exists())throw createFileManagerError("SOURCE_NOT_FOUND",`Source file not found: ${sourcePath}`,resolvedSource,"createBackup");let backupPath=generateBackupPath(resolvedSource,backupDir,timestamp);if(await ensureDirectoryExists(dirname4(backupPath)),keepOriginal)await copyFile(resolvedSource,backupPath);else await rename2(resolvedSource,backupPath);return backupPath},restoreFromBackup=async(backupPath,targetPath,deleteBackup=!1)=>{let resolvedBackup=resolvePath(backupPath),resolvedTarget=resolvePath(targetPath);if(!await Bun.file(resolvedBackup).exists())throw createFileManagerError("BACKUP_NOT_FOUND",`Backup file not found: ${backupPath}`,resolvedBackup,"restoreFromBackup");try{if(await ensureDirectoryExists(extractDirectoryPath(resolvedTarget)),deleteBackup)await rename2(resolvedBackup,resolvedTarget);else await copyFile(resolvedBackup,resolvedTarget);return!0}catch(error){return logger.scoped("storage.fs").error(`Failed to restore from backup ${backupPath}`,error,{backupPath}),!1}},safeUpdate=async(path2,updateFunction,options={})=>{let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath),backupPath;try{if(await file.exists())backupPath=await createBackup({sourcePath:resolvedPath,keepOriginal:!0,timestamp:!0});let currentData=await file.exists()?await file.text():"",newData=await updateFunction(currentData),result=await atomicWrite({path:resolvedPath,data:newData,backup:!1,...options});return{success:result.success,bytesWritten:result.bytesWritten,tempPath:result.tempPath,backupPath}}catch(error){if(backupPath)try{await restoreFromBackup(backupPath,resolvedPath,!1)}catch(rollbackError){logger.scoped("storage.fs").error("Atomic write rollback failed",rollbackError,{backupPath})}throw error}},batchAtomicWrite=async(operations)=>{let successful=[],failed=[];for(let operation of operations)try{let result=await atomicWrite(operation);successful.push(result)}catch(error){failed.push({operation,error})}return{successful,failed}};var init_atomic=__esm(()=>{init_Logger2();init_utils4();DEFAULT_ATOMIC_CONFIG={tempSuffix:".tmp",backup:!1,sync:!0,timestamp:!0}});import{chmod,stat as stat3}from"fs/promises";var PERMISSION_MODES,setFilePermissions=async(path2,mode)=>{let resolvedPath=resolvePath(path2);if(!validatePermissionMode(mode))throw createFileManagerError("INVALID_PERMISSION_MODE",`Invalid permission mode: ${mode.toString(8)}`,resolvedPath,"setFilePermissions");try{return await chmod(resolvedPath,mode),!0}catch(error){return logger.scoped("storage.fs").error(`Failed to set permissions for ${path2}`,error,{path:path2,mode}),!1}},getFilePermissions=async(path2)=>{let resolvedPath=resolvePath(path2);try{let mode=(await stat3(resolvedPath)).mode&511,permissions=parsePermissions(mode);return{path:resolvedPath,mode,owner:permissions.owner,group:permissions.group,others:permissions.others}}catch(error){throw createFileManagerError("PERMISSION_READ_FAILED",`Failed to read permissions: ${error}`,resolvedPath,"getFilePermissions")}},hasPermissions=async(path2,requiredMode)=>{try{return((await getFilePermissions(path2)).mode&requiredMode)===requiredMode}catch{return!1}},makeReadable=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode|256;return setFilePermissions(path2,newMode)},makeWritable=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode|128;return setFilePermissions(path2,newMode)},makeExecutable=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode|64;return setFilePermissions(path2,newMode)},makeReadOnly=async(path2)=>{let newMode=(await getFilePermissions(path2)).mode&-147;return setFilePermissions(path2,newMode)},setCommonPermissions=async(path2,pattern)=>{let mode=PERMISSION_MODES[pattern];return setFilePermissions(path2,mode)};var init_permissions=__esm(()=>{init_Logger2();init_utils4();PERMISSION_MODES={OWNER_READ_WRITE:384,OWNER_ALL:448,GROUP_READ:416,GROUP_READ_WRITE:432,ALL_READ:420,ALL_READ_WRITE:438,ALL_READ_EXECUTE:493,ALL_FULL:511,READ_ONLY:292,EXECUTABLE:493}});var DEFAULT_STREAM_CONFIG,createFileWriter=async(path2,options={})=>{let resolvedPath=resolvePath(path2),config={...DEFAULT_STREAM_CONFIG,...options};await ensureDirectoryExists(extractDirectoryPath(resolvedPath));let writer=Bun.file(resolvedPath).writer({highWaterMark:config.highWaterMark}),isClosed=!1;return{write:(chunk)=>{if(isClosed)throw createFileManagerError("WRITER_CLOSED","Cannot write to closed writer",resolvedPath,"streamWrite");try{let result=writer.write(chunk);if(config.autoFlush)writer.flush();return result}catch(error){throw createFileManagerError("WRITE_FAILED",`Failed to write chunk: ${error}`,resolvedPath,"streamWrite")}},flush:()=>{if(isClosed)return 0;try{return writer.flush()}catch(error){throw createFileManagerError("FLUSH_FAILED",`Failed to flush writer: ${error}`,resolvedPath,"streamFlush")}},end:async(error)=>{if(isClosed)return 0;try{let result=await writer.end(error);return isClosed=!0,result}catch(err){throw isClosed=!0,createFileManagerError("END_FAILED",`Failed to end writer: ${err}`,resolvedPath,"streamEnd")}},ref:()=>{if(!isClosed)writer.ref()},unref:()=>{if(!isClosed)writer.unref()}}},writeStream=async(path2,chunks,options={})=>{let writer=await createFileWriter(path2,options),totalBytes=0;try{for(let chunk of chunks){let bytesWritten=writer.write(chunk);totalBytes+=bytesWritten}return await writer.flush(),await writer.end(),totalBytes}catch(error){try{await writer.end(error)}catch{}throw error}},appendStream=async(path2,chunks,options={})=>{let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath),existingContent=await file.exists()?await file.arrayBuffer():new ArrayBuffer(0),allChunks=[];if(existingContent.byteLength>0)allChunks.push(existingContent);return allChunks.push(...chunks),writeStream(resolvedPath,allChunks,options)},copyFileStream=async(sourcePath,destinationPath,options={})=>{let resolvedSource=resolvePath(sourcePath),sourceFile=Bun.file(resolvedSource);if(!await sourceFile.exists())throw createFileManagerError("SOURCE_NOT_FOUND",`Source file not found: ${sourcePath}`,resolvedSource,"copyFileStream");let sourceStream=sourceFile.stream(),writer=await createFileWriter(destinationPath,options),totalBytes=0;try{let reader=sourceStream.getReader();while(!0){let{done,value}=await reader.read();if(done)break;let bytesWritten=writer.write(value);totalBytes+=bytesWritten}return await writer.flush(),await writer.end(),totalBytes}catch(error){try{await writer.end(error)}catch{}throw error}},readFileStream=async(path2,chunkProcessor)=>{let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath);if(!await file.exists())throw createFileManagerError("FILE_NOT_FOUND",`File not found: ${path2}`,resolvedPath,"readFileStream");let reader=file.stream().getReader();try{while(!0){let{done,value}=await reader.read();if(done)break;await chunkProcessor(value)}}finally{reader.releaseLock()}};var init_streaming=__esm(()=>{init_utils4();DEFAULT_STREAM_CONFIG={highWaterMark:1048576,autoFlush:!0,closeOnEnd:!0}});import{readdir as readdir2,rm,rmdir,stat as stat4}from"fs/promises";import{extname as extname2,join as join6}from"path";class BunFileManager{static instance;config;constructor(){this.config={...DEFAULT_CONFIG5}}static getInstance(){if(!BunFileManager.instance)BunFileManager.instance=new BunFileManager;return BunFileManager.instance}async createFile({dir,name,data,options={}}){let filePath=resolvePath(join6(dir,name));if(options.createDir!==!1)await ensureDirectoryExists(extractDirectoryPath(filePath));let fileData=options.type?new Blob([data],{type:options.type}):data;return await Bun.write(filePath,fileData)}async createJsonFile(dir,name,data){let fileName=ensureFileExtension(name,".json"),jsonString=safeJsonStringify(data);return this.createFile({dir,name:fileName,data:jsonString,options:{type:"application/json"}})}async createDirectory({path:path2}){await ensureDirectoryExists(path2)}async readFile({path:path2,format="text"}){let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath);if(!await file.exists())throw createFileManagerError("FILE_NOT_FOUND",`File not found: ${path2}`,resolvedPath,"readFile");switch(format){case"text":return await file.text();case"json":return await file.json();case"buffer":return await file.arrayBuffer();case"bytes":return await file.bytes();case"stream":return file.stream();default:return await file.text()}}async readJsonFile(path2){return this.readFile({path:path2,format:"json"})}async getFileInfo(path2){let resolvedPath=resolvePath(path2),file=Bun.file(resolvedPath),fileName=path2.split("/").pop()||path2,stats=null;try{stats=await stat4(resolvedPath)}catch{}return{name:fileName,path:resolvedPath,size:file.size,type:file.type,exists:await file.exists(),extension:extname2(fileName),createdAt:stats?.birthtime,modifiedAt:stats?.mtime}}async readDirectory({path:path2,recursive=!1}){let resolvedPath=resolvePath(path2);return await readdir2(resolvedPath,{recursive,encoding:"utf8"})}async getFilesByExtension(dir,extension){let files=await this.readDirectory({path:dir}),normalizedExt=extension.startsWith(".")?extension:`.${extension}`;return files.filter((file)=>file.endsWith(normalizedExt))}async updateFile({path:path2,data,mode="overwrite"}){let resolvedPath=resolvePath(path2);if(mode==="append"){let combinedData=await this.readFile({path:path2,format:"text"})+data;return await Bun.write(resolvedPath,combinedData)}return await Bun.write(resolvedPath,data)}async updateJsonFile(path2,data,merge=!1){let finalData=data;if(merge)try{let existingData=await this.readJsonFile(path2);if(typeof existingData==="object"&&existingData!==null&&!Array.isArray(existingData)&&typeof data==="object"&&data!==null&&!Array.isArray(data))finalData={...existingData,...data}}catch{}return this.updateFile({path:path2,data:safeJsonStringify(finalData),mode:"overwrite"})}async appendToFile(path2,data){return this.updateFile({path:path2,data,mode:"append"})}async deleteFile(path2){try{let resolvedPath=resolvePath(path2);return await Bun.file(resolvedPath).delete(),!0}catch(error){return logger.scoped("storage.fs").error(`Failed to delete file ${path2}`,error,{path:path2}),!1}}async deleteDirectory({path:path2,recursive=!1}){try{let resolvedPath=resolvePath(path2);if(recursive)await rm(resolvedPath,{recursive:!0,force:!0});else await rmdir(resolvedPath);return!0}catch(error){return logger.scoped("storage.fs").error(`Failed to delete directory ${path2}`,error,{path:path2}),!1}}async deleteFiles(paths){let results=await executeBulkOperation(paths,async(path2)=>{if(!await this.deleteFile(path2))throw Error(`Failed to delete: ${path2}`);return path2}),success=[],failed=[];for(let i=0;i<results.length;i++){let result=results[i],originalPath=paths[i];if(result?.status==="fulfilled")success.push(originalPath||"");else failed.push(originalPath||"")}return{success,failed}}async exists(path2){let resolvedPath=resolvePath(path2);return await Bun.file(resolvedPath).exists()}async copyFile(sourcePath,destinationPath){let resolvedSource=resolvePath(sourcePath),resolvedDestination=resolvePath(destinationPath),sourceFile=Bun.file(resolvedSource);if(!await sourceFile.exists())throw createFileManagerError("SOURCE_NOT_FOUND",`Source file not found: ${sourcePath}`,resolvedSource,"copyFile");return await ensureDirectoryExists(extractDirectoryPath(resolvedDestination)),await Bun.write(resolvedDestination,sourceFile)}async moveFile(sourcePath,destinationPath){try{return await this.copyFile(sourcePath,destinationPath),await this.deleteFile(sourcePath),!0}catch(error){return logger.scoped("storage.fs").error("Failed to move file",error,{sourcePath,destinationPath}),!1}}getFormattedFileSize(bytes){return formatFileSize(bytes)}getConfig(){return{...this.config}}updateConfig(newConfig){let validation=validateConfig(newConfig);if(validation.isValid){let mergedConfig=mergeConfig(newConfig,this.config);Object.assign(this.config,mergedConfig)}return validation}validateConfiguration(config){return validateConfig(config)}async createStreamWriter(path2,options={}){return createFileWriter(path2,options)}async writeStream(path2,chunks,options={}){return writeStream(path2,chunks,options)}async appendStream(path2,chunks,options={}){return appendStream(path2,chunks,options)}async copyFileStream(sourcePath,destinationPath,options={}){return copyFileStream(sourcePath,destinationPath,options)}async readFileStream(path2,chunkProcessor){return readFileStream(path2,chunkProcessor)}async setPermissions(path2,mode){return setFilePermissions(path2,mode)}async setPermissionsAdvanced(options){return setFilePermissions(options.path,options.mode)}async getPermissions(path2){return getFilePermissions(path2)}async checkPermissions(path2,requiredMode){return hasPermissions(path2,requiredMode)}async makeFileReadable(path2){return makeReadable(path2)}async makeFileWritable(path2){return makeWritable(path2)}async makeFileExecutable(path2){return makeExecutable(path2)}async makeFileReadOnly(path2){return makeReadOnly(path2)}async setCommonPermission(path2,pattern){return setCommonPermissions(path2,pattern)}async atomicWrite(options){return atomicWrite(options)}async atomicJsonWrite(path2,data,options={}){return atomicJsonWrite(path2,data,options)}async createFileBackup(options){return createBackup(options)}async restoreFileFromBackup(backupPath,targetPath,deleteBackup=!1){return restoreFromBackup(backupPath,targetPath,deleteBackup)}async safeFileUpdate(path2,updateFunction,options={}){return safeUpdate(path2,updateFunction,options)}async batchAtomicOperations(operations){return batchAtomicWrite(operations)}}var init_core=__esm(()=>{init_Logger2();init_atomic();init_permissions();init_streaming();init_utils4()});var fileManager;var init_File=__esm(()=>{init_core();init_utils4();init_core();fileManager=BunFileManager.getInstance()});import{Pool}from"pg";var init_Postgre=__esm(()=>{init_Logger2()});var init_Managers=__esm(()=>{init_Azure();init_Dapr();init_File();init_Postgre();init_Redis()});var exports_utils={};__export(exports_utils,{warnIfAccessTokenTooLargeForCookie:()=>warnIfAccessTokenTooLargeForCookie,validatePayload:()=>validatePayload,validateEnvVariables:()=>validateEnvVariables,toAudit:()=>toAudit,signNewAccessToken:()=>signNewAccessToken,sanitizePayload:()=>sanitizePayload,resolveDbPoolConfig:()=>resolveDbPoolConfig,resolveConfigCredential:()=>resolveConfigCredential,refreshAccessTokenWithLock:()=>refreshAccessTokenWithLock,redactSensitiveOutput:()=>redactSensitiveOutput,parseTokenValuesFromHeaders:()=>parseTokenValuesFromHeaders,parseTimeToSeconds:()=>parseTimeToSeconds2,parseQueryParams:()=>parseQueryParams,isSensitiveOutputKey:()=>isSensitiveOutputKey,initiateRedisManager:()=>initiateRedisManager,getRedisManager:()=>getRedisManager,ensureDatabaseExists:()=>ensureDatabaseExists,decodeUnverifiedSubject:()=>decodeUnverifiedSubject,createAuditLog:()=>createAuditLog,buildPaginationMeta:()=>buildPaginationMeta,COOKIE_SAFE_TOKEN_BYTES:()=>COOKIE_SAFE_TOKEN_BYTES});function parseTokenValuesFromHeaders(headers,tokenNames){let cookies=(headers.get("cookie")?.split(";")||[]).reduce((acc,cookie)=>{let trimmed=cookie.trim(),eqIndex=trimmed.indexOf("=");if(eqIndex>0)acc[trimmed.slice(0,eqIndex)]=trimmed.slice(eqIndex+1);return acc},{});return{access_token:cookies[tokenNames.access_token]||headers.get("authorization")?.split(" ")[1],refresh_token:cookies[tokenNames.refresh_token],session_token:cookies[tokenNames.session_token]}}async function initiateRedisManager(config){if(!config.redis){logger.info("[Redis] Not configured, skipping");return}let rawWithDapr=config.redis.withDapr,resolvedWithDapr=typeof rawWithDapr==="string"?process.env[rawWithDapr]?.toLowerCase()!=="false":rawWithDapr??!1,resolvedKeyPrefix=config.redis.keyPrefix?process.env[config.redis.keyPrefix]??config.redis.keyPrefix:void 0;if(resolvedWithDapr){redisManagerInstance=new RedisManager({withDapr:!0,stateStoreName:config.redis.stateStoreName,...resolvedKeyPrefix?{keyPrefix:resolvedKeyPrefix}:{}});return}let resolvedUrl=config.redis.url?process.env[config.redis.url]:void 0,resolvedHost=config.redis.host?process.env[config.redis.host]:void 0,resolvedPort=config.redis.port?parseInt(process.env[config.redis.port]||"",10):void 0;if((process.env.REDIS_AUTH_MODE||"password")!=="password"){let{getRedisToken:getRedisToken2,getRedisTokenExpiresAt:getRedisTokenExpiresAt2}=await Promise.resolve().then(() => (init_Azure(),exports_Azure)),clientId=process.env.AZURE_CLIENT_ID||"",initialToken=await getRedisToken2();redisManagerInstance=new RedisManager({host:resolvedHost,port:Number.isNaN(resolvedPort)?void 0:resolvedPort,password:initialToken,username:clientId,tls:!0,...resolvedKeyPrefix?{keyPrefix:resolvedKeyPrefix}:{}});let unref=(t)=>{t.unref?.()},scheduleRedisTokenRefresh=()=>{let expiresAt=getRedisTokenExpiresAt2(),refreshIn=Math.max(expiresAt-Date.now()-300000,30000);unref(setTimeout(async()=>{try{let newToken=await getRedisToken2();if(redisManagerInstance)await redisManagerInstance.reauthenticate(clientId,newToken),logger.info("[Redis] Entra ID token refreshed successfully");scheduleRedisTokenRefresh()}catch(err){logger.error("[Redis] Token refresh failed \u2014 retrying in 30s",err),unref(setTimeout(scheduleRedisTokenRefresh,30000))}},refreshIn))};scheduleRedisTokenRefresh()}else{let resolvedPassword=process.env.REDIS_PASSWORD||void 0;redisManagerInstance=new RedisManager({url:resolvedUrl,host:resolvedHost,port:Number.isNaN(resolvedPort)?void 0:resolvedPort,...resolvedPassword?{password:resolvedPassword}:{},...resolvedKeyPrefix?{keyPrefix:resolvedKeyPrefix}:{}})}}function getRedisManager(){return redisManagerInstance}function parseTimeToSeconds2(timeString){if(typeof timeString==="number")return timeString;if(!timeString||timeString.trim()==="")throw Error("Time string cannot be empty");let match=timeString.trim().match(/^(\d+(?:\.\d+)?)\s*([smhdwMy])$/);if(!match||!match[1]||!match[2])throw Error(`Invalid time format: "${timeString}". Expected format: "75s", "10m", "2h", "1d", "1w", "2M", "1y"`);let value=parseFloat(match[1]),unit=match[2],multiplier={s:1,m:60,h:3600,d:86400,w:604800,M:2592000,y:31536000}[unit];if(multiplier===void 0)throw Error(`Unknown time unit: "${unit}"`);let seconds=Math.floor(value*multiplier);if(seconds<=0)throw Error(`Time value must be positive: "${timeString}"`);return seconds}function warnIfAccessTokenTooLargeForCookie(token,jwtClaimsMode){let bytes=Buffer.byteLength(token,"utf-8");if(bytes<=COOKIE_SAFE_TOKEN_BYTES)return;let now=Date.now();if(now-lastOversizeWarnAt<60000)return;lastOversizeWarnAt=now,logger.warn(`[Auth] access_token is ${bytes} bytes \u2014 at/over the ~4KB per-cookie browser limit, so the cookie may be `+"silently dropped (symptom: appears logged-in but requests are unauthenticated). Shrink it: set authorization.jwtClaimsMode:'resolve' (token carries only roles; claims resolved from the Redis claims cache), or deliver the token off-cookie via authentication.accessToken.setHeadersEnabled:false. "+(jwtClaimsMode?`Current jwtClaimsMode='${jwtClaimsMode}'.`:""))}function signNewAccessToken({sessionData,options,refreshTokenId,roles,claims,tenant,claimScopes,resolveMode}){let secretEnvName=options.authentication?.accessToken?.secret;if(!secretEnvName)throw Error("Access token secret env name is not configured");let secret=process.env[secretEnvName];if(!secret)throw Error(`Access token secret env "${secretEnvName}" is not set`);let token=signJWT({subject:sessionData.userId,issuer:options.authentication?.accessToken?.issuer,audience:options.authentication?.accessToken?.audience,algorithm:options.authentication?.accessToken?.algorithm,expiresInSeconds:parseTimeToSeconds2(options.authentication?.accessToken?.expiresIn??"15m"),sessionId:sessionData.id,customClaims:{refreshTokenId,...roles&&roles.length>0?{roles}:{},...!resolveMode&&claims&&claims.length>0?{claims}:{},...!resolveMode&&claimScopes&&Object.keys(claimScopes).length>0?{claimScopes}:{},...tenant?{tenant}:{}}},secret);return warnIfAccessTokenTooLargeForCookie(token,resolveMode?"resolve":options.authorization?.jwtClaimsMode),token}function resolveDbPoolConfig(pool){return{max:pool?.max!=null&&pool.max>0?pool.max:10,idleTimeoutMillis:pool?.idleTimeoutMillis??30000,connectionTimeoutMillis:pool?.connectionTimeoutMillis??1e4,...pool?.maxUses!=null&&pool.maxUses>0?{maxUses:pool.maxUses}:{}}}function toAudit(payload,summary,opts){return payload?{entityName:payload.entity_name,entityId:payload.entity_id===" - "?null:payload.entity_id,operation:payload.operation_type,userId:opts&&"userId"in opts?opts.userId??null:payload.user_id==="unknown"?null:payload.user_id,summary,severity:opts?.severity,category:opts?.category,ipAddress:payload.ip_address,userAgent:payload.user_agent,path:payload.path,query:payload.query}:void 0}function decodeUnverifiedSubject(token){if(!token)return null;try{let parts=token.split(".");if(parts.length<2)return null;let payloadPart=parts[1];if(!payloadPart)return null;let json=Buffer.from(payloadPart,"base64url").toString("utf-8"),decoded=JSON.parse(json);return typeof decoded.sub==="string"&&decoded.sub?decoded.sub:null}catch{return null}}function reconstructBracketParams(query){let result={},arrayGroups={};for(let[key2,value]of Object.entries(query)){let match=key2.match(/^(\w+)\[\d*\]\[(\w+)\]$/),arrayName=match?.[1],prop=match?.[2];if(arrayName&&prop){if(!arrayGroups[arrayName])arrayGroups[arrayName]={};let group=arrayGroups[arrayName];if(!group[prop])group[prop]=[];let arr=group[prop];if(Array.isArray(value))for(let v of value)arr.push(v);else arr.push(value)}else result[key2]=value}for(let[arrayName,props]of Object.entries(arrayGroups)){let propNames=Object.keys(props);if(propNames.length===0)continue;let maxLen=Math.max(...propNames.map((p)=>(props[p]||[]).length)),items=[];for(let i=0;i<maxLen;i++){let item={};for(let p of propNames)item[p]=(props[p]||[])[i];items.push(item)}result[arrayName]=items}return result}function parseQueryParams(query){let q=reconstructBracketParams(query),parseJSONOrPassthrough=(value)=>{if(value===void 0||value===null)return;if(typeof value==="object")return value;if(typeof value==="string")try{return JSON.parse(value)}catch{return}return},DEFAULT_PAGE_SIZE=20,MAX_PAGE_SIZE=200,rawPage=q.page?parseInt(q.page,10):1,page=Number.isFinite(rawPage)&&rawPage>0?rawPage:1,rawLimit=q.limit?parseInt(q.limit,10):20,limit=Number.isFinite(rawLimit)&&rawLimit>0?Math.min(rawLimit,200):20,rawOffset=q.offset?parseInt(q.offset,10):(page-1)*limit,offset=Number.isFinite(rawOffset)&&rawOffset>=0?rawOffset:0;return{page,limit,offset,search:q.search,searchFields:q.searchFields?q.searchFields.split(","):void 0,filters:parseJSONOrPassthrough(q.filters),sort:parseJSONOrPassthrough(q.sort),select:q.select?q.select.split(","):void 0,with:parseJSONOrPassthrough(q.with),distinct:q.distinct==="true",distinctOn:q.distinctOn?q.distinctOn.split(","):void 0}}function buildPaginationMeta(page,limit,offset,totalItems){let totalPages=Math.ceil(totalItems/limit),hasNextPage=page<totalPages,hasPrevPage=page>1;return{page,limit,offset,totalItems,totalPages,hasNextPage,hasPrevPage,nextPage:hasNextPage?page+1:null,prevPage:hasPrevPage?page-1:null}}function getBaseTypeValidator(type){let stringTypes=["varchar","char","text","uuid","citext","bit","varbit"],numberTypes=["integer","smallint","bigint","serial","smallserial","bigserial","real","doublePrecision","numeric","decimal"],booleanTypes=["boolean"];if(stringTypes.includes(type))return(v)=>({valid:typeof v==="string",expectedType:"string"});if(numberTypes.includes(type))return(v)=>({valid:typeof v==="number",expectedType:"number"});if(booleanTypes.includes(type))return(v)=>({valid:typeof v==="boolean",expectedType:"boolean"});if(type==="json"||type==="jsonb")return(v)=>({valid:typeof v==="object",expectedType:"object"});return()=>({valid:!0,expectedType:"any"})}function validatePayload(payload,columns,isPartial=!1){let errors=[];for(let col7 of columns){let value=payload[col7.name]??payload[col7.name.replace(/_([a-z])/g,(_,l)=>l.toUpperCase())],hasDbDefault=col7.default!==void 0||!!col7.defaultRaw||!!col7.generatedByDefaultAsIdentity||!!col7.generatedAlwaysAsIdentity||!!col7.generatedAlwaysAs,isRequired=col7.notNull&&!col7.nullable&&!hasDbDefault;if(value===void 0||value===null){if(isRequired&&!isPartial)errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} is required`});continue}if(col7.array){let baseValidator=getBaseTypeValidator(col7.type),expectedType=baseValidator(void 0).expectedType;if(!Array.isArray(value)){errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be an array of ${expectedType}`});continue}if(value.some((el)=>el!==null&&!baseValidator(el).valid))errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be an array of ${expectedType}`});continue}let typeCheck=getBaseTypeValidator(col7.type)(value);if(!typeCheck.valid){errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be of type ${typeCheck.expectedType}`});continue}if(typeof value==="string"){let len=value.length;if(col7.length&&len>col7.length)errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} exceeds max length of ${col7.length}`});if(col7.validation?.minLength&&len<col7.validation.minLength)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at least ${col7.validation.minLength} characters`});if(col7.validation?.maxLength&&len>col7.validation.maxLength)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at most ${col7.validation.maxLength} characters`});if(col7.validation?.pattern){if(!new RegExp(col7.validation.pattern).test(value))errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} does not match required pattern`})}if(col7.validation?.format){let formatRegex=FORMAT_PATTERNS[col7.validation.format];if(formatRegex&&!formatRegex.test(value))errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be a valid ${col7.validation.format}`})}}if(typeof value==="number"){if(col7.validation?.min!==void 0&&value<col7.validation.min)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at least ${col7.validation.min}`});if(col7.validation?.max!==void 0&&value>col7.validation.max)errors.push({field:col7.name,message:col7.validation.customMessage||`${col7.name} must be at most ${col7.validation.max}`})}if(col7.enumValues&&col7.enumValues.length>0){if(!col7.enumValues.includes(value))errors.push({field:col7.name,message:col7.validation?.customMessage||`${col7.name} must be one of: ${col7.enumValues.join(", ")}`})}}return{valid:errors.length===0,errors}}function escapeHtml(str3){return str3.replace(/[&<>"'`=/]/g,(char)=>HTML_ENTITIES[char]||char)}function stripTags(str3){return str3.replace(/<[^>]*>/g,"")}function normalizeEmail(email){let parts=email.split("@"),localPart=parts[0],domain=parts[1];if(!localPart||!domain)return email;let beforePlus=localPart.split("+")[0];if(!beforePlus)return email;return`${beforePlus.replace(/\./g,"")}@${domain.toLowerCase()}`}function slugify(str3){return str3.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")}function applySanitizer(value,sanitizer){if(value===null||value===void 0)return value;switch(sanitizer){case"trim":return typeof value==="string"?value.trim():value;case"lowercase":return typeof value==="string"?value.toLowerCase():value;case"uppercase":return typeof value==="string"?value.toUpperCase():value;case"escapeHtml":return typeof value==="string"?escapeHtml(value):value;case"stripTags":return typeof value==="string"?stripTags(value):value;case"normalizeEmail":return typeof value==="string"?normalizeEmail(value):value;case"toNumber":if(typeof value==="number")return value;if(typeof value==="string"){let num2=Number(value);return Number.isNaN(num2)?value:num2}return value;case"toBoolean":if(typeof value==="boolean")return value;if(typeof value==="string"){let lower=value.toLowerCase();if(lower==="true"||lower==="1"||lower==="yes")return!0;if(lower==="false"||lower==="0"||lower==="no")return!1}if(typeof value==="number")return value!==0;return value;case"slugify":return typeof value==="string"?slugify(value):value;default:return value}}function isSensitiveOutputKey(key2){return SENSITIVE_OUTPUT_KEYS.has(key2)}function redactSensitiveOutput(row){return scrubSensitive(row)}function scrubSensitive(value){if(value===null||typeof value!=="object")return value;if(Array.isArray(value))return value.map(scrubSensitive);let proto=Object.getPrototypeOf(value);if(proto!==Object.prototype&&proto!==null)return value;let out={};for(let[k,v]of Object.entries(value)){if(SENSITIVE_OUTPUT_KEYS.has(k))continue;out[k]=scrubSensitive(v)}return out}function sanitizePayload(payload,columns,opts){let sanitized={},toCamel2=(s)=>s.replace(/_([a-z])/g,(_,l)=>l.toUpperCase()),SERVER_MANAGED=new Set(["id","created_at","updated_at","created_by","updated_by","createdAt","updatedAt","createdBy","updatedBy"]),PROTECTED_SENSITIVE=new Set(["is_god","password","password_hash","email_verified","verified_at","email_verified_at","is_locked","locked_until","failed_login_attempts","login_count","last_login_at","email_verification_token","email_verification_sent_at","email_verification_expires_at","password_reset_token","password_reset_expires_at","password_reset_sent_at","magic_link_token","two_factor_secret"]),STORAGE_MANAGED=opts?.isFormData?new Set(["path","name","original_name","originalName","extension","size","mime_type","mimeType","uploaded_by","uploadedBy"]):null;for(let key2 of Object.keys(payload)){if(SERVER_MANAGED.has(key2))continue;if(STORAGE_MANAGED?.has(key2))continue;let value=payload[key2],snakeKey=key2.replace(/[A-Z]/g,(l)=>`_${l.toLowerCase()}`);if(PROTECTED_SENSITIVE.has(snakeKey))continue;let col7=columns.find((c)=>c.name===key2||c.name===snakeKey);if(col7?.readOnly)continue;if(col7?.sanitize&&col7.sanitize.length>0)for(let sanitizer of col7.sanitize)value=applySanitizer(value,sanitizer);if(col7&&(col7.type==="timestamp"||col7.type==="timestamptz"||col7.type==="date")&&typeof value==="string"){let parsed=new Date(value);if(!Number.isNaN(parsed.getTime()))value=parsed}let normalizedKey=key2.includes("_")?toCamel2(key2):key2;sanitized[normalizedKey]=value}return sanitized}function createAuditLog(db,auditTable,entry){if(!db||!auditTable)return;let logEntry={user_id:entry.user_id,entity_name:entry.entity_name,entity_id:entry.entity_id,operation:entry.operation,old_data:entry.old_data??null,new_data:entry.new_data??null,timestamp:new Date().toISOString()};db.insert(auditTable).values(logEntry).execute().catch((err)=>{logger.error("[Audit] Database audit write failed",err,{entity:logEntry.entity_name,operation:logEntry.operation})})}async function refreshAccessTokenWithLock(userId,sessionId,generateToken){let redis=new RedisManager,lockKey=`${REFRESH_LOCK_PREFIX}${userId}`,cacheKey=`${ACCESS_TOKEN_CACHE_PREFIX}${userId}:${sessionId}`,cachedResult=await redis.read(cacheKey);if(cachedResult.success&&cachedResult.data)return{success:!0,accessToken:cachedResult.data,fromCache:!0};let lockResult=await redis.acquireLock(lockKey,LOCK_TTL_SECONDS);if(!lockResult.success)return{success:!1,error:lockResult.error};if(lockResult.data)try{let newToken=generateToken();return await redis.create(cacheKey,newToken,ACCESS_TOKEN_CACHE_TTL_SECONDS),{success:!0,accessToken:newToken,fromCache:!1}}finally{await redis.releaseLock(lockKey)}let waitResult=await redis.waitForLock(lockKey,LOCK_WAIT_TIMEOUT_MS,50);if(!waitResult.success)return{success:!1,error:waitResult.error};if(!waitResult.data)return{success:!1,error:"Lock wait timeout"};let newCachedResult=await redis.read(cacheKey);if(newCachedResult.success&&newCachedResult.data)return{success:!0,accessToken:newCachedResult.data,fromCache:!0};let fallbackToken=generateToken();return await redis.create(cacheKey,fallbackToken,ACCESS_TOKEN_CACHE_TTL_SECONDS),{success:!0,accessToken:fallbackToken,fromCache:!1}}function resolveConfigCredential(value){if(!value)return;let fromEnv=process.env[value];if(fromEnv)return fromEnv;if(/^[A-Z][A-Z0-9_]{2,}$/.test(value))return;return value}function validateEnvVariables(config){let errors=[],resolved={},databaseAuthMode=process.env.DATABASE_AUTH_MODE||"password",redisAuthMode=process.env.REDIS_AUTH_MODE||"password";resolved.databaseAuthMode=databaseAuthMode,resolved.redisAuthMode=redisAuthMode;let entraIdModes=["managed_identity","service_principal"];if(entraIdModes.includes(databaseAuthMode)||entraIdModes.includes(redisAuthMode)){if(!process.env.AZURE_CLIENT_ID)logger.warn("[Config] AZURE_CLIENT_ID is not set. Required for user-assigned managed identity.");if([databaseAuthMode,redisAuthMode].filter((m)=>m==="service_principal").length>0){if(!process.env.AZURE_TENANT_ID)errors.push({field:"azure.tenantId",envName:"AZURE_TENANT_ID",message:"AZURE_TENANT_ID is required for service_principal auth mode."});if(!process.env.AZURE_CLIENT_SECRET)errors.push({field:"azure.clientSecret",envName:"AZURE_CLIENT_SECRET",message:"AZURE_CLIENT_SECRET is required for service_principal auth mode."})}}if(config.database?.url){let envValue=process.env[config.database.url];if(!envValue)errors.push({field:"database.url",envName:config.database.url,message:`Environment variable "${config.database.url}" is not set. Please set it in your .env file.`});else resolved.databaseUrl=envValue}let validationWithDapr=typeof config.redis?.withDapr==="string"?process.env[config.redis.withDapr]?.toLowerCase()!=="false":config.redis?.withDapr??!1;if(config.redis&&!validationWithDapr)if(config.redis.url){let envValue=process.env[config.redis.url];if(!envValue)errors.push({field:"redis.url",envName:config.redis.url,message:`Environment variable "${config.redis.url}" is not set. Please set it in your .env file.`});else resolved.redisUrl=envValue}else{if(config.redis.host){if(!process.env[config.redis.host])errors.push({field:"redis.host",envName:config.redis.host,message:`Environment variable "${config.redis.host}" is not set. Please set it in your .env file.`})}if(config.redis.port){if(!process.env[config.redis.port])errors.push({field:"redis.port",envName:config.redis.port,message:`Environment variable "${config.redis.port}" is not set. Please set it in your .env file.`})}}if(config.authentication?.enabled){if(!config.authentication.mode)errors.push({field:"authentication.mode",envName:"",message:'authentication.mode is required when authentication is enabled. Use "full" for IDP/standalone services, "consumer" for resource servers.'});if(config.authentication.accessToken?.secret){let envValue=process.env[config.authentication.accessToken.secret];if(!envValue)errors.push({field:"authentication.accessToken.secret",envName:config.authentication.accessToken.secret,message:`Environment variable "${config.authentication.accessToken.secret}" is not set. Please set it in your .env file.`});else resolved.accessTokenSecret=envValue}else errors.push({field:"authentication.accessToken.secret",envName:"",message:"authentication.accessToken.secret is required when authentication is enabled."});let isConsumerMode=config.authentication.mode==="consumer";if(config.authentication.refreshToken?.secret){let envValue=process.env[config.authentication.refreshToken.secret];if(!envValue)errors.push({field:"authentication.refreshToken.secret",envName:config.authentication.refreshToken.secret,message:`Environment variable "${config.authentication.refreshToken.secret}" is not set. Please set it in your .env file.`});else resolved.refreshTokenSecret=envValue}else if(!isConsumerMode)errors.push({field:"authentication.refreshToken.secret",envName:"",message:"authentication.refreshToken.secret is required when authentication is enabled."});if(config.authentication.sessionToken?.secret){let envValue=process.env[config.authentication.sessionToken.secret];if(!envValue)errors.push({field:"authentication.sessionToken.secret",envName:config.authentication.sessionToken.secret,message:`Environment variable "${config.authentication.sessionToken.secret}" is not set. Please set it in your .env file.`});else resolved.sessionTokenSecret=envValue}else if(!isConsumerMode)errors.push({field:"authentication.sessionToken.secret",envName:"",message:"authentication.sessionToken.secret is required when authentication is enabled."})}if(config.authentication?.oauth?.enabled&&config.authentication.oauth.providers){let resolvedProviders={};for(let[providerName,providerConfig]of Object.entries(config.authentication.oauth.providers)){if(!providerConfig)continue;let{clientId:clientIdEnvName,clientSecret:clientSecretEnvName,redirectUri:redirectUriEnvName,tenantId:tenantIdEnvName}=providerConfig,clientId=resolveConfigCredential(clientIdEnvName),clientSecret=resolveConfigCredential(clientSecretEnvName),redirectUri=resolveConfigCredential(redirectUriEnvName),tenantId=resolveConfigCredential(tenantIdEnvName),credentialsMayComeFromDb=config.secrets?.enabled===!0;if(!clientId&&!credentialsMayComeFromDb)errors.push({field:`authentication.oauth.providers.${providerName}.clientId`,envName:clientIdEnvName,message:`Environment variable "${clientIdEnvName}" is not set (OAuth ${providerName} clientId).`});if(!clientSecret&&!credentialsMayComeFromDb)errors.push({field:`authentication.oauth.providers.${providerName}.clientSecret`,envName:clientSecretEnvName,message:`Environment variable "${clientSecretEnvName}" is not set (OAuth ${providerName} clientSecret).`});if(clientId&&clientSecret||credentialsMayComeFromDb)resolvedProviders[providerName]={clientId:clientId??"",clientSecret:clientSecret??"",redirectUri:redirectUri??"",tenantId,scopes:providerConfig.scopes,authorizationUrl:providerConfig.authorizationUrl,tokenUrl:providerConfig.tokenUrl,userInfoUrl:providerConfig.userInfoUrl,extraAuthParams:providerConfig.extraAuthParams}}if(Object.keys(resolvedProviders).length>0)resolved.oauthProviders=resolvedProviders}return{valid:errors.length===0,errors,resolved}}async function ensureDatabaseExists(databaseUrl,logger2,authMode){let{Pool:Pool2}=await import("pg"),targetDb=new URL(databaseUrl).pathname.replace("/","");if(!targetDb)return;let adminUrl=new URL(databaseUrl);adminUrl.pathname="/postgres";let pool;if(authMode&&authMode!=="password"){let{getPostgresToken:getPostgresToken2}=await Promise.resolve().then(() => (init_Azure(),exports_Azure));pool=new Pool2({connectionString:adminUrl.toString(),password:getPostgresToken2,ssl:{rejectUnauthorized:!0}})}else pool=new Pool2({connectionString:adminUrl.toString()});try{if((await pool.query("SELECT 1 FROM pg_database WHERE datname = $1",[targetDb])).rowCount===0)logger2.info(`[Database] Creating database "${targetDb}"...`),await pool.query(`CREATE DATABASE "${targetDb}" TEMPLATE template0`),logger2.info(`[Database] Database "${targetDb}" created successfully`);else logger2.info(`[Database] Database "${targetDb}" exists`)}catch(err){let message=err instanceof Error?err.message:String(err);logger2.warn(`[Database] Could not auto-create database: ${message}`)}finally{await pool.end()}}var redisManagerInstance=null,COOKIE_SAFE_TOKEN_BYTES=3500,lastOversizeWarnAt=0,FORMAT_PATTERNS,HTML_ENTITIES,SENSITIVE_OUTPUT_KEYS,ACCESS_TOKEN_CACHE_PREFIX="access_token:",REFRESH_LOCK_PREFIX="refresh_lock:",LOCK_TTL_SECONDS=5,LOCK_WAIT_TIMEOUT_MS=3000,ACCESS_TOKEN_CACHE_TTL_SECONDS=60;var init_utils5=__esm(()=>{init_Managers();init_Services();init_Logger2();FORMAT_PATTERNS={email:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,url:/^https?:\/\/.+/,uuid:/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,date:/^\d{4}-\d{2}-\d{2}$/,datetime:/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,time:/^\d{2}:\d{2}:\d{2}$/,uri:/^[a-z][a-z0-9+.-]*:/i,ipv4:/^(\d{1,3}\.){3}\d{1,3}$/,ipv6:/^([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$/i};HTML_ENTITIES={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;","`":"&#96;","=":"&#x3D;"};SENSITIVE_OUTPUT_KEYS=new Set(["password","passwordHash","password_hash","emailVerificationToken","email_verification_token","emailVerificationTokenExpiresAt","email_verification_token_expires_at","passwordResetToken","password_reset_token","refreshTokenHash","refresh_token_hash"])});import{eq as eq13}from"drizzle-orm";import{pgSchema}from"drizzle-orm/pg-core";class TenantRegistry{db;logger;mainSchemaName;mainSchemaTables;mainSchemaRelations;createAllTablesForSchema;createAllRelationsForSchema;appId;authMode;tenantResolution;tenantHeader;redisCacheTtlSeconds;defaultTrustedSources;idpUrl;allowDataLoss;onTenantProvisioned;tenantsBySubdomain=new Map;tenantsBySchemaName=new Map;tenantsById=new Map;schemaContexts=new Map;tenantFeatures=new Map;tenantsByDomainHostname=new Map;constructor(config){this.db=config.db,this.logger=config.logger,this.mainSchemaName=config.mainSchemaName,this.mainSchemaTables=config.mainSchemaTables,this.mainSchemaRelations=config.mainSchemaRelations,this.createAllTablesForSchema=config.createAllTablesForSchema,this.createAllRelationsForSchema=config.createAllRelationsForSchema,this.appId=config.appId,this.authMode=config.authMode,this.tenantResolution=config.tenantResolution,this.tenantHeader=config.tenantHeader,this.redisCacheTtlSeconds=config.redisCacheTtlSeconds,this.defaultTrustedSources=config.defaultTrustedSources,this.idpUrl=config.idpUrl,this.allowDataLoss=config.allowDataLoss===!0,this.onTenantProvisioned=config.onTenantProvisioned}async initialize(){this.logger.info("[TenantRegistry] Initializing..."),this.schemaContexts.set(this.mainSchemaName,{schemaName:this.mainSchemaName,schemaTables:this.mainSchemaTables,schemaRelations:this.mainSchemaRelations,tenant:null});let tenants=await this.loadTenantsFromDb();this.logger.info(`[TenantRegistry] Loaded ${tenants.length} tenants from database`);let features=await this.loadTenantFeaturesFromDb();this.logger.info(`[TenantRegistry] Loaded ${features.length} tenant feature mappings`);for(let tenant of tenants)this.indexTenant(tenant);for(let feature of features){let existing=this.tenantFeatures.get(feature.tenantId)||[];existing.push(feature),this.tenantFeatures.set(feature.tenantId,existing)}let activeTenants=tenants.filter((t)=>t.status==="active");for(let tenant of activeTenants)await this.buildSchemaContext(tenant);await this.loadDomainHostnames(),this.logger.info(`[TenantRegistry] Initialized with ${activeTenants.length} active tenant schemas + main schema`)}resolveFromRequest(request,vettedClientIp){let url=new URL(request.url),host=request.headers.get("host")||url.host||"",normalizedHost=normalizeHost(host);if(normalizedHost){let byHostname=this.tenantsByDomainHostname.get(normalizedHost);if(byHostname)return this.validateAndReturnTenant(byHostname)}if(this.tenantResolution==="subdomain"||this.tenantResolution==="both"){let subdomain=extractSubdomain(host);if(subdomain){let tenant=this.tenantsBySubdomain.get(subdomain);if(tenant)return this.validateAndReturnTenant(tenant);return{resolved:!1,error:`Tenant not found for subdomain: ${subdomain}`,statusCode:404}}}if(this.tenantResolution==="header"||this.tenantResolution==="both"){let tenantIdOrSubdomain=request.headers.get(this.tenantHeader);if(tenantIdOrSubdomain){let result=this.resolveFromHeader(tenantIdOrSubdomain,request,vettedClientIp);if(result)return result}}let mainContext=this.schemaContexts.get(this.mainSchemaName);if(mainContext)return{resolved:!0,context:mainContext};return{resolved:!1,error:"No tenant could be resolved and main schema is unavailable",statusCode:500}}getSchemaContext(schemaName){return this.schemaContexts.get(schemaName)}getMainContext(){let ctx=this.schemaContexts.get(this.mainSchemaName);if(!ctx)throw Error("[TenantRegistry] Main schema context not initialized");return ctx}getActiveTenants(){return Array.from(this.tenantsById.values()).filter((t)=>t.status==="active")}getTenantById(id){return this.tenantsById.get(id)}getTenantBySchemaName(schemaName){return this.tenantsBySchemaName.get(schemaName)}getTenantFeatures(tenantId){return this.tenantFeatures.get(tenantId)||[]}isTenantFeatureEnabled(tenantId,featureName){return(this.tenantFeatures.get(tenantId)||[]).find((f)=>f.featureName===featureName)?.enabled??!1}getTenantIdsWithFeature(featureName){let result=[];for(let[tenantId,features]of this.tenantFeatures)if(features.find((f)=>f.featureName===featureName&&f.enabled))result.push(tenantId);return result}getSchemaNamesWithFeature(featureName){let tenantIds=this.getTenantIdsWithFeature(featureName),schemaNames=[];for(let tenantId of tenantIds){let tenant=this.tenantsById.get(tenantId);if(tenant&&tenant.status==="active")schemaNames.push(tenant.schemaName)}return schemaNames}getAllSchemaNames(){return Array.from(this.schemaContexts.keys())}async provisionTenant(tenant){this.logger.info(`[TenantRegistry] Provisioning tenant: ${tenant.subdomain} \u2192 ${tenant.schemaName}`),await ensureSchemaExists(this.db,tenant.schemaName);let context=await this.buildSchemaContext(tenant);if(this.indexTenant(tenant),await this.syncSchemaToDb(context),this.onTenantProvisioned)try{await this.onTenantProvisioned(context)}catch(err){let msg=err instanceof Error?err.message:String(err);this.logger.warn(`[TenantRegistry] onTenantProvisioned hook failed for ${tenant.schemaName}: ${msg}`)}return this.logger.info(`[TenantRegistry] Tenant provisioned: ${tenant.subdomain}`),context}async syncSchemaToDb(context){let{pushSchema}=await import("drizzle-kit/api"),{applySchemaPush:applySchemaPush2}=await Promise.resolve().then(() => (init_schema(),exports_schema)),targetSchema=pgSchema(context.schemaName);try{let push=await pushSchema({schema:targetSchema,...context.schemaTables},this.db,[context.schemaName]);if(await applySchemaPush2(push,{schemaName:context.schemaName,allowDataLoss:this.allowDataLoss,logger:this.logger}))this.logger.info(`[TenantRegistry] Schema sync completed for: ${context.schemaName}`)}catch(err){let msg=err instanceof Error?err.message:String(err);this.logger.warn(`[TenantRegistry] Schema sync warning for ${context.schemaName}: ${msg}`)}}async syncAllSchemas(){for(let[schemaName,context]of this.schemaContexts)this.logger.info(`[TenantRegistry] Syncing schema: ${schemaName}`),await ensureSchemaExists(this.db,schemaName),await this.syncSchemaToDb(context)}async invalidateCache(subdomain){try{let{getRedisManager:getRedisManager2}=await Promise.resolve().then(() => (init_utils5(),exports_utils)),redis=getRedisManager2();if(redis)await redis.remove(`tenant:${subdomain}`),this.logger.info(`[TenantRegistry] Cache invalidated for: ${subdomain}`)}catch{}}async refreshTenant(tenantId){let tenantsTable=this.mainSchemaTables.tenants;if(!tenantsTable)return;let row=(await this.db.select().from(tenantsTable).where(eq13(tenantsTable.id,tenantId)).limit(1))[0];if(!row)return;let tenant=rowToTenantRecord(row),oldTenant=this.tenantsById.get(tenantId);if(oldTenant)this.tenantsBySubdomain.delete(oldTenant.subdomain),this.tenantsBySchemaName.delete(oldTenant.schemaName);if(this.indexTenant(tenant),tenant.status==="active")await this.buildSchemaContext(tenant);else this.schemaContexts.delete(tenant.schemaName);if(oldTenant)await this.invalidateCache(oldTenant.subdomain);await this.invalidateCache(tenant.subdomain)}async initializeFromIdp(){if(!this.idpUrl){this.logger.error("[TenantRegistry] Consumer multi-tenant requires idpUrl (IDP_URL)");return}this.logger.info(`[TenantRegistry] Initializing from IDP: ${this.idpUrl}`),this.schemaContexts.set(this.mainSchemaName,{schemaName:this.mainSchemaName,schemaTables:this.mainSchemaTables,schemaRelations:this.mainSchemaRelations,tenant:null});let tenants=await this.loadTenantsFromIdp();this.logger.info(`[TenantRegistry] Fetched ${tenants.length} tenants from IDP`);for(let tenant of tenants)this.indexTenant(tenant);let activeTenants=tenants.filter((t)=>t.status==="active");for(let tenant of activeTenants)await this.buildSchemaContext(tenant);await this.loadDomainHostnames(),this.logger.info(`[TenantRegistry] Consumer initialized with ${activeTenants.length} active tenant schemas + main schema`)}isConsumerMode(){return this.authMode==="consumer"&&!!this.idpUrl}async syncFromIdp(){if(!this.isConsumerMode())return{added:[],removed:[],total:this.tenantsById.size};let tenants=await this.loadTenantsFromIdp(),added=[],removed=[],seenSchemaNames=new Set;for(let tenant of tenants){seenSchemaNames.add(tenant.schemaName);let known=this.tenantsById.get(tenant.id);if(known)this.tenantsBySubdomain.delete(known.subdomain),this.tenantsBySchemaName.delete(known.schemaName);if(this.indexTenant(tenant),tenant.status==="active"){if(!this.schemaContexts.has(tenant.schemaName)){await ensureSchemaExists(this.db,tenant.schemaName);let ctx=await this.buildSchemaContext(tenant);await this.syncSchemaToDb(ctx),added.push(tenant.schemaName),await this.invalidateCache(tenant.subdomain),this.logger.info(`[TenantRegistry] Synced new tenant from IDP: ${tenant.subdomain}`)}}else if(this.schemaContexts.has(tenant.schemaName))this.schemaContexts.delete(tenant.schemaName),removed.push(tenant.schemaName),await this.invalidateCache(tenant.subdomain),this.logger.info(`[TenantRegistry] Deactivated tenant context: ${tenant.subdomain}`)}for(let[schemaName,ctx]of this.schemaContexts){if(schemaName===this.mainSchemaName)continue;if(!seenSchemaNames.has(schemaName)){if(this.schemaContexts.delete(schemaName),removed.push(schemaName),ctx.tenant)this.tenantsBySubdomain.delete(ctx.tenant.subdomain),this.tenantsById.delete(ctx.tenant.id),this.tenantsBySchemaName.delete(schemaName),await this.invalidateCache(ctx.tenant.subdomain)}}return{added,removed,total:tenants.length}}registerDomainHostname(hostname,tenantId){let tenant=this.tenantsById.get(tenantId);if(!tenant)return;this.tenantsByDomainHostname.set(normalizeHost(hostname),tenant)}unregisterDomainHostname(hostname){this.tenantsByDomainHostname.delete(normalizeHost(hostname))}async loadDomainHostnames(){let table=this.mainSchemaTables.domainHostnames;if(!table)return 0;try{let rows=await this.db.select().from(table),count=0;this.tenantsByDomainHostname.clear();for(let row of rows){let status=String(row.status??""),tenantId=String(row.tenantId??row.tenant_id??""),hostname=String(row.normalizedHostname??row.normalized_hostname??"");if(status!=="active"||!tenantId||!hostname)continue;let tenant=this.tenantsById.get(tenantId);if(!tenant)continue;this.tenantsByDomainHostname.set(normalizeHost(hostname),tenant),count++}if(count>0)this.logger.info(`[TenantRegistry] Indexed ${count} active custom hostnames`);return count}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to load custom hostnames: ${msg}`),0}}async loadTenantsFromIdp(){if(!this.idpUrl)return[];try{let response=await fetch(`${this.idpUrl}/tenants`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!response.ok)return this.logger.warn(`[TenantRegistry] IDP tenant fetch failed: ${response.status} ${response.statusText}`),[];return((await response.json())?.data?.items||[]).map((item)=>rowToTenantRecord(item))}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to fetch tenants from IDP: ${msg}`),[]}}async loadTenantsFromDb(){let tenantsTable=this.mainSchemaTables.tenants;if(!tenantsTable)return this.logger.warn("[TenantRegistry] No tenants table found in main schema"),[];try{return(await this.db.select().from(tenantsTable)).map((row)=>rowToTenantRecord(row))}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to load tenants: ${msg}`),[]}}async loadTenantFeaturesFromDb(){let featuresTable=this.mainSchemaTables.tenantFeatures;if(!featuresTable)return this.logger.warn("[TenantRegistry] No tenant_features table found in main schema"),[];try{return(await this.db.select().from(featuresTable)).map((row)=>rowToFeatureRecord(row,parseJsonbToConfig))}catch(err){let msg=err instanceof Error?err.message:String(err);return this.logger.warn(`[TenantRegistry] Failed to load tenant features: ${msg}`),[]}}indexTenant(tenant){if(this.tenantsById.set(tenant.id,tenant),this.tenantsBySubdomain.set(tenant.subdomain,tenant),this.tenantsBySchemaName.set(tenant.schemaName,tenant),tenant.domain)this.tenantsBySubdomain.set(tenant.domain,tenant)}async buildSchemaContext(tenant){let schema=pgSchema(tenant.schemaName),schemaTables=this.createAllTablesForSchema(schema),schemaRelations=this.createAllRelationsForSchema?this.createAllRelationsForSchema(schema):{},context={schemaName:tenant.schemaName,schemaTables,schemaRelations,tenant};return this.schemaContexts.set(tenant.schemaName,context),context}resolveFromHeader(tenantIdOrSubdomain,request,vettedClientIp){let tenant=this.tenantsBySubdomain.get(tenantIdOrSubdomain);if(!tenant)tenant=this.tenantsById.get(tenantIdOrSubdomain);if(!tenant)tenant=this.tenantsBySchemaName.get(tenantIdOrSubdomain);if(!tenant)return{resolved:!1,error:`Tenant not found for header value: ${tenantIdOrSubdomain}`,statusCode:404};if(!isTrustedSource(tenant,request,this.authMode,this.defaultTrustedSources,vettedClientIp))return{resolved:!1,error:`Untrusted source for tenant: ${tenant.subdomain}`,statusCode:403};return this.validateAndReturnTenant(tenant)}validateAndReturnTenant(tenant){if(tenant.status==="suspended")return{resolved:!1,error:`Tenant ${tenant.subdomain} is suspended: ${tenant.suspendedReason||"No reason provided"}`,statusCode:403};if(tenant.status==="provisioning")return{resolved:!1,error:`Tenant ${tenant.subdomain} is still being provisioned`,statusCode:503};if(tenant.status==="archived")return{resolved:!1,error:`Tenant ${tenant.subdomain} has been archived`,statusCode:410};let context=this.schemaContexts.get(tenant.schemaName);if(!context)return{resolved:!1,error:`Schema context not found for tenant: ${tenant.subdomain}`,statusCode:500};return{resolved:!0,context}}}var init_TenantRegistry=__esm(()=>{init_schema()});var init_Tenant=__esm(()=>{init_schema();init_TenantRegistry()});var init_types5=()=>{};import{and as and8,desc as desc2,eq as eq14,inArray as inArray2}from"drizzle-orm";function toCamel2(obj){let result={};for(let[key2,value]of Object.entries(obj)){let camelKey=key2.replace(/_([a-z])/g,(_,c)=>c.toUpperCase());result[camelKey]=value}return result}function fromCamel2(obj){let result={};for(let[key2,value]of Object.entries(obj)){let snakeKey=key2.replace(/[A-Z]/g,(c)=>`_${c.toLowerCase()}`);result[snakeKey]=value}return result}class VerificationService{db;schemaTables;config;logger;onNotificationTrigger;constructor(serviceConfig){this.db=serviceConfig.db,this.schemaTables=serviceConfig.schemaTables,this.config=serviceConfig.config,this.logger=serviceConfig.logger}setNotificationHandler(handler){this.onNotificationTrigger=handler}getConnectedNotificationNodeIds(stepNodeId,steps,edges){let result=new Set,getNeighbors=(nid)=>{let neighbors=[];for(let edge of edges){let{sourceNodeId:src,targetNodeId:tgt}=edge;if(src===nid)neighbors.push(tgt);else if(tgt===nid)neighbors.push(src)}return neighbors},directNeighbors=getNeighbors(stepNodeId);for(let neighborId of directNeighbors)if(steps.find((s)=>s.nodeId===neighborId)?.nodeType==="notification")result.add(neighborId);for(let neighborId of directNeighbors)if(steps.find((s)=>s.nodeId===neighborId)?.nodeType==="verifier"){let verifierNeighbors=getNeighbors(neighborId);for(let vNeighborId of verifierNeighbors){if(vNeighborId===stepNodeId)continue;if(steps.find((s)=>s.nodeId===vNeighborId)?.nodeType==="notification")result.add(vNeighborId)}}let ids=[...result];return this.logger.info(`[Verification] Connected notification nodes for step ${stepNodeId}: [${ids.join(", ")}]`),ids}getTable(name){return resolveSchemaTable(this.schemaTables,name,this.logger)}getCol(table,col7){return table[col7]}async listFlows(entityName){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return[];return(await(entityName?this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"entityName"),entityName)):this.db.select().from(flowsTable))).map((r)=>fromCamel2(r))}async getFlow(flowId){let flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),notifRulesTable=this.getTable("verificationNotificationRules"),notifRecipientsTable=this.getTable("verificationNotificationRecipients"),notifChannelsTable=this.getTable("verificationNotificationChannels");if(!flowsTable)return null;let flowRow=(await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),flowId)).limit(1))[0];if(!flowRow)return null;let flow=fromCamel2(flowRow),steps=(stepsTable?await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),edges=(edgesTable?await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),verifierConfigs=(verifierConfigsTable?await this.db.select().from(verifierConfigsTable).where(eq14(this.getCol(verifierConfigsTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),notifRules=(notifRulesTable?await this.db.select().from(notifRulesTable).where(eq14(this.getCol(notifRulesTable,"flowId"),flowId)):[]).map((r)=>fromCamel2(r)),ruleIds=notifRules.map((r)=>r.id),notifRecipients=(notifRecipientsTable&&ruleIds.length>0?await this.db.select().from(notifRecipientsTable).where(inArray2(this.getCol(notifRecipientsTable,"ruleId"),ruleIds)):[]).map((r)=>fromCamel2(r)),notifChannels=(notifChannelsTable&&ruleIds.length>0?await this.db.select().from(notifChannelsTable).where(inArray2(this.getCol(notifChannelsTable,"ruleId"),ruleIds)):[]).map((r)=>fromCamel2(r));return{flow,graph:{steps,edges,verifier_configs:verifierConfigs,notification_rules:notifRules,notification_recipients:notifRecipients,notification_channels:notifChannels}}}async saveFlow(params){let flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),notifRulesTable=this.getTable("verificationNotificationRules"),notifRecipientsTable=this.getTable("verificationNotificationRecipients"),notifChannelsTable=this.getTable("verificationNotificationChannels");if(!flowsTable||!stepsTable||!edgesTable)throw Error("Verification tables not configured");let existingFlows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),params.flow_id)).limit(1),flowId=params.flow_id;if(existingFlows.length>0)await this.db.update(flowsTable).set(toCamel2({entity_name:params.entity_name,name:params.name,description:params.description||null,trigger_on:params.trigger_on,trigger_fields:params.trigger_fields||null,is_draft:params.is_draft,viewport:params.viewport||null})).where(eq14(this.getCol(flowsTable,"id"),flowId));else{let[newFlow]=await this.db.insert(flowsTable).values(toCamel2({id:flowId,entity_name:params.entity_name,name:params.name,description:params.description||null,trigger_on:params.trigger_on,trigger_fields:params.trigger_fields||null,is_draft:params.is_draft,viewport:params.viewport||null})).returning();flowId=newFlow.id}if(await this.db.delete(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),flowId)),edgesTable)await this.db.delete(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),flowId));if(verifierConfigsTable)await this.db.delete(verifierConfigsTable).where(eq14(this.getCol(verifierConfigsTable,"flowId"),flowId));if(notifRulesTable){let oldRuleIds=(await this.db.select().from(notifRulesTable).where(eq14(this.getCol(notifRulesTable,"flowId"),flowId))).map((r)=>r.id);if(oldRuleIds.length>0){if(notifRecipientsTable)for(let rid of oldRuleIds)await this.db.delete(notifRecipientsTable).where(eq14(this.getCol(notifRecipientsTable,"ruleId"),rid));if(notifChannelsTable)for(let rid of oldRuleIds)await this.db.delete(notifChannelsTable).where(eq14(this.getCol(notifChannelsTable,"ruleId"),rid))}await this.db.delete(notifRulesTable).where(eq14(this.getCol(notifRulesTable,"flowId"),flowId))}let{graph}=params;if(graph.steps.length>0)await this.db.insert(stepsTable).values(graph.steps.map((s)=>toCamel2({flow_id:flowId,entity_name:params.entity_name,node_id:s.node_id,node_type:s.node_type,step_order:s.step_order,name:s.name||null,description:s.description||null,position_x:s.position_x,position_y:s.position_y,width:s.width||null,height:s.height||null,style:s.style||null,data:s.data||null})));if(graph.edges.length>0&&edgesTable)await this.db.insert(edgesTable).values(graph.edges.map((e)=>toCamel2({flow_id:flowId,edge_id:e.edge_id,source_node_id:e.source_node_id,target_node_id:e.target_node_id,source_handle:e.source_handle||null,target_handle:e.target_handle||null,edge_type:e.edge_type,label:e.label||null,condition:e.condition||null,style:e.style||null,animated:e.animated})));if(graph.verifier_configs.length>0&&verifierConfigsTable)await this.db.insert(verifierConfigsTable).values(graph.verifier_configs.map((vc)=>toCamel2({flow_id:flowId,node_id:vc.node_id,verifier_type:vc.verifier_type,verifier_user_id:vc.verifier_user_id||null,verifier_role:vc.verifier_role||null,require_signature:vc.require_signature,all_must_approve:vc.all_must_approve})));if(this.logger.info(`[Verification] Save: ${graph.notification_rules.length} rules, ${graph.notification_recipients.length} recipients, ${graph.notification_channels.length} channels`),graph.notification_rules.length>0&&notifRulesTable)for(let rule of graph.notification_rules){this.logger.info(`[Verification] Saving notification rule: node_id=${rule.node_id}, trigger=${rule.trigger}, title_template=${JSON.stringify(rule.title_template)}, body_template=${JSON.stringify(rule.body_template)}`);let[insertedRule]=await this.db.insert(notifRulesTable).values(toCamel2({flow_id:flowId,node_id:rule.node_id,trigger:rule.trigger,title_template:rule.title_template||null,body_template:rule.body_template||null,starts_at:rule.starts_at||null,expires_at:rule.expires_at||null})).returning(),ruleId=insertedRule.id,ruleRecipients=graph.notification_recipients.filter((r)=>r.rule_id===rule.node_id);if(ruleRecipients.length>0&&notifRecipientsTable)await this.db.insert(notifRecipientsTable).values(ruleRecipients.map((r)=>toCamel2({rule_id:ruleId,recipient_type:r.recipient_type,recipient_user_id:r.recipient_user_id||null,recipient_role:r.recipient_role||null})));let ruleChannels=graph.notification_channels.filter((c)=>c.rule_id===rule.node_id);if(ruleChannels.length>0&&notifChannelsTable)await this.db.insert(notifChannelsTable).values(ruleChannels.map((c)=>toCamel2({rule_id:ruleId,channel:c.channel})))}return this.logger.info(`[Verification] Flow saved: ${flowId} for ${params.entity_name}`),{success:!0,flow_id:flowId}}async publishFlow(flowId){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return{success:!1,message:"Flow table not configured"};return await this.db.update(flowsTable).set(toCamel2({is_draft:!1,published_at:new Date})).where(eq14(this.getCol(flowsTable,"id"),flowId)),this.logger.info(`[Verification] Flow published: ${flowId}`),{success:!0,message:"Flow published"}}async deleteFlow(flowId){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return{success:!1,message:"Flow table not configured"};return await this.db.delete(flowsTable).where(eq14(this.getCol(flowsTable,"id"),flowId)),this.logger.info(`[Verification] Flow deleted: ${flowId}`),{success:!0,message:"Flow deleted"}}async startFlow(params){let flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),instancesTable=this.getTable("verificationInstances"),requirementsTable=this.getTable("verificationRequirements"),userRolesTable=this.getTable("userRoles");if(!flowsTable||!stepsTable||!edgesTable||!instancesTable||!requirementsTable)return{success:!1,message:"Verification tables not configured"};let flow=(await this.db.select().from(flowsTable).where(and8(eq14(this.getCol(flowsTable,"id"),params.flow_id),eq14(this.getCol(flowsTable,"isDraft"),!1))).limit(1))[0];if(!flow)return{success:!1,message:"Published flow not found"};if((await this.db.select().from(instancesTable).where(and8(eq14(this.getCol(instancesTable,"entityName"),params.entity_name),eq14(this.getCol(instancesTable,"entityId"),params.entity_id),eq14(this.getCol(instancesTable,"status"),"active"))).limit(1)).length>0)return{success:!1,message:"An active verification instance already exists for this entity"};let steps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),params.flow_id)),edges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),params.flow_id)),stepNodes=steps.filter((s)=>s.nodeType==="step");if(stepNodes.sort((a,b)=>a.stepOrder-b.stepOrder),stepNodes.length===0)return{success:!1,message:"Flow has no step nodes"};let[instance]=await this.db.insert(instancesTable).values(toCamel2({flow_id:params.flow_id,entity_name:params.entity_name,entity_id:params.entity_id,started_by:params.started_by||null,status:"active",current_step_order:1,started_at:new Date})).returning(),instanceId=instance.id;if(await this.materializeRequirementsForStep(instanceId,params.flow_id,params.entity_name,params.entity_id,1,steps,edges,verifierConfigsTable,requirementsTable,userRolesTable),this.onNotificationTrigger){let firstStep=stepNodes[0],firstStepNodeId=firstStep?.nodeId,connectedNotifIds=firstStepNodeId?this.getConnectedNotificationNodeIds(firstStepNodeId,steps,edges):[],ctx={flow_name:flow.name,step_name:firstStep?.name||"Step 1",step_order:1,total_steps:stepNodes.length};if(connectedNotifIds.length>0)for(let notifNodeId of connectedNotifIds)await this.onNotificationTrigger({trigger:"on_flow_started",flow_id:params.flow_id,entity_name:params.entity_name,entity_id:params.entity_id,node_id:notifNodeId,context:ctx});else await this.onNotificationTrigger({trigger:"on_flow_started",flow_id:params.flow_id,entity_name:params.entity_name,entity_id:params.entity_id,context:ctx})}return this.logger.info(`[Verification] Flow started: instance ${instanceId} for ${params.entity_name}:${params.entity_id}`),{success:!0,instance_id:instanceId,message:"Flow started"}}async materializeRequirementsForStep(instanceId,flowId,entityName,entityId,stepOrder,steps,edges,verifierConfigsTable,requirementsTable,userRolesTable){if(!requirementsTable)return;let stepNode=steps.find((s)=>s.nodeType==="step"&&s.stepOrder===stepOrder);if(!stepNode)return;let stepNodeId=stepNode.nodeId,verifierNodeIds=edges.filter((e)=>e.targetNodeId===stepNodeId).map((e)=>e.sourceNodeId),verifierNodes=steps.filter((s)=>s.nodeType==="verifier"&&verifierNodeIds.includes(s.nodeId));if(verifierNodes.length===0)return;let verifierConfigs=[];if(verifierConfigsTable)verifierConfigs=await this.db.select().from(verifierConfigsTable).where(eq14(this.getCol(verifierConfigsTable,"flowId"),flowId));for(let verifierNode of verifierNodes){let nodeId=verifierNode.nodeId,config=verifierConfigs.find((vc)=>vc.nodeId===nodeId);if(!config)continue;let{verifierType,allMustApprove}=config;if(verifierType==="role"&&allMustApprove&&userRolesTable){let rolesTable=this.getTable("roles");if(rolesTable){let rolesCols=rolesTable,userRolesCols=userRolesTable,role=(await this.db.select().from(rolesTable).where(eq14(rolesCols.name,config.verifierRole)).limit(1))[0];if(role){let usersWithRole=await this.db.select({user_id:userRolesCols.userId}).from(userRolesTable).where(eq14(userRolesCols.roleId,role.id));for(let userRole of usersWithRole)await this.db.insert(requirementsTable).values(toCamel2({instance_id:instanceId,step_node_id:stepNodeId,verifier_node_id:nodeId,entity_name:entityName,entity_id:entityId,verifier_type:"user",verifier_user_id:userRole.user_id,verifier_role:config.verifierRole||null,require_signature:config.requireSignature,all_must_approve:!0,step_order:stepOrder,status:"pending"}))}}}else await this.db.insert(requirementsTable).values(toCamel2({instance_id:instanceId,step_node_id:stepNodeId,verifier_node_id:nodeId,entity_name:entityName,entity_id:entityId,verifier_type:verifierType,verifier_user_id:config.verifierUserId||null,verifier_role:config.verifierRole||null,require_signature:config.requireSignature,all_must_approve:allMustApprove,step_order:stepOrder,status:"pending"}))}if(this.onNotificationTrigger){let connectedNotifIds=this.getConnectedNotificationNodeIds(stepNodeId,steps,edges);if(this.logger.info(`[Verification] on_step_reached: stepNodeId=${stepNodeId}, connectedNotifIds=[${connectedNotifIds.join(", ")}]`),connectedNotifIds.length>0)for(let notifNodeId of connectedNotifIds)this.logger.info(`[Verification] Triggering on_step_reached for notifNodeId=${notifNodeId}`),await this.onNotificationTrigger({trigger:"on_step_reached",flow_id:flowId,entity_name:entityName,entity_id:entityId,node_id:notifNodeId,context:{step_name:stepNode.name||`Step ${stepOrder}`,step_order:stepOrder}});else await this.onNotificationTrigger({trigger:"on_step_reached",flow_id:flowId,entity_name:entityName,entity_id:entityId,context:{step_name:stepNode.name||`Step ${stepOrder}`,step_order:stepOrder}})}}async getStatus(entityName,entityId){let instancesTable=this.getTable("verificationInstances"),flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),verificationsTable=this.getTable("verifications"),requirementsTable=this.getTable("verificationRequirements"),emptyStatus={entity_name:entityName,entity_id:entityId,instance:null,flow:null,current_step:0,total_steps:0,is_completed:!1,is_rejected:!1,verifications:[],pending_requirements:[]};if(!instancesTable||!flowsTable||!verificationsTable||!requirementsTable)return this.logger.error("[Verification] Required tables not found"),emptyStatus;let instanceRow=(await this.db.select().from(instancesTable).where(and8(eq14(this.getCol(instancesTable,"entityName"),entityName),eq14(this.getCol(instancesTable,"entityId"),entityId))).orderBy(desc2(this.getCol(instancesTable,"createdAt"))).limit(1))[0];if(!instanceRow)return emptyStatus;let instance=fromCamel2(instanceRow),flows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),instance.flow_id)).limit(1),flow=flows[0]?fromCamel2(flows[0]):void 0,totalSteps=(stepsTable?await this.db.select().from(stepsTable).where(and8(eq14(this.getCol(stepsTable,"flowId"),instance.flow_id),eq14(this.getCol(stepsTable,"nodeType"),"step"))):[]).length,verifications=(await this.db.select().from(verificationsTable).where(eq14(this.getCol(verificationsTable,"instanceId"),instance.id)).orderBy(desc2(this.getCol(verificationsTable,"createdAt")))).map((r)=>fromCamel2(r)),pendingRequirements=(await this.db.select().from(requirementsTable).where(and8(eq14(this.getCol(requirementsTable,"instanceId"),instance.id),eq14(this.getCol(requirementsTable,"status"),"pending")))).map((r)=>fromCamel2(r));return{entity_name:entityName,entity_id:entityId,instance,flow:flow||null,current_step:instance.current_step_order,total_steps:totalSteps,is_completed:instance.status==="completed",is_rejected:instance.status==="rejected",verifications,pending_requirements:pendingRequirements}}async decide(params){let{entity_name,entity_id,user_id,decision,reason,signature_id,diff}=params,verificationsTable=this.getTable("verifications"),requirementsTable=this.getTable("verificationRequirements"),instancesTable=this.getTable("verificationInstances"),stepsTable=this.getTable("verificationSteps"),edgesTable=this.getTable("verificationEdges"),verifierConfigsTable=this.getTable("verificationVerifierConfigs"),userRolesTable=this.getTable("userRoles"),rolesTable=this.getTable("roles");if(!verificationsTable||!requirementsTable||!instancesTable)return{success:!1,message:"Verification tables not configured"};let status=await this.getStatus(entity_name,entity_id);if(!status.instance||status.instance.status!=="active")return{success:!1,message:"No active verification instance"};let currentPending=status.pending_requirements.filter((r)=>r.step_order===status.current_step);if(currentPending.length===0)return{success:!1,message:"No pending requirements for current step"};let matchedReq=null;for(let req of currentPending){if(req.verifier_type==="user"&&req.verifier_user_id===user_id){matchedReq=req;break}if(req.verifier_type==="role"&&req.verifier_role&&userRolesTable&&rolesTable){let userRolesCols=userRolesTable,rolesCols=rolesTable;if((await this.db.select({role_name:rolesCols.name}).from(userRolesTable).innerJoin(rolesTable,eq14(userRolesCols.roleId,rolesCols.id)).where(eq14(userRolesCols.userId,user_id))).some((ur)=>ur.role_name===req.verifier_role)){matchedReq=req;break}}}if(!matchedReq)return{success:!1,message:"User is not authorized to verify at this step"};if(matchedReq.require_signature){if(!signature_id)return{success:!1,message:"Signature is required for this verification step"};let filesTable=resolveSchemaTable(this.schemaTables,"files",this.logger);if(filesTable){if((await this.db.select().from(filesTable).where(and8(eq14(this.getCol(filesTable,"id"),signature_id),eq14(this.getCol(filesTable,"uploadedBy"),user_id))).limit(1)).length===0)return{success:!1,message:"Signature file not found or not owned by the verifier"}}}if((await this.db.update(requirementsTable).set({status:decision}).where(and8(eq14(this.getCol(requirementsTable,"id"),matchedReq.id),eq14(this.getCol(requirementsTable,"status"),"pending"))).returning()).length===0)return{success:!1,message:"This requirement has already been decided"};let[newVerification]=await this.db.insert(verificationsTable).values(toCamel2({instance_id:status.instance.id,requirement_id:matchedReq.id,verifier_id:user_id,signature_id:signature_id||null,entity_name,entity_id,step_order:status.current_step,decision,reason:reason||null,diff:diff||null})).returning();if(this.onNotificationTrigger&&status.instance){let stepName=`Step ${status.current_step}`,allSteps=[],allEdges=[];if(stepsTable){allSteps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),status.instance.flow_id));let stepRow=allSteps.find((s)=>s.nodeId===matchedReq.step_node_id);if(stepRow?.name)stepName=stepRow.name}if(edgesTable)allEdges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),status.instance.flow_id));let triggerName=decision==="approved"?"on_approved":"on_rejected",ctx={flow_name:status.flow?.name||"",step_name:stepName,step_order:status.current_step,total_steps:status.total_steps,decision},connectedNotifIds=this.getConnectedNotificationNodeIds(matchedReq.step_node_id,allSteps,allEdges);if(connectedNotifIds.length>0)for(let notifNodeId of connectedNotifIds)await this.onNotificationTrigger({trigger:triggerName,flow_id:status.instance.flow_id,entity_name,entity_id,node_id:notifNodeId,verifier_id:user_id,decision,context:ctx});else await this.onNotificationTrigger({trigger:triggerName,flow_id:status.instance.flow_id,entity_name,entity_id,verifier_id:user_id,decision,context:ctx})}if(decision==="rejected"){await this.db.update(instancesTable).set(toCamel2({status:"rejected",completed_at:new Date})).where(eq14(this.getCol(instancesTable,"id"),status.instance.id));let newInstanceId;if(this.config.autoResetOnRejection){this.logger.info(`[Verification] Flow rejected for ${entity_name}:${entity_id}, auto-restarting from step 1`);let restartResult=await this.startFlow({flow_id:status.instance.flow_id,entity_name,entity_id,started_by:status.instance.started_by});if(restartResult.success)newInstanceId=restartResult.instance_id}return{success:!0,message:this.config.autoResetOnRejection?"Verification rejected \u2014 flow restarted from step 1":"Verification rejected",verification:newVerification,flow_completed:!1,new_instance_id:newInstanceId}}let matchedReqId=matchedReq.id,remainingPending=currentPending.filter((r)=>r.id!==matchedReqId);if(remainingPending.length>0)return{success:!0,message:`Step ${status.current_step} partially approved, ${remainingPending.length} verifier(s) remaining`,verification:newVerification,flow_completed:!1};let nextStep=status.current_step+1;if(nextStep>status.total_steps){if(await this.db.update(instancesTable).set(toCamel2({status:"completed",completed_at:new Date})).where(eq14(this.getCol(instancesTable,"id"),status.instance.id)),this.onNotificationTrigger){let completedSteps=[],completedEdges=[];if(stepsTable)completedSteps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),status.instance.flow_id));if(edgesTable)completedEdges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),status.instance.flow_id));let lastStepNodeId=matchedReq.step_node_id,completedNotifIds=this.getConnectedNotificationNodeIds(lastStepNodeId,completedSteps,completedEdges),completedCtx={flow_name:status.flow?.name||"",step_order:status.current_step,total_steps:status.total_steps};if(completedNotifIds.length>0)for(let notifNodeId of completedNotifIds)await this.onNotificationTrigger({trigger:"on_flow_completed",flow_id:status.instance.flow_id,entity_name,entity_id,node_id:notifNodeId,context:completedCtx});else await this.onNotificationTrigger({trigger:"on_flow_completed",flow_id:status.instance.flow_id,entity_name,entity_id,context:completedCtx})}return{success:!0,message:"Verification flow completed",verification:newVerification,flow_completed:!0}}if(await this.db.update(instancesTable).set(toCamel2({current_step_order:nextStep})).where(eq14(this.getCol(instancesTable,"id"),status.instance.id)),stepsTable&&edgesTable){let steps=await this.db.select().from(stepsTable).where(eq14(this.getCol(stepsTable,"flowId"),status.instance.flow_id)),edges=await this.db.select().from(edgesTable).where(eq14(this.getCol(edgesTable,"flowId"),status.instance.flow_id));await this.materializeRequirementsForStep(status.instance.id,status.instance.flow_id,entity_name,entity_id,nextStep,steps,edges,verifierConfigsTable,requirementsTable,userRolesTable)}return{success:!0,message:`Step ${status.current_step} approved, moving to step ${nextStep}`,verification:newVerification,flow_completed:!1,next_step:nextStep}}async startFlowForEntity(params){let flowsTable=this.getTable("verificationFlows");if(!flowsTable)return{success:!1,message:"Flow table not configured"};let flow=(await this.db.select().from(flowsTable).where(and8(eq14(this.getCol(flowsTable,"entityName"),params.entity_name),eq14(this.getCol(flowsTable,"isDraft"),!1))).limit(1))[0];if(!flow)return{success:!1,message:`No published flow found for entity '${params.entity_name}'`};return this.startFlow({flow_id:flow.id,entity_name:params.entity_name,entity_id:params.entity_id,started_by:params.started_by})}async listEntityStatuses(params){let instancesTable=this.getTable("verificationInstances"),flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),emptyResult={items:[],total:0,page:1,limit:20};if(!instancesTable||!flowsTable)return emptyResult;let page=params.page||1,limit=params.limit||20,offset=(page-1)*limit,conditions=[eq14(this.getCol(instancesTable,"entityName"),params.entity_name)];if(params.status)conditions.push(eq14(this.getCol(instancesTable,"status"),params.status));let whereClause=conditions.length===1?conditions[0]:and8(...conditions),allInstances=(await this.db.select().from(instancesTable).where(whereClause).orderBy(desc2(this.getCol(instancesTable,"createdAt")))).map((r)=>fromCamel2(r)),total=allInstances.length,paged=allInstances.slice(offset,offset+limit),items=[];for(let inst of paged){let flowId=inst.flow_id,flows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),flowId)).limit(1),flow=flows[0]?fromCamel2(flows[0]):void 0,totalSteps=0;if(stepsTable)totalSteps=(await this.db.select().from(stepsTable).where(and8(eq14(this.getCol(stepsTable,"flowId"),flowId),eq14(this.getCol(stepsTable,"nodeType"),"step")))).length;items.push({instance_id:inst.id,entity_name:inst.entity_name,entity_id:inst.entity_id,flow_id:flowId,flow_name:flow?.name||"Unknown",status:inst.status,current_step_order:inst.current_step_order,total_steps:totalSteps,started_by:inst.started_by,started_at:inst.started_at,completed_at:inst.completed_at})}return{items,total,page,limit}}async getPending(userId){let requirementsTable=this.getTable("verificationRequirements"),instancesTable=this.getTable("verificationInstances"),flowsTable=this.getTable("verificationFlows"),stepsTable=this.getTable("verificationSteps"),userRolesTable=this.getTable("userRoles"),rolesTable=this.getTable("roles");if(this.logger.info(`[Verification.getPending] userId=${userId}, tables: req=${!!requirementsTable} inst=${!!instancesTable} flow=${!!flowsTable} steps=${!!stepsTable} roles=${!!rolesTable} userRoles=${!!userRolesTable}`),!requirementsTable||!instancesTable||!flowsTable)return this.logger.warn("[Verification.getPending] Missing required tables, returning empty"),[];let userRolesCols=userRolesTable,rolesCols=rolesTable,userRoleNames=(userRolesTable&&rolesTable?await this.db.select({role_name:rolesCols.name}).from(userRolesTable).innerJoin(rolesTable,eq14(userRolesCols.roleId,rolesCols.id)).where(eq14(userRolesCols.userId,userId)):[]).map((ur)=>ur.role_name),pendingReqs=(await this.db.select().from(requirementsTable).where(eq14(this.getCol(requirementsTable,"status"),"pending"))).map((r)=>fromCamel2(r));this.logger.info(`[Verification.getPending] Found ${pendingReqs.length} pending requirements, userRoles=${JSON.stringify(userRoleNames)}`);for(let req of pendingReqs)this.logger.info(`[Verification.getPending] Req: verifier_type=${req.verifier_type} verifier_user_id=${req.verifier_user_id} verifier_role=${req.verifier_role} step_order=${req.step_order} entity=${req.entity_name}/${req.entity_id}`);let pendingItems=[];for(let req of pendingReqs){if(!(req.verifier_type==="user"&&req.verifier_user_id===userId||req.verifier_type==="role"&&userRoleNames.includes(req.verifier_role))){this.logger.info(`[Verification.getPending] Skipping req: canVerify=false (type=${req.verifier_type}, reqUserId=${req.verifier_user_id}, loggedInUserId=${userId})`);continue}let instances=await this.db.select().from(instancesTable).where(and8(eq14(this.getCol(instancesTable,"id"),req.instance_id),eq14(this.getCol(instancesTable,"status"),"active"))).limit(1),instance=instances[0]?fromCamel2(instances[0]):void 0;if(!instance)continue;if(instance.current_step_order!==req.step_order)continue;let flows=await this.db.select().from(flowsTable).where(eq14(this.getCol(flowsTable,"id"),instance.flow_id)).limit(1),flow=flows[0]?fromCamel2(flows[0]):void 0;if(!flow)continue;let stepName;if(stepsTable&&req.step_node_id){let stepRow=(await this.db.select().from(stepsTable).where(and8(eq14(this.getCol(stepsTable,"flowId"),instance.flow_id),eq14(this.getCol(stepsTable,"nodeId"),req.step_node_id))).limit(1))[0];if(stepRow?.name)stepName=stepRow.name}pendingItems.push({instance_id:instance.id,entity_name:req.entity_name,entity_id:req.entity_id,flow_name:flow.name,step_order:req.step_order,step_name:stepName,require_signature:req.require_signature,created_at:req.created_at})}return pendingItems}}var init_Verification=__esm(()=>{init_types5()});var genLookup=(target)=>{let lookupTemp=typeof Uint8Array>"u"?[]:new Uint8Array(256),len=64;for(let i=0;i<64;i++)lookupTemp[target.charCodeAt(i)]=i;return lookupTemp},lookup,lookupUrl,base64UrlPattern,base64Pattern,base64,base64_default;var init_base64=__esm(()=>{lookup=genLookup("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"),lookupUrl=genLookup("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"),base64UrlPattern=/^[-A-Za-z0-9\-_]*$/,base64Pattern=/^[-A-Za-z0-9+/]*={0,3}$/,base64={};base64.toArrayBuffer=(data,urlMode)=>{let len=data.length,bufferLength=data.length*0.75,i,p=0,encoded1,encoded2,encoded3,encoded4;if(data[data.length-1]==="="){if(bufferLength--,data[data.length-2]==="=")bufferLength--}let arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer),target=urlMode?lookupUrl:lookup;for(i=0;i<len;i+=4)encoded1=target[data.charCodeAt(i)],encoded2=target[data.charCodeAt(i+1)],encoded3=target[data.charCodeAt(i+2)],encoded4=target[data.charCodeAt(i+3)],bytes[p++]=encoded1<<2|encoded2>>4,bytes[p++]=(encoded2&15)<<4|encoded3>>2,bytes[p++]=(encoded3&3)<<6|encoded4&63;return arraybuffer};base64.fromArrayBuffer=(arrBuf,urlMode)=>{let bytes=new Uint8Array(arrBuf),i,result="",len=bytes.length,target=urlMode?"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(i=0;i<len;i+=3)result+=target[bytes[i]>>2],result+=target[(bytes[i]&3)<<4|bytes[i+1]>>4],result+=target[(bytes[i+1]&15)<<2|bytes[i+2]>>6],result+=target[bytes[i+2]&63];let remainder=len%3;if(remainder===2)result=result.substring(0,result.length-1)+(urlMode?"":"=");else if(remainder===1)result=result.substring(0,result.length-2)+(urlMode?"":"==");return result};base64.toString=(str3,urlMode)=>{return new TextDecoder().decode(base64.toArrayBuffer(str3,urlMode))};base64.fromString=(str3,urlMode)=>{return base64.fromArrayBuffer(new TextEncoder().encode(str3),urlMode)};base64.validate=(encoded,urlMode)=>{if(!(typeof encoded==="string"||encoded instanceof String))return!1;try{return urlMode?base64UrlPattern.test(encoded):base64Pattern.test(encoded)}catch(_e){return!1}};base64.base64=base64;base64_default=base64});var exports_isoBase64URL={};__export(exports_isoBase64URL,{trimPadding:()=>trimPadding,toUTF8String:()=>toUTF8String,toBuffer:()=>toBuffer,toBase64:()=>toBase64,isBase64URL:()=>isBase64URL,isBase64:()=>isBase64,fromUTF8String:()=>fromUTF8String,fromBuffer:()=>fromBuffer});function toBuffer(base64urlString,from="base64url"){let _buffer=base64_default.toArrayBuffer(base64urlString,from==="base64url");return new Uint8Array(_buffer)}function fromBuffer(buffer,to="base64url"){let _normalized=new Uint8Array(buffer);return base64_default.fromArrayBuffer(_normalized.buffer,to==="base64url")}function toBase64(base64urlString){let fromBase64Url=base64_default.toArrayBuffer(base64urlString,!0);return base64_default.fromArrayBuffer(fromBase64Url)}function fromUTF8String(utf8String){return base64_default.fromString(utf8String,!0)}function toUTF8String(base64urlString){return base64_default.toString(base64urlString,!0)}function isBase64(input){return base64_default.validate(input,!1)}function isBase64URL(input){return input=trimPadding(input),base64_default.validate(input,!0)}function trimPadding(input){return input.replace(/=/g,"")}var init_isoBase64URL=__esm(()=>{init_base64()});function decodeLength(data,argument,index){if(argument<24)return[argument,1];let remainingDataLength=data.byteLength-index-1,view=new DataView(data.buffer,index+1),output,bytes=0;switch(argument){case 24:{if(remainingDataLength>0)output=view.getUint8(0),bytes=2;break}case 25:{if(remainingDataLength>1)output=view.getUint16(0,!1),bytes=3;break}case 26:{if(remainingDataLength>3)output=view.getUint32(0,!1),bytes=5;break}case 27:{if(remainingDataLength>7){let bigOutput=view.getBigUint64(0,!1);if(bigOutput>=24n&&bigOutput<=Number.MAX_SAFE_INTEGER)return[Number(bigOutput),9]}break}}if(output&&output>=24)return[output,bytes];throw Error("Length not supported or not well formed")}function encodeLength(major,argument){let majorEncoded=major<<5;if(argument<0)throw Error("CBOR Data Item argument must not be negative");let bigintArgument;if(typeof argument=="number"){if(!Number.isInteger(argument))throw Error("CBOR Data Item argument must be an integer");bigintArgument=BigInt(argument)}else bigintArgument=argument;if(major==MAJOR_TYPE_NEGATIVE_INTEGER){if(bigintArgument==0n)throw Error("CBOR Data Item argument cannot be zero when negative");bigintArgument=bigintArgument-1n}if(bigintArgument>18446744073709551615n)throw Error("CBOR number out of range");let buffer=new Uint8Array(8);if(new DataView(buffer.buffer).setBigUint64(0,bigintArgument,!1),bigintArgument<=23)return[majorEncoded|buffer[7]];else if(bigintArgument<=255)return[majorEncoded|24,buffer[7]];else if(bigintArgument<=65535)return[majorEncoded|25,...buffer.slice(6)];else if(bigintArgument<=4294967295)return[majorEncoded|26,...buffer.slice(4)];else return[majorEncoded|27,...buffer]}var MAJOR_TYPE_UNSIGNED_INTEGER=0,MAJOR_TYPE_NEGATIVE_INTEGER=1,MAJOR_TYPE_BYTE_STRING=2,MAJOR_TYPE_TEXT_STRING=3,MAJOR_TYPE_ARRAY=4,MAJOR_TYPE_MAP=5,MAJOR_TYPE_TAG=6,MAJOR_TYPE_SIMPLE_OR_FLOAT=7;class CBORTag{constructor(tag,value){Object.defineProperty(this,"tagId",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"tagValue",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.tagId=tag,this.tagValue=value}get tag(){return this.tagId}get value(){return this.tagValue}}function decodeUnsignedInteger(data,argument,index){return decodeLength(data,argument,index)}function decodeNegativeInteger(data,argument,index){let[value,length]=decodeUnsignedInteger(data,argument,index);return[-value-1,length]}function decodeByteString(data,argument,index){let[lengthValue,lengthConsumed]=decodeLength(data,argument,index),dataStartIndex=index+lengthConsumed;return[new Uint8Array(data.buffer.slice(dataStartIndex,dataStartIndex+lengthValue)),lengthConsumed+lengthValue]}function decodeString(data,argument,index){let[value,length]=decodeByteString(data,argument,index);return[TEXT_DECODER.decode(value),length]}function decodeArray(data,argument,index){if(argument===0)return[[],1];let[length,lengthConsumed]=decodeLength(data,argument,index),consumedLength=lengthConsumed,value=[];for(let i=0;i<length;i++){if(data.byteLength-index-consumedLength<=0)throw Error("array is not supported or well formed");let[decodedValue,consumed]=decodeNext(data,index+consumedLength);value.push(decodedValue),consumedLength+=consumed}return[value,consumedLength]}function decodeMap(data,argument,index){if(argument===0)return[new Map,1];let[length,lengthConsumed]=decodeLength(data,argument,index),consumedLength=lengthConsumed,result=new Map;for(let i=0;i<length;i++){let remainingDataLength=data.byteLength-index-consumedLength;if(remainingDataLength<=0)throw Error(MAP_ERROR);let[key2,keyConsumed]=decodeNext(data,index+consumedLength);if(consumedLength+=keyConsumed,remainingDataLength-=keyConsumed,remainingDataLength<=0)throw Error(MAP_ERROR);if(typeof key2!=="string"&&typeof key2!=="number")throw Error(MAP_ERROR);if(result.has(key2))throw Error(MAP_ERROR);let[value,valueConsumed]=decodeNext(data,index+consumedLength);consumedLength+=valueConsumed,result.set(key2,value)}return[result,consumedLength]}function decodeFloat16(data,index){if(index+3>data.byteLength)throw Error("CBOR stream ended before end of Float 16");let result=data.getUint16(index+1,!1);if(result==31744)return[1/0,3];else if(result==32256)return[NaN,3];else if(result==64512)return[-1/0,3];throw Error("Float16 data is unsupported")}function decodeFloat32(data,index){if(index+5>data.byteLength)throw Error("CBOR stream ended before end of Float 32");return[data.getFloat32(index+1,!1),5]}function decodeFloat64(data,index){if(index+9>data.byteLength)throw Error("CBOR stream ended before end of Float 64");return[data.getFloat64(index+1,!1),9]}function decodeTag(data,argument,index){let[tag,tagBytes]=decodeLength(data,argument,index),[value,valueBytes]=decodeNext(data,index+tagBytes);return[new CBORTag(tag,value),tagBytes+valueBytes]}function decodeNext(data,index){if(index>=data.byteLength)throw Error("CBOR stream ended before tag value");let byte=data.getUint8(index),majorType=byte>>5,argument=byte&31;switch(majorType){case MAJOR_TYPE_UNSIGNED_INTEGER:return decodeUnsignedInteger(data,argument,index);case MAJOR_TYPE_NEGATIVE_INTEGER:return decodeNegativeInteger(data,argument,index);case MAJOR_TYPE_BYTE_STRING:return decodeByteString(data,argument,index);case MAJOR_TYPE_TEXT_STRING:return decodeString(data,argument,index);case MAJOR_TYPE_ARRAY:return decodeArray(data,argument,index);case MAJOR_TYPE_MAP:return decodeMap(data,argument,index);case MAJOR_TYPE_TAG:return decodeTag(data,argument,index);case MAJOR_TYPE_SIMPLE_OR_FLOAT:switch(argument){case 20:return[!1,1];case 21:return[!0,1];case 22:return[null,1];case 23:return[void 0,1];case 25:return decodeFloat16(data,index);case 26:return decodeFloat32(data,index);case 27:return decodeFloat64(data,index)}}throw Error(`Unsupported or not well formed at ${index}`)}function encodeSimple(data){if(data===!0)return 245;else if(data===!1)return 244;else if(data===null)return 246;return 247}function encodeFloat(data){if(Math.fround(data)==data||!Number.isFinite(data)||Number.isNaN(data)){let output=new Uint8Array(5);return output[0]=250,new DataView(output.buffer).setFloat32(1,data,!1),output}else{let output=new Uint8Array(9);return output[0]=251,new DataView(output.buffer).setFloat64(1,data,!1),output}}function encodeNumber(data){if(typeof data=="number"){if(Number.isSafeInteger(data))if(data<0)return encodeLength(MAJOR_TYPE_NEGATIVE_INTEGER,Math.abs(data));else return encodeLength(MAJOR_TYPE_UNSIGNED_INTEGER,data);return[encodeFloat(data)]}else if(data<0n)return encodeLength(MAJOR_TYPE_NEGATIVE_INTEGER,data*-1n);else return encodeLength(MAJOR_TYPE_UNSIGNED_INTEGER,data)}function encodeString(data,output){output.push(...encodeLength(MAJOR_TYPE_TEXT_STRING,data.length)),output.push(ENCODER.encode(data))}function encodeBytes(data,output){output.push(...encodeLength(MAJOR_TYPE_BYTE_STRING,data.length)),output.push(data)}function encodeArray(data,output){output.push(...encodeLength(MAJOR_TYPE_ARRAY,data.length));for(let element of data)encodePartialCBOR(element,output)}function encodeMap(data,output){output.push(new Uint8Array(encodeLength(MAJOR_TYPE_MAP,data.size)));for(let[key2,value]of data.entries())encodePartialCBOR(key2,output),encodePartialCBOR(value,output)}function encodeTag(tag,output){output.push(...encodeLength(MAJOR_TYPE_TAG,tag.tag)),encodePartialCBOR(tag.value,output)}function encodePartialCBOR(data,output){if(typeof data=="boolean"||data===null||data==null){output.push(encodeSimple(data));return}if(typeof data=="number"||typeof data=="bigint"){output.push(...encodeNumber(data));return}if(typeof data=="string"){encodeString(data,output);return}if(data instanceof Uint8Array){encodeBytes(data,output);return}if(Array.isArray(data)){encodeArray(data,output);return}if(data instanceof Map){encodeMap(data,output);return}if(data instanceof CBORTag){encodeTag(data,output);return}throw Error("Not implemented")}function decodePartialCBOR(data,index){if(data.byteLength===0||data.byteLength<=index||index<0)throw Error("No data");if(data instanceof Uint8Array)return decodeNext(new DataView(data.buffer),index);else if(data instanceof ArrayBuffer)return decodeNext(new DataView(data),index);return decodeNext(data,index)}function encodeCBOR(data){let results=[];encodePartialCBOR(data,results);let length=0;for(let result of results)if(typeof result=="number")length+=1;else length+=result.length;let output=new Uint8Array(length),index=0;for(let result of results)if(typeof result=="number")output[index]=result,index+=1;else output.set(result,index),index+=result.length;return output}var TEXT_DECODER,MAP_ERROR="Map is not supported or well formed",ENCODER;var init_cbor=__esm(()=>{TEXT_DECODER=new TextDecoder;ENCODER=new TextEncoder});var init_esm=__esm(()=>{init_cbor()});var exports_isoCBOR={};__export(exports_isoCBOR,{encode:()=>encode,decodeFirst:()=>decodeFirst});function decodeFirst(input){let _input=new Uint8Array(input),decoded=decodePartialCBOR(_input,0),[first]=decoded;return first}function encode(input){return encodeCBOR(input)}var init_isoCBOR=__esm(()=>{init_esm()});function isCOSEPublicKeyOKP(cosePublicKey){let kty=cosePublicKey.get(COSEKEYS.kty);return isCOSEKty(kty)&&kty===COSEKTY.OKP}function isCOSEPublicKeyEC2(cosePublicKey){let kty=cosePublicKey.get(COSEKEYS.kty);return isCOSEKty(kty)&&kty===COSEKTY.EC2}function isCOSEPublicKeyRSA(cosePublicKey){let kty=cosePublicKey.get(COSEKEYS.kty);return isCOSEKty(kty)&&kty===COSEKTY.RSA}function isCOSEKty(kty){return Object.values(COSEKTY).indexOf(kty)>=0}function isCOSECrv(crv){return Object.values(COSECRV).indexOf(crv)>=0}function isCOSEAlg(alg){return Object.values(COSEALG).indexOf(alg)>=0}var COSEKEYS,COSEKTY,COSECRV,COSEALG;var init_cose=__esm(()=>{(function(COSEKEYS2){COSEKEYS2[COSEKEYS2.kty=1]="kty",COSEKEYS2[COSEKEYS2.alg=3]="alg",COSEKEYS2[COSEKEYS2.crv=-1]="crv",COSEKEYS2[COSEKEYS2.x=-2]="x",COSEKEYS2[COSEKEYS2.y=-3]="y",COSEKEYS2[COSEKEYS2.n=-1]="n",COSEKEYS2[COSEKEYS2.e=-2]="e"})(COSEKEYS||(COSEKEYS={}));(function(COSEKTY2){COSEKTY2[COSEKTY2.OKP=1]="OKP",COSEKTY2[COSEKTY2.EC2=2]="EC2",COSEKTY2[COSEKTY2.RSA=3]="RSA"})(COSEKTY||(COSEKTY={}));(function(COSECRV2){COSECRV2[COSECRV2.P256=1]="P256",COSECRV2[COSECRV2.P384=2]="P384",COSECRV2[COSECRV2.P521=3]="P521",COSECRV2[COSECRV2.ED25519=6]="ED25519",COSECRV2[COSECRV2.SECP256K1=8]="SECP256K1"})(COSECRV||(COSECRV={}));(function(COSEALG2){COSEALG2[COSEALG2.ES256=-7]="ES256",COSEALG2[COSEALG2.EdDSA=-8]="EdDSA",COSEALG2[COSEALG2.ES384=-35]="ES384",COSEALG2[COSEALG2.ES512=-36]="ES512",COSEALG2[COSEALG2.PS256=-37]="PS256",COSEALG2[COSEALG2.PS384=-38]="PS384",COSEALG2[COSEALG2.PS512=-39]="PS512",COSEALG2[COSEALG2.ES256K=-47]="ES256K",COSEALG2[COSEALG2.RS256=-257]="RS256",COSEALG2[COSEALG2.RS384=-258]="RS384",COSEALG2[COSEALG2.RS512=-259]="RS512",COSEALG2[COSEALG2.RS1=-65535]="RS1"})(COSEALG||(COSEALG={}))});function mapCoseAlgToWebCryptoAlg(alg){if([COSEALG.RS1].indexOf(alg)>=0)return"SHA-1";else if([COSEALG.ES256,COSEALG.PS256,COSEALG.RS256].indexOf(alg)>=0)return"SHA-256";else if([COSEALG.ES384,COSEALG.PS384,COSEALG.RS384].indexOf(alg)>=0)return"SHA-384";else if([COSEALG.ES512,COSEALG.PS512,COSEALG.RS512,COSEALG.EdDSA].indexOf(alg)>=0)return"SHA-512";throw Error(`Could not map COSE alg value of ${alg} to a WebCrypto alg`)}var init_mapCoseAlgToWebCryptoAlg=__esm(()=>{init_cose()});function getWebCrypto(){return new Promise((resolve2,reject)=>{if(webCrypto)return resolve2(webCrypto);let _globalThisCrypto=_getWebCryptoInternals.stubThisGlobalThisCrypto();if(_globalThisCrypto)return webCrypto=_globalThisCrypto,resolve2(webCrypto);return reject(new MissingWebCrypto)})}var webCrypto=void 0,MissingWebCrypto,_getWebCryptoInternals;var init_getWebCrypto=__esm(()=>{MissingWebCrypto=class MissingWebCrypto extends Error{constructor(){super("An instance of the Crypto API could not be located");this.name="MissingWebCrypto"}};_getWebCryptoInternals={stubThisGlobalThisCrypto:()=>globalThis.crypto,setCachedCrypto:(newCrypto)=>{webCrypto=newCrypto}}});async function digest(data,algorithm){let WebCrypto=await getWebCrypto(),subtleAlgorithm=mapCoseAlgToWebCryptoAlg(algorithm),hashed=await WebCrypto.subtle.digest(subtleAlgorithm,data);return new Uint8Array(hashed)}var init_digest=__esm(()=>{init_mapCoseAlgToWebCryptoAlg();init_getWebCrypto()});async function getRandomValues(array){return(await getWebCrypto()).getRandomValues(array),array}var init_getRandomValues=__esm(()=>{init_getWebCrypto()});async function importKey(opts){let WebCrypto=await getWebCrypto(),{keyData,algorithm}=opts;return WebCrypto.subtle.importKey("jwk",keyData,algorithm,!1,["verify"])}var init_importKey=__esm(()=>{init_getWebCrypto()});async function verifyEC2(opts){let{cosePublicKey,signature,data,shaHashOverride}=opts,WebCrypto=await getWebCrypto(),alg=cosePublicKey.get(COSEKEYS.alg),crv=cosePublicKey.get(COSEKEYS.crv),x=cosePublicKey.get(COSEKEYS.x),y=cosePublicKey.get(COSEKEYS.y);if(!alg)throw Error("Public key was missing alg (EC2)");if(!crv)throw Error("Public key was missing crv (EC2)");if(!x)throw Error("Public key was missing x (EC2)");if(!y)throw Error("Public key was missing y (EC2)");let _crv;if(crv===COSECRV.P256)_crv="P-256";else if(crv===COSECRV.P384)_crv="P-384";else if(crv===COSECRV.P521)_crv="P-521";else throw Error(`Unexpected COSE crv value of ${crv} (EC2)`);let keyData={kty:"EC",crv:_crv,x:exports_isoBase64URL.fromBuffer(x),y:exports_isoBase64URL.fromBuffer(y),ext:!1},key2=await importKey({keyData,algorithm:{name:"ECDSA",namedCurve:_crv}}),subtleAlg=mapCoseAlgToWebCryptoAlg(alg);if(shaHashOverride)subtleAlg=mapCoseAlgToWebCryptoAlg(shaHashOverride);let verifyAlgorithm={name:"ECDSA",hash:{name:subtleAlg}};return WebCrypto.subtle.verify(verifyAlgorithm,key2,signature,data)}var init_verifyEC2=__esm(()=>{init_cose();init_mapCoseAlgToWebCryptoAlg();init_importKey();init_iso();init_getWebCrypto()});function mapCoseAlgToWebCryptoKeyAlgName(alg){if([COSEALG.EdDSA].indexOf(alg)>=0)return"Ed25519";else if([COSEALG.ES256,COSEALG.ES384,COSEALG.ES512,COSEALG.ES256K].indexOf(alg)>=0)return"ECDSA";else if([COSEALG.RS256,COSEALG.RS384,COSEALG.RS512,COSEALG.RS1].indexOf(alg)>=0)return"RSASSA-PKCS1-v1_5";else if([COSEALG.PS256,COSEALG.PS384,COSEALG.PS512].indexOf(alg)>=0)return"RSA-PSS";throw Error(`Could not map COSE alg value of ${alg} to a WebCrypto key alg name`)}var init_mapCoseAlgToWebCryptoKeyAlgName=__esm(()=>{init_cose()});async function verifyRSA(opts){let{cosePublicKey,signature,data,shaHashOverride}=opts,WebCrypto=await getWebCrypto(),alg=cosePublicKey.get(COSEKEYS.alg),n=cosePublicKey.get(COSEKEYS.n),e=cosePublicKey.get(COSEKEYS.e);if(!alg)throw Error("Public key was missing alg (RSA)");if(!isCOSEAlg(alg))throw Error(`Public key had invalid alg ${alg} (RSA)`);if(!n)throw Error("Public key was missing n (RSA)");if(!e)throw Error("Public key was missing e (RSA)");let keyData={kty:"RSA",alg:"",n:exports_isoBase64URL.fromBuffer(n),e:exports_isoBase64URL.fromBuffer(e),ext:!1},keyAlgorithm={name:mapCoseAlgToWebCryptoKeyAlgName(alg),hash:{name:mapCoseAlgToWebCryptoAlg(alg)}},verifyAlgorithm={name:mapCoseAlgToWebCryptoKeyAlgName(alg)};if(shaHashOverride)keyAlgorithm.hash.name=mapCoseAlgToWebCryptoAlg(shaHashOverride);if(keyAlgorithm.name==="RSASSA-PKCS1-v1_5"){if(keyAlgorithm.hash.name==="SHA-256")keyData.alg="RS256";else if(keyAlgorithm.hash.name==="SHA-384")keyData.alg="RS384";else if(keyAlgorithm.hash.name==="SHA-512")keyData.alg="RS512";else if(keyAlgorithm.hash.name==="SHA-1")keyData.alg="RS1"}else if(keyAlgorithm.name==="RSA-PSS"){let saltLength=0;if(keyAlgorithm.hash.name==="SHA-256")keyData.alg="PS256",saltLength=32;else if(keyAlgorithm.hash.name==="SHA-384")keyData.alg="PS384",saltLength=48;else if(keyAlgorithm.hash.name==="SHA-512")keyData.alg="PS512",saltLength=64;verifyAlgorithm.saltLength=saltLength}else throw Error(`Unexpected RSA key algorithm ${alg} (${keyAlgorithm.name})`);let key2=await importKey({keyData,algorithm:keyAlgorithm});return WebCrypto.subtle.verify(verifyAlgorithm,key2,signature,data)}var init_verifyRSA=__esm(()=>{init_cose();init_mapCoseAlgToWebCryptoAlg();init_importKey();init_iso();init_mapCoseAlgToWebCryptoKeyAlgName();init_getWebCrypto()});function convertAAGUIDToString(aaguid){let hex=exports_isoUint8Array.toHex(aaguid);return[hex.slice(0,8),hex.slice(8,12),hex.slice(12,16),hex.slice(16,20),hex.slice(20,32)].join("-")}var init_convertAAGUIDToString=__esm(()=>{init_iso()});function convertCertBufferToPEM(certBuffer){let b64cert;if(typeof certBuffer==="string")if(exports_isoBase64URL.isBase64URL(certBuffer))b64cert=exports_isoBase64URL.toBase64(certBuffer);else if(exports_isoBase64URL.isBase64(certBuffer))b64cert=certBuffer;else throw Error("Certificate is not a valid base64 or base64url string");else b64cert=exports_isoBase64URL.fromBuffer(certBuffer,"base64");let PEMKey="";for(let i=0;i<Math.ceil(b64cert.length/64);i+=1){let start=64*i;PEMKey+=`${b64cert.substr(start,64)}
102
102
  `}return PEMKey=`-----BEGIN CERTIFICATE-----
103
103
  ${PEMKey}-----END CERTIFICATE-----
104
104
  `,PEMKey}var init_convertCertBufferToPEM=__esm(()=>{init_iso()});function convertCOSEtoPKCS(cosePublicKey){let struct=exports_isoCBOR.decodeFirst(cosePublicKey),tag=Uint8Array.from([4]),x=struct.get(COSEKEYS.x),y=struct.get(COSEKEYS.y);if(!x)throw Error("COSE public key was missing x");if(y)return exports_isoUint8Array.concat([tag,x,y]);return exports_isoUint8Array.concat([tag,x])}var init_convertCOSEtoPKCS=__esm(()=>{init_iso();init_cose()});function decodeAttestationObject(attestationObject){return _decodeAttestationObjectInternals.stubThis(exports_isoCBOR.decodeFirst(attestationObject))}var _decodeAttestationObjectInternals;var init_decodeAttestationObject=__esm(()=>{init_iso();_decodeAttestationObjectInternals={stubThis:(value)=>value}});function decodeClientDataJSON(data){let toString=exports_isoBase64URL.toUTF8String(data),clientData=JSON.parse(toString);return _decodeClientDataJSONInternals.stubThis(clientData)}var _decodeClientDataJSONInternals;var init_decodeClientDataJSON=__esm(()=>{init_iso();_decodeClientDataJSONInternals={stubThis:(value)=>value}});function decodeCredentialPublicKey(publicKey){return _decodeCredentialPublicKeyInternals.stubThis(exports_isoCBOR.decodeFirst(publicKey))}var _decodeCredentialPublicKeyInternals;var init_decodeCredentialPublicKey=__esm(()=>{init_iso();_decodeCredentialPublicKeyInternals={stubThis:(value)=>value}});async function generateUserID(){let newUserID=new Uint8Array(32);return await exports_isoCrypto.getRandomValues(newUserID),_generateUserIDInternals.stubThis(newUserID)}var _generateUserIDInternals;var init_generateUserID=__esm(()=>{init_iso();_generateUserIDInternals={stubThis:(value)=>value}});class BufferSourceConverter{static isArrayBuffer(data){return Object.prototype.toString.call(data)==="[object ArrayBuffer]"}static toArrayBuffer(data){if(this.isArrayBuffer(data))return data;if(data.byteLength===data.buffer.byteLength)return data.buffer;if(data.byteOffset===0&&data.byteLength===data.buffer.byteLength)return data.buffer;return this.toUint8Array(data.buffer).slice(data.byteOffset,data.byteOffset+data.byteLength).buffer}static toUint8Array(data){return this.toView(data,Uint8Array)}static toView(data,type){if(data.constructor===type)return data;if(this.isArrayBuffer(data))return new type(data);if(this.isArrayBufferView(data))return new type(data.buffer,data.byteOffset,data.byteLength);throw TypeError("The provided value is not of type '(ArrayBuffer or ArrayBufferView)'")}static isBufferSource(data){return this.isArrayBufferView(data)||this.isArrayBuffer(data)}static isArrayBufferView(data){return ArrayBuffer.isView(data)||data&&this.isArrayBuffer(data.buffer)}static isEqual(a,b){let aView=BufferSourceConverter.toUint8Array(a),bView=BufferSourceConverter.toUint8Array(b);if(aView.length!==bView.byteLength)return!1;for(let i=0;i<aView.length;i++)if(aView[i]!==bView[i])return!1;return!0}static concat(...args){let buffers;if(Array.isArray(args[0])&&!(args[1]instanceof Function))buffers=args[0];else if(Array.isArray(args[0])&&args[1]instanceof Function)buffers=args[0];else if(args[args.length-1]instanceof Function)buffers=args.slice(0,args.length-1);else buffers=args;let size=0;for(let buffer of buffers)size+=buffer.byteLength;let res=new Uint8Array(size),offset=0;for(let buffer of buffers){let view=this.toUint8Array(buffer);res.set(view,offset),offset+=view.length}if(args[args.length-1]instanceof Function)return this.toView(res,args[args.length-1]);return res.buffer}}class Utf8Converter{static fromString(text){let s=unescape(encodeURIComponent(text)),uintArray=new Uint8Array(s.length);for(let i=0;i<s.length;i++)uintArray[i]=s.charCodeAt(i);return uintArray.buffer}static toString(buffer){let buf=BufferSourceConverter.toUint8Array(buffer),encodedString="";for(let i=0;i<buf.length;i++)encodedString+=String.fromCharCode(buf[i]);return decodeURIComponent(escape(encodedString))}}class Utf16Converter{static toString(buffer,littleEndian=!1){let arrayBuffer=BufferSourceConverter.toArrayBuffer(buffer),dataView=new DataView(arrayBuffer),res="";for(let i=0;i<arrayBuffer.byteLength;i+=2){let code=dataView.getUint16(i,littleEndian);res+=String.fromCharCode(code)}return res}static fromString(text,littleEndian=!1){let res=new ArrayBuffer(text.length*2),dataView=new DataView(res);for(let i=0;i<text.length;i++)dataView.setUint16(i*2,text.charCodeAt(i),littleEndian);return res}}class Convert{static isHex(data){return typeof data===STRING_TYPE&&HEX_REGEX.test(data)}static isBase64(data){return typeof data===STRING_TYPE&&BASE64_REGEX.test(data)}static isBase64Url(data){return typeof data===STRING_TYPE&&BASE64URL_REGEX.test(data)}static ToString(buffer,enc="utf8"){let buf=BufferSourceConverter.toUint8Array(buffer);switch(enc.toLowerCase()){case"utf8":return this.ToUtf8String(buf);case"binary":return this.ToBinary(buf);case"hex":return this.ToHex(buf);case"base64":return this.ToBase64(buf);case"base64url":return this.ToBase64Url(buf);case"utf16le":return Utf16Converter.toString(buf,!0);case"utf16":case"utf16be":return Utf16Converter.toString(buf);default:throw Error(`Unknown type of encoding '${enc}'`)}}static FromString(str3,enc="utf8"){if(!str3)return new ArrayBuffer(0);switch(enc.toLowerCase()){case"utf8":return this.FromUtf8String(str3);case"binary":return this.FromBinary(str3);case"hex":return this.FromHex(str3);case"base64":return this.FromBase64(str3);case"base64url":return this.FromBase64Url(str3);case"utf16le":return Utf16Converter.fromString(str3,!0);case"utf16":case"utf16be":return Utf16Converter.fromString(str3);default:throw Error(`Unknown type of encoding '${enc}'`)}}static ToBase64(buffer){let buf=BufferSourceConverter.toUint8Array(buffer);if(typeof btoa<"u"){let binary=this.ToString(buf,"binary");return btoa(binary)}else return Buffer.from(buf).toString("base64")}static FromBase64(base642){let formatted=this.formatString(base642);if(!formatted)return new ArrayBuffer(0);if(!Convert.isBase64(formatted))throw TypeError("Argument 'base64Text' is not Base64 encoded");if(typeof atob<"u")return this.FromBinary(atob(formatted));else return new Uint8Array(Buffer.from(formatted,"base64")).buffer}static FromBase64Url(base64url){let formatted=this.formatString(base64url);if(!formatted)return new ArrayBuffer(0);if(!Convert.isBase64Url(formatted))throw TypeError("Argument 'base64url' is not Base64Url encoded");return this.FromBase64(this.Base64Padding(formatted.replace(/\-/g,"+").replace(/\_/g,"/")))}static ToBase64Url(data){return this.ToBase64(data).replace(/\+/g,"-").replace(/\//g,"_").replace(/\=/g,"")}static FromUtf8String(text,encoding=Convert.DEFAULT_UTF8_ENCODING){switch(encoding){case"ascii":return this.FromBinary(text);case"utf8":return Utf8Converter.fromString(text);case"utf16":case"utf16be":return Utf16Converter.fromString(text);case"utf16le":case"usc2":return Utf16Converter.fromString(text,!0);default:throw Error(`Unknown type of encoding '${encoding}'`)}}static ToUtf8String(buffer,encoding=Convert.DEFAULT_UTF8_ENCODING){switch(encoding){case"ascii":return this.ToBinary(buffer);case"utf8":return Utf8Converter.toString(buffer);case"utf16":case"utf16be":return Utf16Converter.toString(buffer);case"utf16le":case"usc2":return Utf16Converter.toString(buffer,!0);default:throw Error(`Unknown type of encoding '${encoding}'`)}}static FromBinary(text){let stringLength=text.length,resultView=new Uint8Array(stringLength);for(let i=0;i<stringLength;i++)resultView[i]=text.charCodeAt(i);return resultView.buffer}static ToBinary(buffer){let buf=BufferSourceConverter.toUint8Array(buffer),res="";for(let i=0;i<buf.length;i++)res+=String.fromCharCode(buf[i]);return res}static ToHex(buffer){let buf=BufferSourceConverter.toUint8Array(buffer),result="",len=buf.length;for(let i=0;i<len;i++){let byte=buf[i];if(byte<16)result+="0";result+=byte.toString(16)}return result}static FromHex(hexString){let formatted=this.formatString(hexString);if(!formatted)return new ArrayBuffer(0);if(!Convert.isHex(formatted))throw TypeError("Argument 'hexString' is not HEX encoded");if(formatted.length%2)formatted=`0${formatted}`;let res=new Uint8Array(formatted.length/2);for(let i=0;i<formatted.length;i=i+2){let c=formatted.slice(i,i+2);res[i/2]=parseInt(c,16)}return res.buffer}static ToUtf16String(buffer,littleEndian=!1){return Utf16Converter.toString(buffer,littleEndian)}static FromUtf16String(text,littleEndian=!1){return Utf16Converter.fromString(text,littleEndian)}static Base64Padding(base642){let padCount=4-base642.length%4;if(padCount<4)for(let i=0;i<padCount;i++)base642+="=";return base642}static formatString(data){return(data===null||data===void 0?void 0:data.replace(/[\n\r\t ]/g,""))||""}}function combine(...buf){let totalByteLength=buf.map((item)=>item.byteLength).reduce((prev,cur)=>prev+cur),res=new Uint8Array(totalByteLength),currentPos=0;return buf.map((item)=>new Uint8Array(item)).forEach((arr)=>{for(let item2 of arr)res[currentPos++]=item2}),res.buffer}function isEqual(bytes1,bytes2){if(!(bytes1&&bytes2))return!1;if(bytes1.byteLength!==bytes2.byteLength)return!1;let b1=new Uint8Array(bytes1),b2=new Uint8Array(bytes2);for(let i=0;i<bytes1.byteLength;i++)if(b1[i]!==b2[i])return!1;return!0}var STRING_TYPE="string",HEX_REGEX,BASE64_REGEX,BASE64URL_REGEX;var init_index_es=__esm(()=>{/*!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.813",
3
+ "version": "0.9.814",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -107,3 +107,31 @@ describe('numeric defaults are quoted', () => {
107
107
  expect(code).toContain(".default('0')")
108
108
  })
109
109
  })
110
+
111
+ /**
112
+ * An empty array default written as `{}` is an empty OBJECT in Postgres.
113
+ *
114
+ * Every row that never had a value then reads back as `{}` — and the first
115
+ * `for…of` over it throws "object is not iterable". Which is every existing row,
116
+ * the moment a new jsonb column is added to a table already in use: the panel
117
+ * that reads it dies on the next click, about a column nobody has filled in yet.
118
+ */
119
+ describe('jsonb defaults keep their own shape', () => {
120
+ const emit = (col: Record<string, unknown>) => generateColumnCode(col as never, [], 'widgets')
121
+
122
+ it('an empty array default stays an array', () => {
123
+ expect(emit({ name: 'reference_checks', type: 'jsonb', default: [] })).toContain("'[]'")
124
+ })
125
+
126
+ it('an empty object default stays an object', () => {
127
+ expect(emit({ name: 'settings', type: 'jsonb', default: {} })).toContain("'{}'")
128
+ })
129
+
130
+ it('carries a default that actually holds something', () => {
131
+ expect(emit({ name: 'flags', type: 'jsonb', default: { a: 1 } })).toContain('\'{"a":1}\'')
132
+ })
133
+
134
+ it('escapes a quote rather than ending the literal early', () => {
135
+ expect(emit({ name: 'labels', type: 'jsonb', default: ["it's"] })).toContain("''s")
136
+ })
137
+ })
@@ -212,7 +212,11 @@ export function selectEmittedTables<T extends { table_name: string }>(
212
212
  )
213
213
  }
214
214
 
215
- export function generateColumnCode(col: Column, allTables: string[], currentTable?: string): string {
215
+ export function generateColumnCode(
216
+ col: Column,
217
+ allTables: string[],
218
+ currentTable?: string
219
+ ): string {
216
220
  const drizzleType = TYPE_MAP[col.type] || 'text'
217
221
  let code = ''
218
222
 
@@ -273,10 +277,13 @@ export function generateColumnCode(col: Column, allTables: string[], currentTabl
273
277
  // that ran fine and failed `tsc`, which is how it survived: the file is
274
278
  // generated, so nobody reads it, and only a typecheck ever objected.
275
279
  code += isNumericType(col.type) ? `.default('${col.default}')` : `.default(${col.default})`
276
- } else if (Array.isArray(col.default)) {
277
- code += `.default(sql\`'{}'\`)`
278
- } else if (typeof col.default === 'object') {
279
- code += `.default(sql\`'{}'\`)`
280
+ } else if (typeof col.default === 'object' && col.default !== null) {
281
+ // Serialised properly, because `[]` written as `{}` is an empty OBJECT in
282
+ // Postgres. Every reader that iterates the column then throws "object is
283
+ // not iterable" the first time a row that never had a value is read —
284
+ // which is every existing row, the moment the column is added.
285
+ const literal = JSON.stringify(col.default).replace(/'/g, "''")
286
+ code += `.default(sql\`'${literal}'\`)`
280
287
  }
281
288
  }
282
289