@undefineds.co/xpod 0.2.8 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/auth/NodeTokenAuthenticator.js +10 -12
- package/dist/api/auth/NodeTokenAuthenticator.js.map +1 -1
- package/dist/api/container/index.js +3 -2
- package/dist/api/container/index.js.map +1 -1
- package/dist/identity/drizzle/DdnsRepository.d.ts +1 -0
- package/dist/identity/drizzle/DdnsRepository.js +35 -57
- package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
- package/dist/identity/drizzle/db.js +23 -0
- package/dist/identity/drizzle/db.js.map +1 -1
- package/dist/subdomain/SubdomainClient.js +1 -2
- package/dist/subdomain/SubdomainClient.js.map +1 -1
- package/package.json +1 -1
- package/static/app/assets/index.css +1 -1
- package/static/app/assets/main.js +16 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/identity/drizzle/db.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,8BAEC;AAgCD,kCAEC;AAMD,kDAqDC;AAKD,8CAQC;AAMD,wDAMC;AAED,kEAGC;AAOD,4CAKC;AAiBD,oCAYC;AAMD,4CAYC;AAMD,sCAEC;AAMD,0CAcC;AAzOD,2BAAiC;AACjC,6DAAiE;AAEjE,sDAAwC;AACxC,8DAAgD;AAChD,oFAA8F;AAC9F,+DAAoF;AAOpF;;;;;;;GAOG;AACH,SAAgB,SAAS,CAAC,EAAoB;IAC5C,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC;AACxD,CAAC;AAgBD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;AACpD,MAAM,cAAc,GAAG,IAAI,OAAO,EAAyB,CAAC;AAE5D,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAI9B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;IAC5B,iEAAiE;IACjE,gEAAgE;IAChE,UAAK,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW,CAAC,gBAAwB;IAClD,OAAO,gBAAgB,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,gBAAwB;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC,EAAE,CAAC;IACnB,CAAC;IAED,IAAI,WAAW,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,QAAQ,KAAK,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC5E,MAAM,aAAa,GAAG,IAAA,gCAAgB,GAAE,CAAC;QACzC,MAAM,MAAM,GAAG,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAE5E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,EAAE,GAAG,aAAa,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAEvD,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAE3B,cAAc,CAAC,GAAG,CAAC,EAAY,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE;YAC5B,EAAE;YACF,MAAM,EAAE,YAAY;YACpB,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;SACvC,CAAC,CAAC;QACH,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,2EAA2E;IAC3E,MAAM,IAAI,GAAG,IAAA,mCAAa,EAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,IAAA,uBAAS,EAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,WAAW,GAAG,CAAC,KAAK,IAAkB,EAAE;QAC5C,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC;IACL,cAAc,CAAC,GAAG,CAAC,EAAY,EAAE,WAAW,CAAC,CAAC;IAC9C,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACxB,OAAO,CAAC,KAAK,CAAC,qCAAqC,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE;QAC5B,EAAE;QACF,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,wDAAwD;YACxD,IAAA,uCAAiB,EAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1C,CAAC;KACF,CAAC,CAAC;IACH,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,gBAAwB;IACxD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,0CAA0C;IAC1C,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;IACtC,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAE,CAAC,MAAM,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,SAAgB,sBAAsB,CAAC,gBAAwB;IAC7D,IAAI,CAAC;QACH,OAAO,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,2BAA2B;IAC/C,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,EAAoB;IACnD,IAAK,EAAU,EAAE,kBAAkB,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,EAAE,CAAC,GAAG,KAAK,UAAU,IAAI,OAAO,EAAE,CAAC,OAAO,KAAK,UAAU,CAAC;AAC1E,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,EAAoB;IACrD,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,EAAY,CAAC,CAAC;IACrD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,WAAW,CAAC;IACpB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,YAAY,CAChC,EAAoB,EACpB,KAAU;IAEV,MAAM,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,gBAAgB,CAAC,EAAE,CAAC,EAAE,CAAC;QACzB,0CAA0C;QAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAQ,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IACD,mDAAmD;IACnD,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAA4B,CAAC;AACtD,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,gBAAgB,CACpC,EAAoB,EACpB,KAAU;IAEV,MAAM,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,gBAAgB,CAAC,EAAE,CAAC,EAAE,CAAC;QACzB,kCAAkC;QAClC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACd,OAAO;IACT,CAAC;IACD,oDAAoD;IACpD,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,EAAoB,EAAE,IAAU;IAC5D,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,MAAyB;IACnD,MAAM,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFX,CAAC,CAAC;IAEH,sDAAsD;IACtD,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,MAAyB;IACrD,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,IAAY,EAAQ,EAAE;QACtE,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,eAAe,KAAK,eAAe,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,EAAE,kBAAkB,CAAC,EAAE,CAAC;QACzE,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,gGAAgG;QAClG,CAAC;IACH,CAAC;IACD,MAAM,eAAe,GAA4B;QAC/C,CAAE,WAAW,EAAE,qBAAqB,CAAE;QACtC,CAAE,WAAW,EAAE,MAAM,CAAE;QACvB,CAAE,aAAa,EAAE,MAAM,CAAE;QACzB,CAAE,MAAM,EAAE,MAAM,CAAE;QAClB,CAAE,aAAa,EAAE,SAAS,CAAE;QAC5B,CAAE,YAAY,EAAE,MAAM,CAAE;QACxB,CAAE,oBAAoB,EAAE,MAAM,CAAE;QAChC,CAAE,qBAAqB,EAAE,MAAM,CAAE;QACjC,CAAE,aAAa,EAAE,MAAM,CAAE;QACzB,CAAE,eAAe,EAAE,SAAS,CAAE;QAC9B,CAAE,UAAU,EAAE,MAAM,CAAE;QACtB,CAAE,MAAM,EAAE,MAAM,CAAE;QAClB,CAAE,SAAS,EAAE,MAAM,CAAE;QACrB,CAAE,cAAc,EAAE,MAAM,CAAE;QAC1B,CAAE,UAAU,EAAE,MAAM,CAAE;QACtB,CAAE,qBAAqB,EAAE,wBAAwB,CAAE;QACnD,CAAE,yBAAyB,EAAE,SAAS,CAAE;QACxC,CAAE,WAAW,EAAE,SAAS,CAAE;KAC3B,CAAC;IACF,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;QAC7C,SAAS,CAAC,oBAAoB,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,sCAAsC;IACtC,SAAS,CAAC,wBAAwB,EAAE,iBAAiB,EAAE,4BAA4B,CAAC,CAAC;IACrF,SAAS,CAAC,wBAAwB,EAAE,aAAa,EAAE,4BAA4B,CAAC,CAAC;IACjF,SAAS,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,SAAS,CAAC,CAAC;IACxE,SAAS,CAAC,wBAAwB,EAAE,qBAAqB,EAAE,SAAS,CAAC,CAAC;IACtE,SAAS,CAAC,wBAAwB,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;IAC/D,SAAS,CAAC,oBAAoB,EAAE,iBAAiB,EAAE,4BAA4B,CAAC,CAAC;IACjF,SAAS,CAAC,oBAAoB,EAAE,aAAa,EAAE,4BAA4B,CAAC,CAAC;IAC7E,SAAS,CAAC,oBAAoB,EAAE,uBAAuB,EAAE,SAAS,CAAC,CAAC;IACpE,SAAS,CAAC,oBAAoB,EAAE,qBAAqB,EAAE,SAAS,CAAC,CAAC;IAClE,SAAS,CAAC,oBAAoB,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAyB,EAAE,KAAa,EAAE,MAAc;IAClF,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAmB,qBAAqB,KAAK,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IACnF,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,gBAAgB,CAAC,IAA8C;IAC5E,MAAM,SAAS,GAAG,KAAK,EAAE,KAAa,EAAE,MAAc,EAAE,IAAY,EAAiB,EAAE;QACrF,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CACd;;;kCAG0B,KAAK,wBAAwB,MAAM;;0BAE3C,KAAK,eAAe,MAAM,IAAI,IAAI;;gBAE5C,CACT,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC,CAAC;IAEF,sCAAsC;IACtC,MAAM,SAAS,CAAC,wBAAwB,EAAE,iBAAiB,EAAE,2BAA2B,CAAC,CAAC;IAC1F,MAAM,SAAS,CAAC,wBAAwB,EAAE,aAAa,EAAE,2BAA2B,CAAC,CAAC;IACtF,MAAM,SAAS,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,QAAQ,CAAC,CAAC;IAC7E,MAAM,SAAS,CAAC,wBAAwB,EAAE,qBAAqB,EAAE,QAAQ,CAAC,CAAC;IAC3E,MAAM,SAAS,CAAC,wBAAwB,EAAE,cAAc,EAAE,0BAA0B,CAAC,CAAC;IACtF,MAAM,SAAS,CAAC,oBAAoB,EAAE,iBAAiB,EAAE,2BAA2B,CAAC,CAAC;IACtF,MAAM,SAAS,CAAC,oBAAoB,EAAE,aAAa,EAAE,2BAA2B,CAAC,CAAC;IAClF,MAAM,SAAS,CAAC,oBAAoB,EAAE,uBAAuB,EAAE,QAAQ,CAAC,CAAC;IACzE,MAAM,SAAS,CAAC,oBAAoB,EAAE,qBAAqB,EAAE,QAAQ,CAAC,CAAC;IACvE,MAAM,SAAS,CAAC,oBAAoB,EAAE,cAAc,EAAE,0BAA0B,CAAC,CAAC;IAElF,sBAAsB;IACtB,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;KAUhB,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;AACH,CAAC;AAGD,KAAK,UAAU,oBAAoB,CAAC,IAAU;IAC5C,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DhB,CAAC,CAAC;IAEH,MAAM,sBAAsB,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,IAAU;IAC9C,MAAM,SAAS,GAAG,KAAK,EAAE,KAAa,EAAE,MAAc,EAAE,IAAY,EAAiB,EAAE;QACrF,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,KAAK,6BAA6B,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACtF,CAAC,CAAC;IAEF,MAAM,IAAI,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC1F,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;GAehB,CAAC,CAAC;IAEH,MAAM,eAAe,GAA4B;QAC/C,CAAE,WAAW,EAAE,qBAAqB,CAAE;QACtC,CAAE,WAAW,EAAE,MAAM,CAAE;QACvB,CAAE,aAAa,EAAE,MAAM,CAAE;QACzB,CAAE,MAAM,EAAE,MAAM,CAAE;QAClB,CAAE,aAAa,EAAE,QAAQ,CAAE;QAC3B,CAAE,YAAY,EAAE,MAAM,CAAE;QACxB,CAAE,oBAAoB,EAAE,MAAM,CAAE;QAChC,CAAE,qBAAqB,EAAE,MAAM,CAAE;QACjC,CAAE,aAAa,EAAE,MAAM,CAAE;QACzB,CAAE,eAAe,EAAE,QAAQ,CAAE;QAC7B,CAAE,UAAU,EAAE,MAAM,CAAE;QACtB,CAAE,MAAM,EAAE,MAAM,CAAE;QAClB,CAAE,SAAS,EAAE,MAAM,CAAE;QACrB,CAAE,cAAc,EAAE,OAAO,CAAE;QAC3B,CAAE,UAAU,EAAE,OAAO,CAAE;QACvB,CAAE,qBAAqB,EAAE,wBAAwB,CAAE;QACnD,CAAE,yBAAyB,EAAE,aAAa,CAAE;QAC5C,CAAE,WAAW,EAAE,aAAa,CAAE;KAC/B,CAAC;IACF,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;QAC7C,MAAM,SAAS,CAAC,oBAAoB,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACtD,CAAC;AACH,CAAC","sourcesContent":["import { Pool, types } from 'pg';\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport type { SQL } from 'drizzle-orm/sql';\nimport * as pgSchema from './schema.pg';\nimport * as sqliteSchema from './schema.sqlite';\nimport { getSharedPool, releaseSharedPool } from '../../storage/database/PostgresPoolManager';\nimport { getSqliteRuntime, type SqliteDatabase } from '../../storage/SqliteRuntime';\n\n// Use 'any' to allow both PostgreSQL and SQLite database instances\n// The actual type depends on the connection string at runtime\nexport type IdentityDatabase = any;\nexport type IdentitySchema = typeof pgSchema | typeof sqliteSchema;\n\n/**\n * Get the appropriate schema for the given database connection.\n * This provides a unified abstraction layer over PG and SQLite schemas.\n *\n * @example\n * const schema = getSchema(db);\n * await db.select().from(schema.accountUsage).where(eq(schema.accountUsage.accountId, id));\n */\nexport function getSchema(db: IdentityDatabase): typeof pgSchema | typeof sqliteSchema {\n return isDatabaseSqlite(db) ? sqliteSchema : pgSchema;\n}\n\n/**\n * Standardized query result format across databases.\n */\nexport interface QueryResult<T = Record<string, unknown>> {\n rows: T[];\n}\n\ninterface CachedConnection {\n db: IdentityDatabase;\n schema: IdentitySchema;\n isSqlite: boolean;\n close: () => Promise<void>;\n}\n\nconst dbCache = new Map<string, CachedConnection>();\nconst dbInitPromises = new WeakMap<object, Promise<void>>();\n\nconst JSON_OIDS = [114, 3802];\n\ntype SqliteDdlExecutor = Pick<SqliteDatabase, 'exec' | 'prepare'>;\n\nfor (const oid of JSON_OIDS) {\n // Explicitly return raw string to avoid \"Type Conflict\" with CSS\n // and to satisfy PgQuintStore's parseVector expecting a string.\n types.setTypeParser(oid, (value) => value);\n}\n\n/**\n * Returns true if the connection string is a SQLite URL.\n */\nexport function isSqliteUrl(connectionString: string): boolean {\n return connectionString.startsWith('sqlite:');\n}\n\n/**\n * Get or create a Drizzle database connection with the appropriate schema.\n * Supports both PostgreSQL and SQLite.\n */\nexport function getIdentityDatabase(connectionString: string): IdentityDatabase {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.db;\n }\n\n if (isSqliteUrl(connectionString)) {\n const filename = connectionString.replace('sqlite:', '');\n const isMemory = filename === ':memory:' || filename.startsWith(':memory:');\n const sqliteRuntime = getSqliteRuntime();\n const sqlite = sqliteRuntime.openDatabase(isMemory ? ':memory:' : filename);\n\n if (!isMemory) {\n sqlite.pragma('journal_mode = WAL');\n sqlite.pragma('busy_timeout = 5000');\n sqlite.pragma('synchronous = NORMAL');\n }\n\n const db = sqliteRuntime.createDrizzleDatabase(sqlite);\n\n ensureSqliteTables(sqlite);\n\n dbInitPromises.set(db as object, Promise.resolve());\n dbCache.set(connectionString, {\n db,\n schema: sqliteSchema,\n isSqlite: true,\n close: async () => { sqlite.close(); },\n });\n return db;\n }\n\n // PostgreSQL: use shared pool to avoid connection exhaustion and deadlocks\n const pool = getSharedPool({ connectionString });\n const db = drizzlePg(pool);\n const initPromise = (async(): Promise<void> => {\n await ensurePostgresTables(pool);\n await migratePgColumns(pool);\n })();\n dbInitPromises.set(db as object, initPromise);\n initPromise.catch((err) => {\n console.error(`[IdentityDB] PG migration failed: ${err}`);\n });\n dbCache.set(connectionString, {\n db,\n schema: pgSchema,\n isSqlite: false,\n close: async () => { \n // Release reference to shared pool instead of ending it\n releaseSharedPool({ connectionString }); \n },\n });\n return db;\n}\n\n/**\n * Get the schema for a given connection string.\n */\nexport function getIdentitySchema(connectionString: string): IdentitySchema {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.schema;\n }\n // Initialize connection to populate cache\n getIdentityDatabase(connectionString);\n return dbCache.get(connectionString)!.schema;\n}\n\n/**\n * Safely get a Drizzle database connection, returning undefined on error.\n * Use this when the identity database is optional (e.g., for usage tracking).\n */\nexport function tryGetIdentityDatabase(connectionString: string): IdentityDatabase | undefined {\n try {\n return getIdentityDatabase(connectionString);\n } catch {\n return undefined;\n }\n}\n\nexport async function closeAllIdentityConnections(): Promise<void> {\n await Promise.all([...dbCache.values()].map(({ close }) => close()));\n dbCache.clear();\n}\n\n/**\n * Check if a database connection is SQLite.\n * SQLite drizzle has `all()` method but no `execute()` method.\n * PostgreSQL drizzle has `execute()` method but no `all()` method.\n */\nexport function isDatabaseSqlite(db: IdentityDatabase): boolean {\n if ((db as any)?.$xpodSqliteRuntime) {\n return true;\n }\n return typeof db.all === 'function' && typeof db.execute !== 'function';\n}\n\nasync function ensureDatabaseReady(db: IdentityDatabase): Promise<void> {\n const initPromise = dbInitPromises.get(db as object);\n if (initPromise) {\n await initPromise;\n }\n}\n\n/**\n * Execute a SQL query uniformly across PostgreSQL and SQLite.\n * Returns a standardized result with rows array.\n *\n * @example\n * const result = await executeQuery(db, sql`SELECT * FROM users WHERE id = ${userId}`);\n * if (result.rows.length > 0) { ... }\n */\nexport async function executeQuery<T = Record<string, unknown>>(\n db: IdentityDatabase,\n query: SQL,\n): Promise<QueryResult<T>> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.all() returns array directly\n const rows = db.all(query) as T[];\n return { rows };\n }\n // PostgreSQL: db.execute() returns { rows: [...] }\n return db.execute(query) as Promise<QueryResult<T>>;\n}\n\n/**\n * Execute a SQL statement that doesn't return rows (INSERT, UPDATE, DELETE).\n * Works uniformly across PostgreSQL and SQLite.\n */\nexport async function executeStatement(\n db: IdentityDatabase,\n query: SQL,\n): Promise<void> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.run() for statements\n db.run(query);\n return;\n }\n // PostgreSQL: db.execute() works for statements too\n await db.execute(query);\n}\n\n/**\n * Convert a Date to a value suitable for the database.\n * SQLite uses Unix timestamps (seconds), PostgreSQL uses Date objects.\n */\nexport function toDbTimestamp(db: IdentityDatabase, date: Date): number | Date {\n return isDatabaseSqlite(db) ? Math.floor(date.getTime() / 1000) : date;\n}\n\n/**\n * Parse a timestamp value from database result to Date.\n * Handles both Unix timestamps (SQLite) and Date objects (PostgreSQL).\n */\nexport function fromDbTimestamp(value: unknown): Date | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n if (value instanceof Date) {\n return value;\n }\n if (typeof value === 'number') {\n return new Date(value * 1000);\n }\n if (typeof value === 'string') {\n return new Date(value);\n }\n return undefined;\n}\n\n/**\n * Ensure SQLite tables exist (simple DDL for local/dev mode).\n */\nfunction ensureSqliteTables(sqlite: SqliteDdlExecutor): void {\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port INTEGER,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port INTEGER,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities TEXT,\n metadata TEXT,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check INTEGER,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n last_seen INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n expires_at INTEGER\n );\n `);\n\n // Migrate existing tables: add new columns if missing\n migrateSqliteColumns(sqlite);\n}\n\n/**\n * Add columns that may be missing from older databases.\n * SQLite ALTER TABLE ADD COLUMN is idempotent-safe via try/catch.\n */\nfunction migrateSqliteColumns(sqlite: SqliteDdlExecutor): void {\n const addColumn = (table: string, column: string, type: string): void => {\n try {\n sqlite.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);\n } catch {\n // Column already exists — ignore\n }\n };\n\n if (sqliteColumnExists(sqlite, 'identity_edge_node', 'owner_account_id')) {\n try {\n sqlite.exec('ALTER TABLE identity_edge_node DROP COLUMN owner_account_id');\n } catch {\n // Older SQLite runtimes may not support DROP COLUMN. Ignore and keep runtime-compatible schema.\n }\n }\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'INTEGER' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'INTEGER' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'TEXT' ],\n [ 'metadata', 'TEXT' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'INTEGER' ],\n [ 'last_seen', 'INTEGER' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n addColumn('identity_edge_node', column, type);\n }\n\n // Usage tables: compute/token columns\n addColumn('identity_account_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_account_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_account_usage', 'period_start', 'INTEGER');\n addColumn('identity_pod_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_pod_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_pod_usage', 'period_start', 'INTEGER');\n}\n\nfunction sqliteColumnExists(sqlite: SqliteDdlExecutor, table: string, column: string): boolean {\n const rows = sqlite.prepare<{ name: string }>(`PRAGMA table_info(${table})`).all();\n return rows.some((row) => row.name === column);\n}\n\n/**\n * Add columns that may be missing from older PostgreSQL databases.\n * Uses IF NOT EXISTS via information_schema check + ALTER TABLE.\n */\nasync function migratePgColumns(pool: { query: (sql: string) => Promise<any> }): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n try {\n await pool.query(\n `DO $$ BEGIN\n IF NOT EXISTS (\n SELECT 1 FROM information_schema.columns\n WHERE table_name = '${table}' AND column_name = '${column}'\n ) THEN\n ALTER TABLE ${table} ADD COLUMN ${column} ${type};\n END IF;\n END $$;`,\n );\n } catch {\n // Ignore errors (table might not exist yet)\n }\n };\n\n // Usage tables: compute/token columns\n await addColumn('identity_account_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_account_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_account_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n await addColumn('identity_pod_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_pod_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_pod_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n\n // Service token table\n try {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMP WITH TIME ZONE\n );\n `);\n } catch {\n // Ignore if already exists\n }\n}\n\n\nasync function ensurePostgresTables(pool: Pool): Promise<void> {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port BIGINT,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port BIGINT,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities JSONB,\n metadata JSONB,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n last_seen TIMESTAMPTZ\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n `);\n\n await migratePostgresColumns(pool);\n}\n\nasync function migratePostgresColumns(pool: Pool): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n await pool.query(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${column} ${type}`);\n };\n\n await pool.query('ALTER TABLE identity_edge_node DROP COLUMN IF EXISTS owner_account_id');\n await pool.query(`\n DO $$\n BEGIN\n IF EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'public_ip'\n ) AND NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'ipv4'\n ) THEN\n ALTER TABLE identity_edge_node RENAME COLUMN public_ip TO ipv4;\n END IF;\n END $$;\n `);\n\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'BIGINT' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'BIGINT' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'JSONB' ],\n [ 'metadata', 'JSONB' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'TIMESTAMPTZ' ],\n [ 'last_seen', 'TIMESTAMPTZ' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n await addColumn('identity_edge_node', column, type);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/identity/drizzle/db.ts"],"names":[],"mappings":"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","sourcesContent":["import { Pool, types } from 'pg';\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport type { SQL } from 'drizzle-orm/sql';\nimport * as pgSchema from './schema.pg';\nimport * as sqliteSchema from './schema.sqlite';\nimport { getSharedPool, releaseSharedPool } from '../../storage/database/PostgresPoolManager';\nimport { getSqliteRuntime, type SqliteDatabase } from '../../storage/SqliteRuntime';\n\n// Use 'any' to allow both PostgreSQL and SQLite database instances\n// The actual type depends on the connection string at runtime\nexport type IdentityDatabase = any;\nexport type IdentitySchema = typeof pgSchema | typeof sqliteSchema;\n\n/**\n * Get the appropriate schema for the given database connection.\n * This provides a unified abstraction layer over PG and SQLite schemas.\n *\n * @example\n * const schema = getSchema(db);\n * await db.select().from(schema.accountUsage).where(eq(schema.accountUsage.accountId, id));\n */\nexport function getSchema(db: IdentityDatabase): typeof pgSchema | typeof sqliteSchema {\n return isDatabaseSqlite(db) ? sqliteSchema : pgSchema;\n}\n\n/**\n * Standardized query result format across databases.\n */\nexport interface QueryResult<T = Record<string, unknown>> {\n rows: T[];\n}\n\ninterface CachedConnection {\n db: IdentityDatabase;\n schema: IdentitySchema;\n isSqlite: boolean;\n close: () => Promise<void>;\n}\n\nconst dbCache = new Map<string, CachedConnection>();\nconst dbInitPromises = new WeakMap<object, Promise<void>>();\n\nconst JSON_OIDS = [114, 3802];\n\ntype SqliteDdlExecutor = Pick<SqliteDatabase, 'exec' | 'prepare'>;\n\nfor (const oid of JSON_OIDS) {\n // Explicitly return raw string to avoid \"Type Conflict\" with CSS\n // and to satisfy PgQuintStore's parseVector expecting a string.\n types.setTypeParser(oid, (value) => value);\n}\n\n/**\n * Returns true if the connection string is a SQLite URL.\n */\nexport function isSqliteUrl(connectionString: string): boolean {\n return connectionString.startsWith('sqlite:');\n}\n\n/**\n * Get or create a Drizzle database connection with the appropriate schema.\n * Supports both PostgreSQL and SQLite.\n */\nexport function getIdentityDatabase(connectionString: string): IdentityDatabase {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.db;\n }\n\n if (isSqliteUrl(connectionString)) {\n const filename = connectionString.replace('sqlite:', '');\n const isMemory = filename === ':memory:' || filename.startsWith(':memory:');\n const sqliteRuntime = getSqliteRuntime();\n const sqlite = sqliteRuntime.openDatabase(isMemory ? ':memory:' : filename);\n\n if (!isMemory) {\n sqlite.pragma('journal_mode = WAL');\n sqlite.pragma('busy_timeout = 5000');\n sqlite.pragma('synchronous = NORMAL');\n }\n\n const db = sqliteRuntime.createDrizzleDatabase(sqlite);\n\n ensureSqliteTables(sqlite);\n\n dbInitPromises.set(db as object, Promise.resolve());\n dbCache.set(connectionString, {\n db,\n schema: sqliteSchema,\n isSqlite: true,\n close: async () => { sqlite.close(); },\n });\n return db;\n }\n\n // PostgreSQL: use shared pool to avoid connection exhaustion and deadlocks\n const pool = getSharedPool({ connectionString });\n const db = drizzlePg(pool);\n const initPromise = (async(): Promise<void> => {\n await ensurePostgresTables(pool);\n await migratePgColumns(pool);\n })();\n dbInitPromises.set(db as object, initPromise);\n initPromise.catch((err) => {\n console.error(`[IdentityDB] PG migration failed: ${err}`);\n });\n dbCache.set(connectionString, {\n db,\n schema: pgSchema,\n isSqlite: false,\n close: async () => { \n // Release reference to shared pool instead of ending it\n releaseSharedPool({ connectionString }); \n },\n });\n return db;\n}\n\n/**\n * Get the schema for a given connection string.\n */\nexport function getIdentitySchema(connectionString: string): IdentitySchema {\n const cached = dbCache.get(connectionString);\n if (cached) {\n return cached.schema;\n }\n // Initialize connection to populate cache\n getIdentityDatabase(connectionString);\n return dbCache.get(connectionString)!.schema;\n}\n\n/**\n * Safely get a Drizzle database connection, returning undefined on error.\n * Use this when the identity database is optional (e.g., for usage tracking).\n */\nexport function tryGetIdentityDatabase(connectionString: string): IdentityDatabase | undefined {\n try {\n return getIdentityDatabase(connectionString);\n } catch {\n return undefined;\n }\n}\n\nexport async function closeAllIdentityConnections(): Promise<void> {\n await Promise.all([...dbCache.values()].map(({ close }) => close()));\n dbCache.clear();\n}\n\n/**\n * Check if a database connection is SQLite.\n * SQLite drizzle has `all()` method but no `execute()` method.\n * PostgreSQL drizzle has `execute()` method but no `all()` method.\n */\nexport function isDatabaseSqlite(db: IdentityDatabase): boolean {\n if ((db as any)?.$xpodSqliteRuntime) {\n return true;\n }\n return typeof db.all === 'function' && typeof db.execute !== 'function';\n}\n\nasync function ensureDatabaseReady(db: IdentityDatabase): Promise<void> {\n const initPromise = dbInitPromises.get(db as object);\n if (initPromise) {\n await initPromise;\n }\n}\n\n/**\n * Execute a SQL query uniformly across PostgreSQL and SQLite.\n * Returns a standardized result with rows array.\n *\n * @example\n * const result = await executeQuery(db, sql`SELECT * FROM users WHERE id = ${userId}`);\n * if (result.rows.length > 0) { ... }\n */\nexport async function executeQuery<T = Record<string, unknown>>(\n db: IdentityDatabase,\n query: SQL,\n): Promise<QueryResult<T>> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.all() returns array directly\n const rows = db.all(query) as T[];\n return { rows };\n }\n // PostgreSQL: db.execute() returns { rows: [...] }\n return db.execute(query) as Promise<QueryResult<T>>;\n}\n\n/**\n * Execute a SQL statement that doesn't return rows (INSERT, UPDATE, DELETE).\n * Works uniformly across PostgreSQL and SQLite.\n */\nexport async function executeStatement(\n db: IdentityDatabase,\n query: SQL,\n): Promise<void> {\n await ensureDatabaseReady(db);\n if (isDatabaseSqlite(db)) {\n // SQLite: db.run() for statements\n db.run(query);\n return;\n }\n // PostgreSQL: db.execute() works for statements too\n await db.execute(query);\n}\n\n/**\n * Convert a Date to a value suitable for the database.\n * SQLite uses Unix timestamps (seconds), PostgreSQL uses Date objects.\n */\nexport function toDbTimestamp(db: IdentityDatabase, date: Date): number | Date {\n return isDatabaseSqlite(db) ? Math.floor(date.getTime() / 1000) : date;\n}\n\n/**\n * Parse a timestamp value from database result to Date.\n * Handles both Unix timestamps (SQLite) and Date objects (PostgreSQL).\n */\nexport function fromDbTimestamp(value: unknown): Date | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n if (value instanceof Date) {\n return value;\n }\n if (typeof value === 'number') {\n return new Date(value * 1000);\n }\n if (typeof value === 'string') {\n return new Date(value);\n }\n return undefined;\n}\n\n/**\n * Ensure SQLite tables exist (simple DDL for local/dev mode).\n */\nfunction ensureSqliteTables(sqlite: SqliteDdlExecutor): void {\n sqlite.exec(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes INTEGER NOT NULL DEFAULT 0,\n ingress_bytes INTEGER NOT NULL DEFAULT 0,\n egress_bytes INTEGER NOT NULL DEFAULT 0,\n storage_limit_bytes INTEGER,\n bandwidth_limit_bps INTEGER,\n compute_seconds INTEGER NOT NULL DEFAULT 0,\n tokens_used INTEGER NOT NULL DEFAULT 0,\n compute_limit_seconds INTEGER,\n token_limit_monthly INTEGER,\n period_start INTEGER,\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port INTEGER,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port INTEGER,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities TEXT,\n metadata TEXT,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check INTEGER,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n last_seen INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_ddns_domain (\n domain TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n provider TEXT,\n zone_id TEXT,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_ddns_record (\n subdomain TEXT PRIMARY KEY,\n domain TEXT NOT NULL,\n ip_address TEXT,\n ipv6_address TEXT,\n record_type TEXT DEFAULT 'A',\n node_id TEXT,\n username TEXT,\n status TEXT DEFAULT 'active',\n banned_reason TEXT,\n ttl INTEGER DEFAULT 60,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n expires_at INTEGER\n );\n `);\n\n // Migrate existing tables: add new columns if missing\n migrateSqliteColumns(sqlite);\n}\n\n/**\n * Add columns that may be missing from older databases.\n * SQLite ALTER TABLE ADD COLUMN is idempotent-safe via try/catch.\n */\nfunction migrateSqliteColumns(sqlite: SqliteDdlExecutor): void {\n const addColumn = (table: string, column: string, type: string): void => {\n try {\n sqlite.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);\n } catch {\n // Column already exists — ignore\n }\n };\n\n if (sqliteColumnExists(sqlite, 'identity_edge_node', 'owner_account_id')) {\n try {\n sqlite.exec('ALTER TABLE identity_edge_node DROP COLUMN owner_account_id');\n } catch {\n // Older SQLite runtimes may not support DROP COLUMN. Ignore and keep runtime-compatible schema.\n }\n }\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'INTEGER' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'INTEGER' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'TEXT' ],\n [ 'metadata', 'TEXT' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'INTEGER' ],\n [ 'last_seen', 'INTEGER' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n addColumn('identity_edge_node', column, type);\n }\n\n // Usage tables: compute/token columns\n addColumn('identity_account_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_account_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_account_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_account_usage', 'period_start', 'INTEGER');\n addColumn('identity_pod_usage', 'compute_seconds', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'tokens_used', 'INTEGER NOT NULL DEFAULT 0');\n addColumn('identity_pod_usage', 'compute_limit_seconds', 'INTEGER');\n addColumn('identity_pod_usage', 'token_limit_monthly', 'INTEGER');\n addColumn('identity_pod_usage', 'period_start', 'INTEGER');\n}\n\nfunction sqliteColumnExists(sqlite: SqliteDdlExecutor, table: string, column: string): boolean {\n const rows = sqlite.prepare<{ name: string }>(`PRAGMA table_info(${table})`).all();\n return rows.some((row) => row.name === column);\n}\n\n/**\n * Add columns that may be missing from older PostgreSQL databases.\n * Uses IF NOT EXISTS via information_schema check + ALTER TABLE.\n */\nasync function migratePgColumns(pool: { query: (sql: string) => Promise<any> }): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n try {\n await pool.query(\n `DO $$ BEGIN\n IF NOT EXISTS (\n SELECT 1 FROM information_schema.columns\n WHERE table_name = '${table}' AND column_name = '${column}'\n ) THEN\n ALTER TABLE ${table} ADD COLUMN ${column} ${type};\n END IF;\n END $$;`,\n );\n } catch {\n // Ignore errors (table might not exist yet)\n }\n };\n\n // Usage tables: compute/token columns\n await addColumn('identity_account_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_account_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_account_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_account_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n await addColumn('identity_pod_usage', 'compute_seconds', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'tokens_used', 'BIGINT NOT NULL DEFAULT 0');\n await addColumn('identity_pod_usage', 'compute_limit_seconds', 'BIGINT');\n await addColumn('identity_pod_usage', 'token_limit_monthly', 'BIGINT');\n await addColumn('identity_pod_usage', 'period_start', 'TIMESTAMP WITH TIME ZONE');\n\n // Service token table\n try {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_service_token (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n service_type TEXT NOT NULL,\n service_id TEXT NOT NULL,\n scopes TEXT NOT NULL,\n created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMP WITH TIME ZONE\n );\n `);\n } catch {\n // Ignore if already exists\n }\n}\n\n\nasync function ensurePostgresTables(pool: Pool): Promise<void> {\n await pool.query(`\n CREATE TABLE IF NOT EXISTS identity_account_usage (\n account_id TEXT PRIMARY KEY,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_pod_usage (\n pod_id TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n storage_bytes BIGINT NOT NULL DEFAULT 0,\n ingress_bytes BIGINT NOT NULL DEFAULT 0,\n egress_bytes BIGINT NOT NULL DEFAULT 0,\n storage_limit_bytes BIGINT,\n bandwidth_limit_bps BIGINT,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node (\n id TEXT PRIMARY KEY,\n display_name TEXT,\n token_hash TEXT NOT NULL,\n account_id TEXT,\n node_type TEXT DEFAULT 'edge',\n subdomain TEXT UNIQUE,\n access_mode TEXT,\n ipv4 TEXT,\n public_port BIGINT,\n public_url TEXT,\n service_token_hash TEXT,\n provision_code_hash TEXT,\n internal_ip TEXT,\n internal_port BIGINT,\n hostname TEXT,\n ipv6 TEXT,\n version TEXT,\n capabilities JSONB,\n metadata JSONB,\n connectivity_status TEXT DEFAULT 'unknown',\n last_connectivity_check TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n last_seen TIMESTAMPTZ\n );\n\n CREATE TABLE IF NOT EXISTS identity_edge_node_pod (\n node_id TEXT NOT NULL REFERENCES identity_edge_node(id) ON DELETE CASCADE,\n base_url TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS api_client_credentials (\n client_id TEXT PRIMARY KEY,\n client_secret_encrypted TEXT NOT NULL,\n web_id TEXT NOT NULL,\n account_id TEXT NOT NULL,\n display_name TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n );\n `);\n\n await migratePostgresColumns(pool);\n}\n\nasync function migratePostgresColumns(pool: Pool): Promise<void> {\n const addColumn = async (table: string, column: string, type: string): Promise<void> => {\n await pool.query(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${column} ${type}`);\n };\n\n await pool.query('ALTER TABLE identity_edge_node DROP COLUMN IF EXISTS owner_account_id');\n await pool.query(`\n DO $$\n BEGIN\n IF EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'public_ip'\n ) AND NOT EXISTS (\n SELECT 1\n FROM information_schema.columns\n WHERE table_name = 'identity_edge_node' AND column_name = 'ipv4'\n ) THEN\n ALTER TABLE identity_edge_node RENAME COLUMN public_ip TO ipv4;\n END IF;\n END $$;\n `);\n\n const edgeNodeColumns: Array<[string, string]> = [\n [ 'node_type', `TEXT DEFAULT 'edge'` ],\n [ 'subdomain', 'TEXT' ],\n [ 'access_mode', 'TEXT' ],\n [ 'ipv4', 'TEXT' ],\n [ 'public_port', 'BIGINT' ],\n [ 'public_url', 'TEXT' ],\n [ 'service_token_hash', 'TEXT' ],\n [ 'provision_code_hash', 'TEXT' ],\n [ 'internal_ip', 'TEXT' ],\n [ 'internal_port', 'BIGINT' ],\n [ 'hostname', 'TEXT' ],\n [ 'ipv6', 'TEXT' ],\n [ 'version', 'TEXT' ],\n [ 'capabilities', 'JSONB' ],\n [ 'metadata', 'JSONB' ],\n [ 'connectivity_status', `TEXT DEFAULT 'unknown'` ],\n [ 'last_connectivity_check', 'TIMESTAMPTZ' ],\n [ 'last_seen', 'TIMESTAMPTZ' ],\n ];\n for (const [column, type] of edgeNodeColumns) {\n await addColumn('identity_edge_node', column, type);\n }\n}\n"]}
|
|
@@ -181,8 +181,7 @@ class SubdomainClient {
|
|
|
181
181
|
method: options.method,
|
|
182
182
|
headers: {
|
|
183
183
|
'Content-Type': 'application/json',
|
|
184
|
-
'Authorization': `
|
|
185
|
-
'X-Node-Id': this.nodeId,
|
|
184
|
+
'Authorization': `XpodNode ${this.nodeId}:${this.nodeToken}`,
|
|
186
185
|
},
|
|
187
186
|
body: options.body,
|
|
188
187
|
signal: controller.signal,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubdomainClient.js","sourceRoot":"","sources":["../../src/subdomain/SubdomainClient.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SubdomainClient - Local 模式子域名客户端\n * \n * Local 模式不持有 DNS/Tunnel 密钥,通过调用 Cloud API 来管理子域名。\n * 使用 Node Token 认证(与 Edge Node 心跳相同的认证方式)。\n */\n\nimport { getLoggerFor } from 'global-logger-factory';\n\nexport interface SubdomainClientOptions {\n /** Cloud API 端点 (如 https://center.example.com/v1/subdomain) */\n cloudApiEndpoint: string;\n \n /** 节点 ID */\n nodeId: string;\n \n /** 节点 Token */\n nodeToken: string;\n \n /** 请求超时 (ms) */\n timeoutMs?: number;\n}\n\nexport interface SubdomainCheckResult {\n subdomain: string;\n available: boolean;\n reason?: string;\n}\n\nexport interface SubdomainRegistrationResult {\n success: boolean;\n subdomain: string;\n fullDomain: string;\n mode: 'direct' | 'tunnel';\n ipv4?: string;\n tunnelProvider?: string;\n tunnelEndpoint?: string;\n registeredAt: string;\n message?: string;\n}\n\nexport interface SubdomainInfo {\n subdomain: string;\n fullDomain: string;\n mode: 'direct' | 'tunnel';\n ipv4?: string;\n tunnelProvider?: string;\n tunnelEndpoint?: string;\n registeredAt: string;\n ownerId?: string;\n}\n\nexport interface DdnsAllocationResult {\n success: boolean;\n subdomain: string;\n domain: string;\n fqdn: string;\n ipAddress?: string;\n ipv6Address?: string;\n createdAt: string;\n}\n\nexport interface DdnsUpdateResult {\n success: boolean;\n subdomain: string;\n domain: string;\n fqdn: string;\n ipAddress?: string;\n ipv6Address?: string;\n updatedAt: string;\n}\n\nexport interface DdnsRecordInfo {\n subdomain: string;\n domain: string;\n fqdn: string;\n ipAddress?: string;\n ipv6Address?: string;\n recordType: string;\n status: string;\n ttl: number;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * 子域名客户端 (Local 模式)\n * \n * 通过 HTTP 调用 Cloud 的子域名 API\n */\nexport class SubdomainClient {\n private readonly logger = getLoggerFor(this);\n private readonly cloudApiEndpoint: string;\n private readonly nodeId: string;\n private readonly nodeToken: string;\n private readonly timeoutMs: number;\n\n constructor(options: SubdomainClientOptions) {\n this.cloudApiEndpoint = options.cloudApiEndpoint.replace(/\\/$/, '');\n this.nodeId = options.nodeId;\n this.nodeToken = options.nodeToken;\n this.timeoutMs = options.timeoutMs ?? 30000;\n }\n\n /**\n * 检查子域名可用性\n */\n async checkAvailability(name: string): Promise<SubdomainCheckResult> {\n const url = `${this.cloudApiEndpoint}/check?name=${encodeURIComponent(name)}`;\n const response = await this.fetch(url, { method: 'GET' });\n return response as SubdomainCheckResult;\n }\n\n /**\n * 注册子域名\n */\n async register(options: {\n subdomain: string;\n localPort: number;\n ipv4?: string;\n }): Promise<SubdomainRegistrationResult> {\n const url = `${this.cloudApiEndpoint}/register`;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n subdomain: options.subdomain,\n localPort: options.localPort,\n ipv4: options.ipv4,\n nodeId: this.nodeId,\n }),\n });\n return response as SubdomainRegistrationResult;\n }\n\n /**\n * 获取子域名信息\n */\n async getInfo(name: string): Promise<SubdomainInfo | null> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}`;\n try {\n const response = await this.fetch(url, { method: 'GET' });\n return response as SubdomainInfo;\n } catch (error) {\n if (error instanceof SubdomainClientError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * 列出所有子域名\n */\n async list(): Promise<{ registrations: SubdomainInfo[]; total: number }> {\n const url = this.cloudApiEndpoint;\n const response = await this.fetch(url, { method: 'GET' });\n return response as { registrations: SubdomainInfo[]; total: number };\n }\n\n /**\n * 释放子域名\n */\n async release(name: string): Promise<{ success: boolean; message: string }> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}`;\n const response = await this.fetch(url, { method: 'DELETE' });\n return response as { success: boolean; message: string };\n }\n\n /**\n * 启动隧道\n */\n async startTunnel(name: string): Promise<{ success: boolean; message: string }> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}/start`;\n const response = await this.fetch(url, { method: 'POST' });\n return response as { success: boolean; message: string };\n }\n\n /**\n * 停止隧道\n */\n async stopTunnel(name: string): Promise<{ success: boolean; message: string }> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}/stop`;\n const response = await this.fetch(url, { method: 'POST' });\n return response as { success: boolean; message: string };\n }\n\n // ============ DDNS API ============\n\n /**\n * 分配 DDNS 子域名\n */\n async allocateDdns(options: {\n subdomain: string;\n ipAddress?: string;\n ipv6Address?: string;\n mode?: 'direct' | 'tunnel';\n tunnelProvider?: string;\n }): Promise<DdnsAllocationResult> {\n const url = `${this.cloudApiEndpoint}/api/v1/ddns/allocate`;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n subdomain: options.subdomain,\n nodeId: this.nodeId,\n ipAddress: options.ipAddress,\n ipv6Address: options.ipv6Address,\n mode: options.mode,\n tunnelProvider: options.tunnelProvider,\n }),\n });\n return response as DdnsAllocationResult;\n }\n\n /**\n * 更新 DDNS 记录\n */\n async updateDdns(subdomain: string, options: {\n ipAddress?: string;\n ipv6Address?: string;\n mode?: 'direct' | 'tunnel';\n tunnelProvider?: string;\n }): Promise<DdnsUpdateResult> {\n const url = `${this.cloudApiEndpoint}/api/v1/ddns/${encodeURIComponent(subdomain)}`;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n ip: options.ipAddress,\n ipv6Address: options.ipv6Address,\n mode: options.mode,\n tunnelProvider: options.tunnelProvider,\n }),\n });\n return response as DdnsUpdateResult;\n }\n\n /**\n * 获取 DDNS 记录\n */\n async getDdns(subdomain: string): Promise<DdnsRecordInfo | null> {\n const url = `${this.cloudApiEndpoint}/api/v1/ddns/${encodeURIComponent(subdomain)}`;\n try {\n const response = await this.fetch(url, { method: 'GET' });\n return response as DdnsRecordInfo;\n } catch (error) {\n if (error instanceof SubdomainClientError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n // ============ Private Methods ============\n\n private async fetch(url: string, options: {\n method: string;\n body?: string;\n }): Promise<unknown> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n // 使用 undici 的 fetch,禁用代理\n const { fetch: undiciFetch, Agent } = await import('undici');\n const agent = new Agent({\n connect: { timeout: this.timeoutMs },\n });\n\n const response = await undiciFetch(url, {\n method: options.method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.nodeToken}`,\n 'X-Node-Id': this.nodeId,\n },\n body: options.body,\n signal: controller.signal,\n dispatcher: agent,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = (data as any)?.error ?? 'Unknown error';\n throw new SubdomainClientError(error, response.status);\n }\n\n return data;\n } catch (error) {\n if (error instanceof SubdomainClientError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new SubdomainClientError('Request timeout', 408);\n }\n throw new SubdomainClientError(\n error instanceof Error ? error.message : 'Unknown error',\n 500,\n );\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\n/**\n * SubdomainClient 错误\n */\nexport class SubdomainClientError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n ) {\n super(message);\n this.name = 'SubdomainClientError';\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SubdomainClient.js","sourceRoot":"","sources":["../../src/subdomain/SubdomainClient.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SubdomainClient - Local 模式子域名客户端\n * \n * Local 模式不持有 DNS/Tunnel 密钥,通过调用 Cloud API 来管理子域名。\n * 使用 Node Token 认证(与 Edge Node 心跳相同的认证方式)。\n */\n\nimport { getLoggerFor } from 'global-logger-factory';\n\nexport interface SubdomainClientOptions {\n /** Cloud API 端点 (如 https://center.example.com/v1/subdomain) */\n cloudApiEndpoint: string;\n \n /** 节点 ID */\n nodeId: string;\n \n /** 节点 Token */\n nodeToken: string;\n \n /** 请求超时 (ms) */\n timeoutMs?: number;\n}\n\nexport interface SubdomainCheckResult {\n subdomain: string;\n available: boolean;\n reason?: string;\n}\n\nexport interface SubdomainRegistrationResult {\n success: boolean;\n subdomain: string;\n fullDomain: string;\n mode: 'direct' | 'tunnel';\n ipv4?: string;\n tunnelProvider?: string;\n tunnelEndpoint?: string;\n registeredAt: string;\n message?: string;\n}\n\nexport interface SubdomainInfo {\n subdomain: string;\n fullDomain: string;\n mode: 'direct' | 'tunnel';\n ipv4?: string;\n tunnelProvider?: string;\n tunnelEndpoint?: string;\n registeredAt: string;\n ownerId?: string;\n}\n\nexport interface DdnsAllocationResult {\n success: boolean;\n subdomain: string;\n domain: string;\n fqdn: string;\n ipAddress?: string;\n ipv6Address?: string;\n createdAt: string;\n}\n\nexport interface DdnsUpdateResult {\n success: boolean;\n subdomain: string;\n domain: string;\n fqdn: string;\n ipAddress?: string;\n ipv6Address?: string;\n updatedAt: string;\n}\n\nexport interface DdnsRecordInfo {\n subdomain: string;\n domain: string;\n fqdn: string;\n ipAddress?: string;\n ipv6Address?: string;\n recordType: string;\n status: string;\n ttl: number;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * 子域名客户端 (Local 模式)\n * \n * 通过 HTTP 调用 Cloud 的子域名 API\n */\nexport class SubdomainClient {\n private readonly logger = getLoggerFor(this);\n private readonly cloudApiEndpoint: string;\n private readonly nodeId: string;\n private readonly nodeToken: string;\n private readonly timeoutMs: number;\n\n constructor(options: SubdomainClientOptions) {\n this.cloudApiEndpoint = options.cloudApiEndpoint.replace(/\\/$/, '');\n this.nodeId = options.nodeId;\n this.nodeToken = options.nodeToken;\n this.timeoutMs = options.timeoutMs ?? 30000;\n }\n\n /**\n * 检查子域名可用性\n */\n async checkAvailability(name: string): Promise<SubdomainCheckResult> {\n const url = `${this.cloudApiEndpoint}/check?name=${encodeURIComponent(name)}`;\n const response = await this.fetch(url, { method: 'GET' });\n return response as SubdomainCheckResult;\n }\n\n /**\n * 注册子域名\n */\n async register(options: {\n subdomain: string;\n localPort: number;\n ipv4?: string;\n }): Promise<SubdomainRegistrationResult> {\n const url = `${this.cloudApiEndpoint}/register`;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n subdomain: options.subdomain,\n localPort: options.localPort,\n ipv4: options.ipv4,\n nodeId: this.nodeId,\n }),\n });\n return response as SubdomainRegistrationResult;\n }\n\n /**\n * 获取子域名信息\n */\n async getInfo(name: string): Promise<SubdomainInfo | null> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}`;\n try {\n const response = await this.fetch(url, { method: 'GET' });\n return response as SubdomainInfo;\n } catch (error) {\n if (error instanceof SubdomainClientError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * 列出所有子域名\n */\n async list(): Promise<{ registrations: SubdomainInfo[]; total: number }> {\n const url = this.cloudApiEndpoint;\n const response = await this.fetch(url, { method: 'GET' });\n return response as { registrations: SubdomainInfo[]; total: number };\n }\n\n /**\n * 释放子域名\n */\n async release(name: string): Promise<{ success: boolean; message: string }> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}`;\n const response = await this.fetch(url, { method: 'DELETE' });\n return response as { success: boolean; message: string };\n }\n\n /**\n * 启动隧道\n */\n async startTunnel(name: string): Promise<{ success: boolean; message: string }> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}/start`;\n const response = await this.fetch(url, { method: 'POST' });\n return response as { success: boolean; message: string };\n }\n\n /**\n * 停止隧道\n */\n async stopTunnel(name: string): Promise<{ success: boolean; message: string }> {\n const url = `${this.cloudApiEndpoint}/${encodeURIComponent(name)}/stop`;\n const response = await this.fetch(url, { method: 'POST' });\n return response as { success: boolean; message: string };\n }\n\n // ============ DDNS API ============\n\n /**\n * 分配 DDNS 子域名\n */\n async allocateDdns(options: {\n subdomain: string;\n ipAddress?: string;\n ipv6Address?: string;\n mode?: 'direct' | 'tunnel';\n tunnelProvider?: string;\n }): Promise<DdnsAllocationResult> {\n const url = `${this.cloudApiEndpoint}/api/v1/ddns/allocate`;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n subdomain: options.subdomain,\n nodeId: this.nodeId,\n ipAddress: options.ipAddress,\n ipv6Address: options.ipv6Address,\n mode: options.mode,\n tunnelProvider: options.tunnelProvider,\n }),\n });\n return response as DdnsAllocationResult;\n }\n\n /**\n * 更新 DDNS 记录\n */\n async updateDdns(subdomain: string, options: {\n ipAddress?: string;\n ipv6Address?: string;\n mode?: 'direct' | 'tunnel';\n tunnelProvider?: string;\n }): Promise<DdnsUpdateResult> {\n const url = `${this.cloudApiEndpoint}/api/v1/ddns/${encodeURIComponent(subdomain)}`;\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({\n ip: options.ipAddress,\n ipv6Address: options.ipv6Address,\n mode: options.mode,\n tunnelProvider: options.tunnelProvider,\n }),\n });\n return response as DdnsUpdateResult;\n }\n\n /**\n * 获取 DDNS 记录\n */\n async getDdns(subdomain: string): Promise<DdnsRecordInfo | null> {\n const url = `${this.cloudApiEndpoint}/api/v1/ddns/${encodeURIComponent(subdomain)}`;\n try {\n const response = await this.fetch(url, { method: 'GET' });\n return response as DdnsRecordInfo;\n } catch (error) {\n if (error instanceof SubdomainClientError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n // ============ Private Methods ============\n\n private async fetch(url: string, options: {\n method: string;\n body?: string;\n }): Promise<unknown> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n // 使用 undici 的 fetch,禁用代理\n const { fetch: undiciFetch, Agent } = await import('undici');\n const agent = new Agent({\n connect: { timeout: this.timeoutMs },\n });\n\n const response = await undiciFetch(url, {\n method: options.method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `XpodNode ${this.nodeId}:${this.nodeToken}`,\n },\n body: options.body,\n signal: controller.signal,\n dispatcher: agent,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = (data as any)?.error ?? 'Unknown error';\n throw new SubdomainClientError(error, response.status);\n }\n\n return data;\n } catch (error) {\n if (error instanceof SubdomainClientError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new SubdomainClientError('Request timeout', 408);\n }\n throw new SubdomainClientError(\n error instanceof Error ? error.message : 'Unknown error',\n 500,\n );\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\n/**\n * SubdomainClient 错误\n */\nexport class SubdomainClientError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n ) {\n super(message);\n this.name = 'SubdomainClientError';\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undefineds.co/xpod",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
|
|
5
5
|
"repository": "https://github.com/undefinedsco/xpod",
|
|
6
6
|
"author": "developer@undefineds.co",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}input:where([type=text]),input:where(:not([type])),input:where([type=email]),input:where([type=url]),input:where([type=password]),input:where([type=number]),input:where([type=date]),input:where([type=datetime-local]),input:where([type=month]),input:where([type=search]),input:where([type=tel]),input:where([type=time]),input:where([type=week]),select:where([multiple]),textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}input:where([type=text]):focus,input:where(:not([type])):focus,input:where([type=email]):focus,input:where([type=url]):focus,input:where([type=password]):focus,input:where([type=number]):focus,input:where([type=date]):focus,input:where([type=datetime-local]):focus,input:where([type=month]):focus,input:where([type=search]):focus,input:where([type=tel]):focus,input:where([type=time]):focus,input:where([type=week]):focus,select:where([multiple]):focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}select:where([multiple]),select:where([size]:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}input:where([type=checkbox]),input:where([type=radio]){-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}input:where([type=checkbox]){border-radius:0}input:where([type=radio]){border-radius:100%}input:where([type=checkbox]):focus,input:where([type=radio]):focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}input:where([type=checkbox]):checked,input:where([type=radio]):checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}input:where([type=checkbox]):checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media(forced-colors:active){input:where([type=checkbox]):checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}input:where([type=radio]):checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media(forced-colors:active){input:where([type=radio]):checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}input:where([type=checkbox]):checked:hover,input:where([type=checkbox]):checked:focus,input:where([type=radio]):checked:hover,input:where([type=radio]):checked:focus{border-color:transparent;background-color:currentColor}input:where([type=checkbox]):indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media(forced-colors:active){input:where([type=checkbox]):indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}input:where([type=checkbox]):indeterminate:hover,input:where([type=checkbox]):indeterminate:focus{border-color:transparent;background-color:currentColor}input:where([type=file]){background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}input:where([type=file]):focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}:root{--background: 0 0% 100%;--foreground: 224 71.4% 4.1%;--card: 0 0% 100%;--card-foreground: 224 71.4% 4.1%;--popover: 0 0% 100%;--popover-foreground: 224 71.4% 4.1%;--primary: 262.1 83.3% 57.8%;--primary-foreground: 210 20% 98%;--secondary: 220 14.3% 95.9%;--secondary-foreground: 220.9 39.3% 11%;--muted: 220 14.3% 95.9%;--muted-foreground: 220 8.9% 46.1%;--accent: 220 14.3% 95.9%;--accent-foreground: 220.9 39.3% 11%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 210 20% 98%;--border: 220 13% 91%;--input: 220 13% 91%;--ring: 262.1 83.3% 57.8%;--radius-xs: 2px;--radius-sm: 4px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 20px;--radius-2xl: 24px;--radius-3xl: 28px;--radius: .5rem;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%;--layout-sidebar: 220 14.3% 95.9%;--layout-list-header: 0 0% 100%;--layout-list-hover: 220 14.3% 95.9%;--layout-list-item: 0 0% 100%;--layout-list-selected: 220 13% 91%;--layout-content: 0 0% 100%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{border-radius:9999px;background-color:hsl(var(--muted-foreground) / .2);-webkit-transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}::-webkit-scrollbar-thumb:hover{background-color:hsl(var(--muted-foreground) / .4)}input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 30px hsl(var(--background)) inset!important;-webkit-text-fill-color:hsl(var(--foreground))!important;caret-color:hsl(var(--foreground))!important;-webkit-transition:background-color 5000s ease-in-out 0s;transition:background-color 5000s ease-in-out 0s}.type-h1{font-size:1.5rem;line-height:2rem;font-weight:600;letter-spacing:-.025em}.type-h2{font-size:1.25rem;line-height:1.75rem;font-weight:600;letter-spacing:-.025em}.pointer-events-none{pointer-events:none}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-1\/2{left:50%}.left-1\/4{left:25%}.left-3{left:.75rem}.right-1\/4{right:25%}.right-2{right:.5rem}.top-0{top:0}.top-1\/2{top:50%}.-z-10{z-index:-10}.z-10{z-index:10}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-ml-2{margin-left:-.5rem}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[400px\]{height:400px}.h-\[500px\]{height:500px}.h-\[600px\]{height:600px}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-20{width:5rem}.w-3\.5{width:.875rem}.w-36{width:9rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-\[1000px\]{width:1000px}.w-\[1200px\]{width:1200px}.w-\[600px\]{width:600px}.w-\[800px\]{width:800px}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.min-w-full{min-width:100%}.min-w-max{min-width:-moz-max-content;min-width:max-content}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[360px\]{max-width:360px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-zinc-100>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(244 244 245 / var(--tw-divide-opacity, 1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-r-2{border-right-width:2px}.border-t{border-top-width:1px}.border-\[\#7C4DFF\]\/30{border-color:#7c4dff4d}.border-\[\#7C4DFF\]\/50{border-color:#7c4dff80}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity, 1))}.border-amber-300{--tw-border-opacity: 1;border-color:rgb(252 211 77 / var(--tw-border-opacity, 1))}.border-border{border-color:hsl(var(--border))}.border-emerald-200{--tw-border-opacity: 1;border-color:rgb(167 243 208 / var(--tw-border-opacity, 1))}.border-emerald-300{--tw-border-opacity: 1;border-color:rgb(110 231 183 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-zinc-100{--tw-border-opacity: 1;border-color:rgb(244 244 245 / var(--tw-border-opacity, 1))}.border-zinc-200{--tw-border-opacity: 1;border-color:rgb(228 228 231 / var(--tw-border-opacity, 1))}.border-zinc-200\/50{border-color:#e4e4e780}.border-zinc-300{--tw-border-opacity: 1;border-color:rgb(212 212 216 / var(--tw-border-opacity, 1))}.bg-\[\#7C4DFF\]{--tw-bg-opacity: 1;background-color:rgb(124 77 255 / var(--tw-bg-opacity, 1))}.bg-\[\#7C4DFF\]\/10{background-color:#7c4dff1a}.bg-\[\#7C4DFF\]\/20{background-color:#7c4dff33}.bg-\[\#7C4DFF\]\/5{background-color:#7c4dff0d}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-background{background-color:hsl(var(--background))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-emerald-100{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity, 1))}.bg-emerald-50{--tw-bg-opacity: 1;background-color:rgb(236 253 245 / var(--tw-bg-opacity, 1))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-layout-content{background-color:hsl(var(--layout-content))}.bg-layout-sidebar{background-color:hsl(var(--layout-sidebar))}.bg-muted{background-color:hsl(var(--muted))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary) / .1)}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/70{background-color:#ffffffb3}.bg-white\/80{background-color:#fffc}.bg-zinc-100{--tw-bg-opacity: 1;background-color:rgb(244 244 245 / var(--tw-bg-opacity, 1))}.bg-zinc-50{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.from-\[\#7C4DFF\]\/10{--tw-gradient-from: rgb(124 77 255 / .1) var(--tw-gradient-from-position);--tw-gradient-to: rgb(124 77 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-\[\#7C4DFF\]\/5{--tw-gradient-to: rgb(124 77 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(124 77 255 / .05) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-zinc-50{--tw-gradient-to: #fafafa var(--tw-gradient-to-position)}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-5{padding-bottom:1.25rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-32{padding-top:8rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:JetBrains Mono,monospace}.font-sans{font-family:Inter,system-ui,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-5xl{font-size:3rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#7C4DFF\]{--tw-text-opacity: 1;color:rgb(124 77 255 / var(--tw-text-opacity, 1))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-destructive{color:hsl(var(--destructive))}.text-emerald-500{--tw-text-opacity: 1;color:rgb(16 185 129 / var(--tw-text-opacity, 1))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-emerald-700{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.text-foreground{color:hsl(var(--foreground))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.text-zinc-700{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity, 1))}.text-zinc-900{--tw-text-opacity: 1;color:rgb(24 24 27 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[\#7C4DFF\]\/20{--tw-shadow-color: rgb(124 77 255 / .2);--tw-shadow: var(--tw-shadow-colored)}.shadow-zinc-200\/50{--tw-shadow-color: rgb(228 228 231 / .5);--tw-shadow: var(--tw-shadow-colored)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.blur-\[100px\]{--tw-blur: blur(100px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-\[120px\]{--tw-blur: blur(120px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-\[150px\]{--tw-blur: blur(150px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.running{animation-play-state:running}.\!paused{animation-play-state:paused!important}.paused{animation-play-state:paused}.selection\:bg-\[\#7C4DFF\]\/20 *::-moz-selection{background-color:#7c4dff33}.selection\:bg-\[\#7C4DFF\]\/20 *::selection{background-color:#7c4dff33}.selection\:bg-\[\#7C4DFF\]\/20::-moz-selection{background-color:#7c4dff33}.selection\:bg-\[\#7C4DFF\]\/20::selection{background-color:#7c4dff33}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-zinc-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.placeholder\:text-zinc-400::placeholder{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.hover\:border-\[\#7C4DFF\]\/50:hover{border-color:#7c4dff80}.hover\:border-zinc-300:hover{--tw-border-opacity: 1;border-color:rgb(212 212 216 / var(--tw-border-opacity, 1))}.hover\:bg-\[\#6B3FE8\]:hover{--tw-bg-opacity: 1;background-color:rgb(107 63 232 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-muted\/80:hover{background-color:hsl(var(--muted) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-100:hover{--tw-bg-opacity: 1;background-color:rgb(244 244 245 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-200:hover{--tw-bg-opacity: 1;background-color:rgb(228 228 231 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-50:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.hover\:text-\[\#6B3FE8\]:hover{--tw-text-opacity: 1;color:rgb(107 63 232 / var(--tw-text-opacity, 1))}.hover\:text-\[\#7C4DFF\]:hover{--tw-text-opacity: 1;color:rgb(124 77 255 / var(--tw-text-opacity, 1))}.hover\:text-blue-800:hover{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-zinc-700:hover{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity, 1))}.hover\:text-zinc-900:hover{--tw-text-opacity: 1;color:rgb(24 24 27 / var(--tw-text-opacity, 1))}.focus\:border-\[\#7C4DFF\]:focus{--tw-border-opacity: 1;border-color:rgb(124 77 255 / var(--tw-border-opacity, 1))}.focus\:border-amber-500:focus{--tw-border-opacity: 1;border-color:rgb(245 158 11 / var(--tw-border-opacity, 1))}.focus\:border-emerald-500:focus{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity, 1))}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-\[\#7C4DFF\]:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(124 77 255 / var(--tw-ring-opacity, 1))}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-primary:focus-visible{--tw-ring-color: hsl(var(--primary))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-\[\#7C4DFF\]{--tw-text-opacity: 1;color:rgb(124 77 255 / var(--tw-text-opacity, 1))}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}@media(min-width:640px){.sm\:inline{display:inline}.sm\:w-auto{width:auto}.sm\:flex-row{flex-direction:row}}@media(min-width:768px){.md\:flex{display:flex}.md\:inline-flex{display:inline-flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:text-7xl{font-size:4.5rem;line-height:1}}@media(min-width:1024px){.lg\:mx-0{margin-left:0;margin-right:0}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:gap-16{gap:4rem}.lg\:p-8{padding:2rem}}@media(min-width:1280px){.xl\:text-3xl{font-size:1.875rem;line-height:2.25rem}}
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}input:where([type=text]),input:where(:not([type])),input:where([type=email]),input:where([type=url]),input:where([type=password]),input:where([type=number]),input:where([type=date]),input:where([type=datetime-local]),input:where([type=month]),input:where([type=search]),input:where([type=tel]),input:where([type=time]),input:where([type=week]),select:where([multiple]),textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}input:where([type=text]):focus,input:where(:not([type])):focus,input:where([type=email]):focus,input:where([type=url]):focus,input:where([type=password]):focus,input:where([type=number]):focus,input:where([type=date]):focus,input:where([type=datetime-local]):focus,input:where([type=month]):focus,input:where([type=search]):focus,input:where([type=tel]):focus,input:where([type=time]):focus,input:where([type=week]):focus,select:where([multiple]):focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}select:where([multiple]),select:where([size]:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}input:where([type=checkbox]),input:where([type=radio]){-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}input:where([type=checkbox]){border-radius:0}input:where([type=radio]){border-radius:100%}input:where([type=checkbox]):focus,input:where([type=radio]):focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}input:where([type=checkbox]):checked,input:where([type=radio]):checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}input:where([type=checkbox]):checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media(forced-colors:active){input:where([type=checkbox]):checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}input:where([type=radio]):checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media(forced-colors:active){input:where([type=radio]):checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}input:where([type=checkbox]):checked:hover,input:where([type=checkbox]):checked:focus,input:where([type=radio]):checked:hover,input:where([type=radio]):checked:focus{border-color:transparent;background-color:currentColor}input:where([type=checkbox]):indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media(forced-colors:active){input:where([type=checkbox]):indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}input:where([type=checkbox]):indeterminate:hover,input:where([type=checkbox]):indeterminate:focus{border-color:transparent;background-color:currentColor}input:where([type=file]){background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}input:where([type=file]):focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}:root{--background: 0 0% 100%;--foreground: 224 71.4% 4.1%;--card: 0 0% 100%;--card-foreground: 224 71.4% 4.1%;--popover: 0 0% 100%;--popover-foreground: 224 71.4% 4.1%;--primary: 262.1 83.3% 57.8%;--primary-foreground: 210 20% 98%;--secondary: 220 14.3% 95.9%;--secondary-foreground: 220.9 39.3% 11%;--muted: 220 14.3% 95.9%;--muted-foreground: 220 8.9% 46.1%;--accent: 220 14.3% 95.9%;--accent-foreground: 220.9 39.3% 11%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 210 20% 98%;--border: 220 13% 91%;--input: 220 13% 91%;--ring: 262.1 83.3% 57.8%;--radius-xs: 2px;--radius-sm: 4px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 20px;--radius-2xl: 24px;--radius-3xl: 28px;--radius: .5rem;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%;--layout-sidebar: 220 14.3% 95.9%;--layout-list-header: 0 0% 100%;--layout-list-hover: 220 14.3% 95.9%;--layout-list-item: 0 0% 100%;--layout-list-selected: 220 13% 91%;--layout-content: 0 0% 100%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{border-radius:9999px;background-color:hsl(var(--muted-foreground) / .2);-webkit-transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}::-webkit-scrollbar-thumb:hover{background-color:hsl(var(--muted-foreground) / .4)}input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 30px hsl(var(--background)) inset!important;-webkit-text-fill-color:hsl(var(--foreground))!important;caret-color:hsl(var(--foreground))!important;-webkit-transition:background-color 5000s ease-in-out 0s;transition:background-color 5000s ease-in-out 0s}.type-h1{font-size:1.5rem;line-height:2rem;font-weight:600;letter-spacing:-.025em}.type-h2{font-size:1.25rem;line-height:1.75rem;font-weight:600;letter-spacing:-.025em}.pointer-events-none{pointer-events:none}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-1\/2{left:50%}.left-1\/4{left:25%}.left-3{left:.75rem}.right-1\/4{right:25%}.right-2{right:.5rem}.top-0{top:0}.top-1\/2{top:50%}.-z-10{z-index:-10}.z-10{z-index:10}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-ml-2{margin-left:-.5rem}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[400px\]{height:400px}.h-\[500px\]{height:500px}.h-\[600px\]{height:600px}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-20{width:5rem}.w-3\.5{width:.875rem}.w-36{width:9rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-\[1000px\]{width:1000px}.w-\[1200px\]{width:1200px}.w-\[600px\]{width:600px}.w-\[800px\]{width:800px}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.min-w-full{min-width:100%}.min-w-max{min-width:-moz-max-content;min-width:max-content}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[360px\]{max-width:360px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-zinc-100>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(244 244 245 / var(--tw-divide-opacity, 1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-r-2{border-right-width:2px}.border-t{border-top-width:1px}.border-\[\#7C4DFF\]\/30{border-color:#7c4dff4d}.border-\[\#7C4DFF\]\/50{border-color:#7c4dff80}.border-amber-200{--tw-border-opacity: 1;border-color:rgb(253 230 138 / var(--tw-border-opacity, 1))}.border-amber-300{--tw-border-opacity: 1;border-color:rgb(252 211 77 / var(--tw-border-opacity, 1))}.border-border{border-color:hsl(var(--border))}.border-emerald-200{--tw-border-opacity: 1;border-color:rgb(167 243 208 / var(--tw-border-opacity, 1))}.border-emerald-300{--tw-border-opacity: 1;border-color:rgb(110 231 183 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-zinc-100{--tw-border-opacity: 1;border-color:rgb(244 244 245 / var(--tw-border-opacity, 1))}.border-zinc-200{--tw-border-opacity: 1;border-color:rgb(228 228 231 / var(--tw-border-opacity, 1))}.border-zinc-200\/50{border-color:#e4e4e780}.border-zinc-300{--tw-border-opacity: 1;border-color:rgb(212 212 216 / var(--tw-border-opacity, 1))}.bg-\[\#7C4DFF\]{--tw-bg-opacity: 1;background-color:rgb(124 77 255 / var(--tw-bg-opacity, 1))}.bg-\[\#7C4DFF\]\/10{background-color:#7c4dff1a}.bg-\[\#7C4DFF\]\/20{background-color:#7c4dff33}.bg-\[\#7C4DFF\]\/5{background-color:#7c4dff0d}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-background{background-color:hsl(var(--background))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-emerald-100{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity, 1))}.bg-emerald-50{--tw-bg-opacity: 1;background-color:rgb(236 253 245 / var(--tw-bg-opacity, 1))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-layout-content{background-color:hsl(var(--layout-content))}.bg-layout-sidebar{background-color:hsl(var(--layout-sidebar))}.bg-muted{background-color:hsl(var(--muted))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary) / .1)}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/70{background-color:#ffffffb3}.bg-white\/80{background-color:#fffc}.bg-zinc-100{--tw-bg-opacity: 1;background-color:rgb(244 244 245 / var(--tw-bg-opacity, 1))}.bg-zinc-50{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.from-\[\#7C4DFF\]\/10{--tw-gradient-from: rgb(124 77 255 / .1) var(--tw-gradient-from-position);--tw-gradient-to: rgb(124 77 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-\[\#7C4DFF\]\/5{--tw-gradient-to: rgb(124 77 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(124 77 255 / .05) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-zinc-50{--tw-gradient-to: #fafafa var(--tw-gradient-to-position)}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-5{padding-bottom:1.25rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-32{padding-top:8rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:JetBrains Mono,monospace}.font-sans{font-family:Inter,system-ui,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-5xl{font-size:3rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#7C4DFF\]{--tw-text-opacity: 1;color:rgb(124 77 255 / var(--tw-text-opacity, 1))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-destructive{color:hsl(var(--destructive))}.text-emerald-500{--tw-text-opacity: 1;color:rgb(16 185 129 / var(--tw-text-opacity, 1))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-emerald-700{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.text-foreground{color:hsl(var(--foreground))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.text-zinc-700{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity, 1))}.text-zinc-900{--tw-text-opacity: 1;color:rgb(24 24 27 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[\#7C4DFF\]\/20{--tw-shadow-color: rgb(124 77 255 / .2);--tw-shadow: var(--tw-shadow-colored)}.shadow-zinc-200\/50{--tw-shadow-color: rgb(228 228 231 / .5);--tw-shadow: var(--tw-shadow-colored)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.blur-\[100px\]{--tw-blur: blur(100px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-\[120px\]{--tw-blur: blur(120px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-\[150px\]{--tw-blur: blur(150px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.running{animation-play-state:running}.\!paused{animation-play-state:paused!important}.paused{animation-play-state:paused}.selection\:bg-\[\#7C4DFF\]\/20 *::-moz-selection{background-color:#7c4dff33}.selection\:bg-\[\#7C4DFF\]\/20 *::selection{background-color:#7c4dff33}.selection\:bg-\[\#7C4DFF\]\/20::-moz-selection{background-color:#7c4dff33}.selection\:bg-\[\#7C4DFF\]\/20::selection{background-color:#7c4dff33}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-zinc-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.placeholder\:text-zinc-400::placeholder{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.hover\:border-\[\#7C4DFF\]\/50:hover{border-color:#7c4dff80}.hover\:border-zinc-300:hover{--tw-border-opacity: 1;border-color:rgb(212 212 216 / var(--tw-border-opacity, 1))}.hover\:bg-\[\#6B3FE8\]:hover{--tw-bg-opacity: 1;background-color:rgb(107 63 232 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-muted\/80:hover{background-color:hsl(var(--muted) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-100:hover{--tw-bg-opacity: 1;background-color:rgb(244 244 245 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-200:hover{--tw-bg-opacity: 1;background-color:rgb(228 228 231 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-50:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.hover\:text-\[\#6B3FE8\]:hover{--tw-text-opacity: 1;color:rgb(107 63 232 / var(--tw-text-opacity, 1))}.hover\:text-\[\#7C4DFF\]:hover{--tw-text-opacity: 1;color:rgb(124 77 255 / var(--tw-text-opacity, 1))}.hover\:text-blue-800:hover{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-zinc-700:hover{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity, 1))}.hover\:text-zinc-900:hover{--tw-text-opacity: 1;color:rgb(24 24 27 / var(--tw-text-opacity, 1))}.focus\:border-\[\#7C4DFF\]:focus{--tw-border-opacity: 1;border-color:rgb(124 77 255 / var(--tw-border-opacity, 1))}.focus\:border-amber-500:focus{--tw-border-opacity: 1;border-color:rgb(245 158 11 / var(--tw-border-opacity, 1))}.focus\:border-emerald-500:focus{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity, 1))}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-\[\#7C4DFF\]:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(124 77 255 / var(--tw-ring-opacity, 1))}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-primary:focus-visible{--tw-ring-color: hsl(var(--primary))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-\[\#7C4DFF\]{--tw-text-opacity: 1;color:rgb(124 77 255 / var(--tw-text-opacity, 1))}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}@media(min-width:640px){.sm\:inline{display:inline}.sm\:w-auto{width:auto}.sm\:flex-row{flex-direction:row}}@media(min-width:768px){.md\:flex{display:flex}.md\:inline-flex{display:inline-flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:text-7xl{font-size:4.5rem;line-height:1}}@media(min-width:1024px){.lg\:mx-0{margin-left:0;margin-right:0}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:gap-16{gap:4rem}.lg\:p-8{padding:2rem}}@media(min-width:1280px){.xl\:text-3xl{font-size:1.875rem;line-height:2.25rem}}
|