@beeblock/svelar 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/Cli.d.ts +1 -0
- package/dist/cli/bin.js +1398 -810
- package/dist/cli/commands/NewCommandTemplates.d.ts +11 -0
- package/dist/cli/index.js +69 -66
- package/dist/hooks/index.js +2 -2
- package/dist/index.js +2 -2
- package/dist/search/index.d.ts +112 -0
- package/dist/search/index.js +1 -0
- package/dist/session/index.js +1 -1
- package/package.json +10 -2
package/dist/cli/bin.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
2
|
+
var vn=Object.defineProperty;var Fe=(l=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(l,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):l)(function(l){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+l+'" is not supported')});var y=(l,e)=>()=>(l&&(e=l(l=0)),e);var E=(l,e)=>{for(var t in e)vn(l,t,{get:e[t],enumerable:!0})};function w(l,e){let t=Symbol.for(l),s=globalThis;return s[t]||(s[t]=e()),s[t]}var R=y(()=>{"use strict"});var P={};E(P,{Connection:()=>g});var os,g,x=y(()=>{"use strict";R();os=class{connections=new Map;config=null;defaultName="default";configure(e){this.config=e,this.defaultName=e.default}async connection(e){let t=e??this.defaultName;if(this.connections.has(t))return this.connections.get(t).drizzle;if(!this.config)throw new Error("Database not configured. Call Connection.configure() first, or register DatabaseServiceProvider.");let s=this.config.connections[t];if(!s)throw new Error(`Database connection "${t}" is not defined in configuration.`);let r=await this.createConnection(s);return this.connections.set(t,r),r.drizzle}async rawClient(e){let t=e??this.defaultName;return await this.connection(t),this.connections.get(t).rawClient}async raw(e,t=[],s){let r=await this.connection(s),i=this.getConfig(s);switch(i.driver){case"sqlite":{let n=await this.rawClient(s),a=t.map(u=>typeof u=="boolean"?u?1:0:u instanceof Date?u.toISOString():u),o=n.prepare(e),c=e.trimStart().toUpperCase();return c.startsWith("SELECT")||c.startsWith("PRAGMA")||c.startsWith("WITH")?o.all(...a):o.run(...a)}case"postgres":return(await this.rawClient(s))(e,...t);case"mysql":{let n=await this.rawClient(s),[a]=await n.execute(e,t);return a}default:throw new Error(`Unsupported driver: ${i.driver}`)}}getDriver(e){return this.getConfig(e).driver}getConfig(e){let t=e??this.defaultName;if(!this.config)throw new Error("Database not configured.");let s=this.config.connections[t];if(!s)throw new Error(`Database connection "${t}" is not defined.`);return s}async disconnect(e){if(e){let t=this.connections.get(e);t&&(await this.closeConnection(t),this.connections.delete(e))}else{for(let[t,s]of this.connections)await this.closeConnection(s);this.connections.clear()}}isConnected(e){return this.connections.has(e??this.defaultName)}async transaction(e,t){let s=this.getConfig(t),r=await this.rawClient(t);switch(s.driver){case"sqlite":{r.exec("BEGIN");try{let i=await e();return r.exec("COMMIT"),i}catch(i){throw r.exec("ROLLBACK"),i}}case"postgres":{await r`BEGIN`;try{let i=await e();return await r`COMMIT`,i}catch(i){throw await r`ROLLBACK`,i}}case"mysql":{let i=await r.getConnection();await i.beginTransaction();try{let n=await e();return await i.commit(),i.release(),n}catch(n){throw await i.rollback(),i.release(),n}}default:throw new Error(`Unsupported driver: ${s.driver}`)}}async createConnection(e){switch(e.driver){case"sqlite":return this.createSQLiteConnection(e);case"postgres":return this.createPostgresConnection(e);case"mysql":return this.createMySQLConnection(e);default:throw new Error(`Unsupported database driver: ${e.driver}`)}}async createSQLiteConnection(e){let t=e.filename??e.database??":memory:";try{let s=(await import("better-sqlite3")).default,{drizzle:r}=await import("drizzle-orm/better-sqlite3"),i=new s(t);return i.pragma("journal_mode = WAL"),i.pragma("foreign_keys = ON"),{drizzle:r(i),config:e,rawClient:i}}catch(s){let r;try{r=(await new Function("mod","return import(mod)")("node:sqlite")).DatabaseSync}catch{throw new Error(`No SQLite driver available. Install better-sqlite3 (npm install better-sqlite3) or use Node.js v22+ which includes built-in SQLite support. Original error: ${s instanceof Error?s.message:String(s)}`)}let i=new r(t);i.exec("PRAGMA journal_mode = WAL"),i.exec("PRAGMA foreign_keys = ON");let n={prepare(o){let c=i.prepare(o);return{all(...u){return c.all(...u)},run(...u){return c.run(...u)},get(...u){return c.get(...u)}}},exec(o){i.exec(o)},pragma(o){return i.prepare(`PRAGMA ${o}`).all()},close(){i.close()}},a;try{let{drizzle:o}=await import("drizzle-orm/better-sqlite3");a=o(n)}catch{a=n}return{drizzle:a,config:e,rawClient:n}}}async createPostgresConnection(e){let t=(await import("postgres")).default,{drizzle:s}=await import("drizzle-orm/postgres-js"),r=e.url??`postgres://${e.user}:${e.password}@${e.host??"localhost"}:${e.port??5432}/${e.database}`,i=t(r);return{drizzle:s(i),config:e,rawClient:i}}async createMySQLConnection(e){let t=await import("mysql2/promise"),{drizzle:s}=await import("drizzle-orm/mysql2"),r=t.createPool({host:e.host??"localhost",port:e.port??3306,database:e.database,user:e.user,password:e.password,uri:e.url});return{drizzle:s(r),config:e,rawClient:r}}async closeConnection(e){try{switch(e.config.driver){case"sqlite":e.rawClient.close();break;case"postgres":await e.rawClient.end();break;case"mysql":await e.rawClient.end();break}}catch{}}},g=w("svelar.connection",()=>new os)});var j,ps,W,O,ti,hs=y(()=>{"use strict";x();R();j=class{constructor(e){this.column=e}nullable(){return this.column.nullable=!0,this}notNullable(){return this.column.nullable=!1,this}default(e){return this.column.defaultValue=e,this}primary(){return this.column.primaryKey=!0,this}unique(){return this.column.unique=!0,this}unsigned(){return this.column.unsigned=!0,this}references(e,t){return this.column.references={table:t,column:e},new ps(this.column)}build(){return this.column}},ps=class{constructor(e){this.column=e}onDelete(e){return this.column.references.onDelete=e,this}onUpdate(e){return this.column.references.onUpdate=e,this}},W=class{columns=[];indices=[];compositePrimary=null;addColumn(e,t){let s={name:e,type:t,nullable:!1,primaryKey:!1,autoIncrement:!1,unique:!1,unsigned:!1};return this.columns.push(s),new j(s)}increments(e="id"){let t={name:e,type:"INTEGER",nullable:!1,primaryKey:!0,autoIncrement:!0,unique:!1,unsigned:!0};return this.columns.push(t),new j(t)}bigIncrements(e="id"){let t={name:e,type:"BIGINT",nullable:!1,primaryKey:!0,autoIncrement:!0,unique:!1,unsigned:!0};return this.columns.push(t),new j(t)}string(e,t=255){return this.addColumn(e,`VARCHAR(${t})`)}text(e){return this.addColumn(e,"TEXT")}integer(e){return this.addColumn(e,"INTEGER")}bigInteger(e){return this.addColumn(e,"BIGINT")}float(e){return this.addColumn(e,"FLOAT")}decimal(e,t=8,s=2){return this.addColumn(e,`DECIMAL(${t},${s})`)}boolean(e){return this.addColumn(e,"BOOLEAN")}date(e){return this.addColumn(e,"DATE")}datetime(e){return this.addColumn(e,"DATETIME")}timestamp(e){return this.addColumn(e,"TIMESTAMP")}timestamps(){this.timestamp("created_at").nullable(),this.timestamp("updated_at").nullable()}json(e){return this.addColumn(e,"JSON")}blob(e){return this.addColumn(e,"BLOB")}enum(e,t){return this.addColumn(e,`ENUM(${t.map(s=>`'${s}'`).join(",")})`)}uuid(e="id"){return this.addColumn(e,"UUID")}ulid(e="id"){return this.addColumn(e,"ULID")}jsonb(e){return this.addColumn(e,"JSONB")}primary(e){this.compositePrimary=e}index(e,t){let s=Array.isArray(e)?e:[e];this.indices.push({columns:s,unique:!1,name:t})}uniqueIndex(e,t){let s=Array.isArray(e)?e:[e];this.indices.push({columns:s,unique:!0,name:t})}foreign(e){let t=this.columns.find(s=>s.name===e);if(!t)throw new Error(`Column "${e}" must be defined before adding a foreign key.`);return new j(t)}toSQL(e,t){let s=[],r=[];for(let i of this.columns)r.push(this.columnToSQL(i,t));this.compositePrimary&&r.push(`PRIMARY KEY (${this.compositePrimary.join(", ")})`);for(let i of this.columns)if(i.references){let n=`FOREIGN KEY (${i.name}) REFERENCES ${i.references.table}(${i.references.column})`;i.references.onDelete&&(n+=` ON DELETE ${i.references.onDelete}`),i.references.onUpdate&&(n+=` ON UPDATE ${i.references.onUpdate}`),r.push(n)}s.push(`CREATE TABLE ${e} (
|
|
3
3
|
${r.join(`,
|
|
4
4
|
`)}
|
|
5
|
-
)`);for(let
|
|
5
|
+
)`);for(let i of this.indices){let n=i.name??`idx_${e}_${i.columns.join("_")}`,a=i.unique?"UNIQUE ":"";s.push(`CREATE ${a}INDEX ${n} ON ${e} (${i.columns.join(", ")})`)}return s}columnToSQL(e,t){let s=e.name,r=e.type;if(t==="sqlite"?r=this.mapSQLiteType(r,e):t==="postgres"?r=this.mapPostgresType(r,e):t==="mysql"&&(r=this.mapMySQLType(r,e)),s+=` ${r}`,e.primaryKey&&!this.compositePrimary&&(s+=" PRIMARY KEY",e.autoIncrement&&(t==="sqlite"?s+=" AUTOINCREMENT":t==="postgres"||t==="mysql"&&(s+=" AUTO_INCREMENT"))),!e.nullable&&!e.primaryKey&&(s+=" NOT NULL"),e.unique&&!e.primaryKey&&(s+=" UNIQUE"),e.defaultValue!==void 0){let i=typeof e.defaultValue=="string"?`'${e.defaultValue}'`:e.defaultValue===null?"NULL":e.defaultValue;s+=` DEFAULT ${i}`}return s}mapSQLiteType(e,t){return e==="BOOLEAN"?"INTEGER":e==="UUID"||e==="ULID"||e.startsWith("ENUM")||e==="JSON"||e==="JSONB"?"TEXT":e==="BIGINT"&&t.autoIncrement?"INTEGER":e}mapPostgresType(e,t){return t.autoIncrement&&e==="INTEGER"?"SERIAL":t.autoIncrement&&e==="BIGINT"?"BIGSERIAL":e==="DATETIME"?"TIMESTAMP":e==="BLOB"?"BYTEA":e.startsWith("ENUM")?"TEXT":e==="UUID"?"UUID":e==="ULID"?"VARCHAR(26)":e==="JSON"||e==="JSONB"?"JSONB":e}mapMySQLType(e,t){return e==="BOOLEAN"?"TINYINT(1)":e==="UUID"?"CHAR(36)":e==="ULID"?"CHAR(26)":e==="JSONB"?"JSON":e==="TIMESTAMP"?"DATETIME":t.unsigned&&!e.startsWith("DECIMAL")?`${e} UNSIGNED`:e}},O=class{constructor(e){this.connectionName=e}async createTable(e,t){let s=new W;t(s);let r=g.getDriver(this.connectionName),i=s.toSQL(e,r);for(let n of i)await g.raw(n,[],this.connectionName)}async dropTable(e){await g.raw(`DROP TABLE IF EXISTS ${e}`,[],this.connectionName)}async dropTableIfExists(e){await g.raw(`DROP TABLE IF EXISTS ${e}`,[],this.connectionName)}async renameTable(e,t){g.getDriver(this.connectionName)==="mysql"?await g.raw(`RENAME TABLE ${e} TO ${t}`,[],this.connectionName):await g.raw(`ALTER TABLE ${e} RENAME TO ${t}`,[],this.connectionName)}async hasTable(e){let t=g.getDriver(this.connectionName),s;switch(t){case"sqlite":s=await g.raw("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[e],this.connectionName);break;case"postgres":s=await g.raw("SELECT tablename FROM pg_tables WHERE tablename = $1",[e],this.connectionName);break;case"mysql":s=await g.raw("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME = ?",[e],this.connectionName);break;default:throw new Error(`Unsupported driver: ${t}`)}return s.length>0}async addColumn(e,t){let s=new W;t(s);let r=g.getDriver(this.connectionName),i=s.columns;for(let n of i){let a=s.columnToSQL(n,r);await g.raw(`ALTER TABLE ${e} ADD COLUMN ${a}`,[],this.connectionName)}}async dropColumn(e,t){await g.raw(`ALTER TABLE ${e} DROP COLUMN ${t}`,[],this.connectionName)}},ti=w("svelar.schema",()=>new O)});var si={};E(si,{Migration:()=>xe,Migrator:()=>Ce});var xe,Ce,gs=y(()=>{"use strict";x();hs();xe=class{schema=new O},Ce=class{migrationsTable="svelar_migrations";connectionName;constructor(e){this.connectionName=e}async ensureMigrationsTable(){let e=new O(this.connectionName);await e.hasTable(this.migrationsTable)||await e.createTable(this.migrationsTable,s=>{s.increments("id"),s.string("migration").unique(),s.integer("batch"),s.timestamp("ran_at").default("CURRENT_TIMESTAMP")})}async run(e){await this.ensureMigrationsTable();let t=await this.getRanMigrations(),s=e.filter(n=>!t.includes(n.name));if(s.length===0)return[];let r=await this.getNextBatch(),i=[];for(let n of s)await n.migration.up(),await g.raw(`INSERT INTO ${this.migrationsTable} (migration, batch) VALUES (?, ?)`,[n.name,r],this.connectionName),i.push(n.name);return i}async rollback(e){await this.ensureMigrationsTable();let t=await this.getLastBatch();if(t===0)return[];let s=await g.raw(`SELECT migration FROM ${this.migrationsTable} WHERE batch = ? ORDER BY id DESC`,[t],this.connectionName),r=[];for(let i of s){let n=i.migration,a=e.find(o=>o.name===n);a&&(await a.migration.down(),await g.raw(`DELETE FROM ${this.migrationsTable} WHERE migration = ?`,[n],this.connectionName),r.push(n))}return r}async reset(e){let t=[];for(;;){let s=await this.rollback(e);if(s.length===0)break;t.push(...s)}return t}async refresh(e){let t=await this.reset(e),s=await this.run(e);return{reset:t,migrated:s}}async fresh(e){let t=await this.dropAllTables(),s=await this.run(e);return{dropped:t,migrated:s}}async dropAllTables(){let e=g.getDriver(this.connectionName),t=[];switch(e){case"sqlite":{t=(await g.raw("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'",[],this.connectionName)).map(r=>r.name),await g.raw("PRAGMA foreign_keys = OFF",[],this.connectionName);for(let r of t)await g.raw(`DROP TABLE IF EXISTS "${r}"`,[],this.connectionName);await g.raw("PRAGMA foreign_keys = ON",[],this.connectionName);break}case"mysql":{t=(await g.raw("SHOW TABLES",[],this.connectionName)).map(r=>Object.values(r)[0]),await g.raw("SET FOREIGN_KEY_CHECKS = 0",[],this.connectionName);for(let r of t)await g.raw(`DROP TABLE IF EXISTS \`${r}\``,[],this.connectionName);await g.raw("SET FOREIGN_KEY_CHECKS = 1",[],this.connectionName);break}case"postgres":{t=(await g.raw("SELECT tablename FROM pg_tables WHERE schemaname = 'public'",[],this.connectionName)).map(r=>r.tablename);for(let r of t)await g.raw(`DROP TABLE IF EXISTS "${r}" CASCADE`,[],this.connectionName);break}default:throw new Error(`Unsupported driver for fresh: ${e}`)}return t}async getRanMigrations(){try{return(await g.raw(`SELECT migration FROM ${this.migrationsTable} ORDER BY batch, id`,[],this.connectionName)).map(t=>t.migration)}catch{return[]}}async status(e){let t=await this.getRanMigrations(),s=new Map;try{let r=await g.raw(`SELECT migration, batch FROM ${this.migrationsTable}`,[],this.connectionName);for(let i of r)s.set(i.migration,i.batch)}catch{}return e.map(r=>({name:r.name,ran:t.includes(r.name),batch:s.get(r.name)??null}))}async getNextBatch(){return await this.getLastBatch()+1}async getLastBatch(){try{return(await g.raw(`SELECT MAX(batch) as max_batch FROM ${this.migrationsTable}`,[],this.connectionName))[0]?.max_batch??0}catch{return 0}}}});var yt={};E(yt,{SchedulerLock:()=>Se});import{hostname as Oa}from"os";async function Pe(){let{Connection:l}=await Promise.resolve().then(()=>(x(),P));return l}async function La(){return(await Pe()).getDriver()}var ni,B,Se,Re=y(()=>{"use strict";ni=!1,B=`${Oa()}:${process.pid}:${Math.random().toString(36).slice(2,10)}`;Se=class{static getOwnerId(){return B}static async ensureTable(){if(ni)return;let e=await Pe();switch(e.getDriver()){case"sqlite":await e.raw(`CREATE TABLE IF NOT EXISTS scheduler_locks (
|
|
6
6
|
task_key TEXT PRIMARY KEY,
|
|
7
7
|
owner TEXT NOT NULL,
|
|
8
8
|
expires_at TEXT NOT NULL
|
|
@@ -14,7 +14,7 @@ var wn=Object.defineProperty;var He=(l=>typeof require<"u"?require:typeof Proxy<
|
|
|
14
14
|
task_key VARCHAR(255) PRIMARY KEY,
|
|
15
15
|
owner VARCHAR(255) NOT NULL,
|
|
16
16
|
expires_at DATETIME NOT NULL
|
|
17
|
-
) ENGINE=InnoDB`);break}
|
|
17
|
+
) ENGINE=InnoDB`);break}ni=!0}static async acquire(e,t=5){await this.ensureTable();let s=await Pe(),r=await La(),i=new Date().toISOString(),n=new Date(Date.now()+t*6e4).toISOString();try{await s.transaction(async()=>{switch(await s.raw("DELETE FROM scheduler_locks WHERE task_key = ? AND expires_at < ?",[e,i]),r){case"sqlite":await s.raw("INSERT OR IGNORE INTO scheduler_locks (task_key, owner, expires_at) VALUES (?, ?, ?)",[e,B,n]);break;case"postgres":await s.raw("INSERT INTO scheduler_locks (task_key, owner, expires_at) VALUES ($1, $2, $3) ON CONFLICT (task_key) DO NOTHING",[e,B,n]);break;case"mysql":await s.raw("INSERT IGNORE INTO scheduler_locks (task_key, owner, expires_at) VALUES (?, ?, ?)",[e,B,n]);break}});let a=await s.raw("SELECT owner FROM scheduler_locks WHERE task_key = ?",[e]);return a.length>0&&a[0].owner===B}catch{return!1}}static async release(e){try{await(await Pe()).raw("DELETE FROM scheduler_locks WHERE task_key = ? AND owner = ?",[e,B])}catch{}}static async releaseAll(){try{await(await Pe()).raw("DELETE FROM scheduler_locks WHERE owner = ?",[B])}catch{}}}});var oi={};E(oi,{ScheduledTask:()=>vt,Scheduler:()=>vs,SchedulerLock:()=>Se,cronMatches:()=>ys,parseCron:()=>ai,task:()=>qa});function Te(l,e,t){if(l==="*")return null;let s=new Set;for(let r of l.split(",")){let[i,n]=r.split("/"),a=n?parseInt(n,10):1;if(i==="*")for(let o=e;o<=t;o+=a)s.add(o);else if(i.includes("-")){let[o,c]=i.split("-"),u=parseInt(o,10),d=parseInt(c,10);for(let p=u;p<=d;p+=a)s.add(p)}else s.add(parseInt(i,10))}return[...s].sort((r,i)=>r-i)}function ai(l){let e=l.trim().split(/\s+/);if(e.length!==5)throw new Error(`Invalid cron expression: "${l}". Expected 5 fields.`);return{minute:Te(e[0],0,59),hour:Te(e[1],0,23),dayOfMonth:Te(e[2],1,31),month:Te(e[3],1,12),dayOfWeek:Te(e[4],0,6)}}function ys(l,e){let t=ai(l),s=e.getMinutes(),r=e.getHours(),i=e.getDate(),n=e.getMonth()+1,a=e.getDay();return!(t.minute&&!t.minute.includes(s)||t.hour&&!t.hour.includes(r)||t.dayOfMonth&&!t.dayOfMonth.includes(i)||t.month&&!t.month.includes(n)||t.dayOfWeek&&!t.dayOfWeek.includes(a))}function qa(l,e,t){class s extends vt{name=l;schedule(){return t&&t(this),this}async handle(){return e()}}return new s}var vt,vs,li=y(()=>{"use strict";Re();vt=class{name=this.constructor.name;_running=!1;withoutOverlapping=!1;_lockTtlMinutes=5;_expression="* * * * *";schedule(){return this}onSuccess(){}onFailure(e){console.error(`[Scheduler] Task "${this.name}" failed:`,e.message)}everyMinute(){return this._expression="* * * * *",this}everyMinutes(e){return this._expression=`*/${e} * * * *`,this}everyFiveMinutes(){return this.everyMinutes(5)}everyTenMinutes(){return this.everyMinutes(10)}everyFifteenMinutes(){return this.everyMinutes(15)}everyThirtyMinutes(){return this.everyMinutes(30)}hourly(){return this._expression="0 * * * *",this}hourlyAt(e){return this._expression=`${e} * * * *`,this}daily(){return this._expression="0 0 * * *",this}dailyAt(e){let[t,s]=e.split(":").map(Number);return this._expression=`${s??0} ${t} * * *`,this}twiceDaily(e=1,t=13){return this._expression=`0 ${e},${t} * * *`,this}weekly(){return this._expression="0 0 * * 0",this}weeklyOn(e,t="00:00"){let[s,r]=t.split(":").map(Number);return this._expression=`${r??0} ${s} * * ${e}`,this}weekdays(){return this._expression=`${this._expression.split(" ").slice(0,4).join(" ")} 1-5`,this}weekends(){return this._expression=`${this._expression.split(" ").slice(0,4).join(" ")} 0,6`,this}monthly(){return this._expression="0 0 1 * *",this}monthlyOn(e,t="00:00"){let[s,r]=t.split(":").map(Number);return this._expression=`${r??0} ${s} ${e} * *`,this}quarterly(){return this._expression="0 0 1 1,4,7,10 *",this}yearly(){return this._expression="0 0 1 1 *",this}cron(e){return this._expression=e,this}preventOverlap(){return this.withoutOverlapping=!0,this}lockExpiresAfter(e){return this._lockTtlMinutes=e,this}getExpression(){return this.schedule(),this._expression}isRunning(){return this._running}async executeTask(){if(this.withoutOverlapping&&this._running)return{task:this.name,success:!0,duration:0,timestamp:new Date};let e=!1;if(this.withoutOverlapping)try{let{SchedulerLock:s}=await Promise.resolve().then(()=>(Re(),yt));if(e=await s.acquire(this.name,this._lockTtlMinutes),!e)return{task:this.name,success:!0,duration:0,timestamp:new Date}}catch{}this._running=!0;let t=Date.now();try{await this.handle();let s=Date.now()-t;return await this.onSuccess(),{task:this.name,success:!0,duration:s,timestamp:new Date}}catch(s){let r=Date.now()-t;return await this.onFailure(s),{task:this.name,success:!1,duration:r,error:s.message,timestamp:new Date}}finally{if(this._running=!1,e)try{let{SchedulerLock:s}=await Promise.resolve().then(()=>(Re(),yt));await s.release(this.name)}catch{}}}},vs=class{tasks=[];timer=null;history=[];maxHistory=100;_persistToDb=!1;persistToDatabase(){return this._persistToDb=!0,this}register(e){return this.tasks.push(e),this}registerMany(e){for(let t of e)this.register(t);return this}async run(e){let t=e??new Date,s=[];for(let r of this.tasks){let i=r.getExpression();if(ys(i,t)){let n=await r.executeTask();s.push(n),this.addToHistory(n)}}return s}start(){if(this.timer)return;this.run().catch(s=>console.error("[Scheduler] Error:",s));let t=6e4-Date.now()%6e4;this.timer=setTimeout(()=>{this.run().catch(s=>console.error("[Scheduler] Error:",s)),this.timer=setInterval(()=>{this.run().catch(s=>console.error("[Scheduler] Error:",s))},6e4)},t),console.log(`[Scheduler] Started with ${this.tasks.length} task(s). Next tick in ${Math.round(t/1e3)}s.`)}async stop(){this.timer&&(clearTimeout(this.timer),clearInterval(this.timer),this.timer=null);try{let{SchedulerLock:e}=await Promise.resolve().then(()=>(Re(),yt));await e.releaseAll()}catch{}console.log("[Scheduler] Stopped.")}getTasks(){return[...this.tasks]}getHistory(){return[...this.history]}dueTasks(e){let t=e??new Date;return this.tasks.filter(s=>ys(s.getExpression(),t))}remove(e){let t=this.tasks.findIndex(s=>s.name===e);return t!==-1?(this.tasks.splice(t,1),!0):!1}clear(){this.tasks=[]}addToHistory(e){this.history.push(e),this.history.length>this.maxHistory&&this.history.shift(),this._persistToDb&&this.persistResult(e).catch(()=>{})}_historyTableEnsured=!1;async ensureHistoryTable(){if(this._historyTableEnsured)return;let{Connection:e}=await Promise.resolve().then(()=>(x(),P));switch(e.getDriver()){case"sqlite":await e.raw(`CREATE TABLE IF NOT EXISTS scheduled_task_runs (
|
|
18
18
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19
19
|
task TEXT NOT NULL,
|
|
20
20
|
success INTEGER NOT NULL,
|
|
@@ -35,11 +35,11 @@ var wn=Object.defineProperty;var He=(l=>typeof require<"u"?require:typeof Proxy<
|
|
|
35
35
|
duration INT NOT NULL,
|
|
36
36
|
error TEXT,
|
|
37
37
|
ran_at DATETIME NOT NULL
|
|
38
|
-
) ENGINE=InnoDB`);break}this._historyTableEnsured=!0}async persistResult(e){try{await this.ensureHistoryTable();let{Connection:t}=await Promise.resolve().then(()=>(x(),
|
|
38
|
+
) ENGINE=InnoDB`);break}this._historyTableEnsured=!0}async persistResult(e){try{await this.ensureHistoryTable();let{Connection:t}=await Promise.resolve().then(()=>(x(),P));await t.raw("INSERT INTO scheduled_task_runs (task, success, duration, error, ran_at) VALUES (?, ?, ?, ?, ?)",[e.task,e.success,e.duration,e.error||null,e.timestamp.toISOString()])}catch{}}}});var Ee={};E(Ee,{Job:()=>V,Queue:()=>Ss});var V,wt,bs,J,xt,ws,xs,Cs,Ps,Ss,Q=y(()=>{"use strict";R();V=class{attempts=0;maxAttempts=3;retryDelay=60;queue="default";failed(e){console.error(`[Queue] Job ${this.constructor.name} permanently failed:`,e.message)}retrying(e){}serialize(){let e={};for(let[t,s]of Object.entries(this))typeof s!="function"&&(e[t]=s);return JSON.stringify(e)}restore(e){for(let[t,s]of Object.entries(e))t!=="attempts"&&t!=="maxAttempts"&&t!=="retryDelay"&&t!=="queue"&&(this[t]=s)}},wt=class{async push(e){try{e.job.attempts=1,await e.job.handle()}catch(t){if(e.attempts+1<e.maxAttempts)return e.attempts++,e.job.attempts=e.attempts+1,e.job.retrying(e.job.attempts),this.push(e);e.job.failed(t)}}async pop(){return null}async size(){return 0}async clear(){}},bs=class{queues=new Map;async push(e){let t=e.queue;this.queues.has(t)||this.queues.set(t,[]),this.queues.get(t).push(e)}async pop(e="default"){let t=this.queues.get(e)??[],s=Date.now(),r=t.findIndex(i=>i.availableAt<=s);return r===-1?null:t.splice(r,1)[0]}async size(e="default"){return this.queues.get(e)?.length??0}async clear(e){e?this.queues.delete(e):this.queues.clear()}},J=class{constructor(e,t){this.table=e;this.registry=t}async getConnection(){let{Connection:e}=await Promise.resolve().then(()=>(x(),P));return e}async push(e){await(await this.getConnection()).raw(`INSERT INTO ${this.table} (id, queue, payload, attempts, max_attempts, available_at, created_at)
|
|
39
39
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,[e.id,e.queue,JSON.stringify({jobClass:e.jobClass,payload:e.payload}),e.attempts,e.maxAttempts,Math.floor(e.availableAt/1e3),Math.floor(e.createdAt/1e3)])}async pop(e="default"){let t=await this.getConnection(),s=Math.floor(Date.now()/1e3),r=await t.raw(`SELECT * FROM ${this.table}
|
|
40
40
|
WHERE queue = ? AND available_at <= ? AND reserved_at IS NULL
|
|
41
|
-
ORDER BY created_at ASC LIMIT 1`,[e,s]);if(!r||r.length===0)return null;let
|
|
42
|
-
VALUES (?, ?, ?, ?, ?, ?)`,[crypto.randomUUID(),e.queue,e.jobClass,e.payload,t.stack??t.message,Math.floor(Date.now()/1e3)])}catch{console.error("[Queue] Could not persist failed job (run migration to create svelar_failed_jobs table)")}}async all(){return(await(await this.getConnection()).raw(`SELECT * FROM ${this.table} ORDER BY failed_at DESC`,[])??[]).map(s=>({id:s.id,queue:s.queue,jobClass:s.job_class,payload:s.payload,exception:s.exception,failedAt:s.failed_at}))}async find(e){let s=await(await this.getConnection()).raw(`SELECT * FROM ${this.table} WHERE id = ? LIMIT 1`,[e]);if(!s||s.length===0)return null;let r=s[0];return{id:r.id,queue:r.queue,jobClass:r.job_class,payload:r.payload,exception:r.exception,failedAt:r.failed_at}}async forget(e){return await(await this.getConnection()).raw(`DELETE FROM ${this.table} WHERE id = ?`,[e]),!0}async flush(){let e=await this.getConnection(),s=(await e.raw(`SELECT COUNT(*) as count FROM ${this.table}`,[]))?.[0]?.count??0;return await e.raw(`DELETE FROM ${this.table}`,[]),s}},Ps=class{jobs=new Map;register(e){this.jobs.set(e.name,e)}registerAll(e){for(let t of e)this.register(t)}resolve(e,t){let s=this.jobs.get(e);if(!s)throw new Error(`Job class "${e}" is not registered. Call Queue.register(${e}) in your app bootstrap. Registered jobs: [${[...this.jobs.keys()].join(", ")}]`);let r=Object.create(s.prototype);r.attempts=0,r.maxAttempts=3,r.retryDelay=60,r.queue="default";try{let n=JSON.parse(t);r.restore(n)}catch{}return r}has(e){return this.jobs.has(e)}},Ss=class{config={default:"sync",connections:{sync:{driver:"sync"}}};drivers=new Map;processing=!1;jobRegistry=new Ps;failedStore=new Cs;_activeWorker=null;configure(e){this.config=e,this.drivers.clear()}register(e){this.jobRegistry.register(e)}registerAll(e){this.jobRegistry.registerAll(e)}async dispatch(e,t){let s=this.config.default,r=this.config.connections[s],n=this.resolveDriver(s);t?.queue&&(e.queue=t.queue),t?.maxAttempts!==void 0&&(e.maxAttempts=t.maxAttempts);let i={id:crypto.randomUUID(),jobClass:e.constructor.name,payload:e.serialize(),queue:e.queue??r?.queue??"default",attempts:0,maxAttempts:e.maxAttempts,availableAt:Date.now()+(t?.delay??0)*1e3,createdAt:Date.now(),job:e};await n.push(i)}async dispatchSync(e){let t=new Ct,s={id:crypto.randomUUID(),jobClass:e.constructor.name,payload:e.serialize(),queue:e.queue,attempts:0,maxAttempts:e.maxAttempts,availableAt:Date.now(),createdAt:Date.now(),job:e};await t.push(s)}async chain(e,t){if(e.length===0)return;let s=new Ts(e);t?.queue&&(s.queue=t.queue),t?.maxAttempts!==void 0&&(s.maxAttempts=t.maxAttempts),await this.dispatch(s,t)}async work(e){let t=this.config.default,s=this.resolveDriver(t),r=e?.queue??"default";if(s instanceof Pt){let o=await s.createWorker(r,this.jobRegistry,this.failedStore,{concurrency:e?.concurrency??1});return this.processing=!0,this._activeWorker=o,await new Promise(c=>{let u=()=>{this.processing?setTimeout(u,500):o.close().then(c).catch(c)};u()}),0}let n=e?.maxJobs??1/0,i=(e?.sleep??1)*1e3,a=0;for(this.processing=!0;this.processing&&a<n;){let o=await s.pop(r);if(!o){if(n===1/0){await new Promise(c=>setTimeout(c,i));continue}break}o.attempts++,o.job.attempts=o.attempts;try{await o.job.handle(),a++,s instanceof V&&await s.delete(o.id)}catch(c){if(o.attempts<o.maxAttempts){o.job.retrying(o.attempts);let u=o.job.retryDelay??60;s instanceof V?await s.release(o.id,u):(o.availableAt=Date.now()+u*1e3,await s.push(o))}else o.job.failed(c),await this.failedStore.store(o,c),s instanceof V&&await s.delete(o.id)}}return a}async stop(){this.processing=!1,this._activeWorker&&(await this._activeWorker.close(),this._activeWorker=null)}async size(e){return this.resolveDriver(this.config.default).size(e)}async clear(e){return this.resolveDriver(this.config.default).clear(e)}async failed(){return this.failedStore.all()}async retry(e){let t=await this.failedStore.find(e);if(!t)return!1;let s=this.jobRegistry.resolve(t.jobClass,t.payload);return s.queue=t.queue,await this.dispatch(s,{queue:t.queue}),await this.failedStore.forget(e),!0}async retryAll(){let e=await this.failedStore.all(),t=0;for(let s of e)try{let r=this.jobRegistry.resolve(s.jobClass,s.payload);r.queue=s.queue,await this.dispatch(r,{queue:s.queue}),await this.failedStore.forget(s.id),t++}catch{}return t}async forgetFailed(e){return this.failedStore.forget(e)}async flushFailed(){return this.failedStore.flush()}resolveDriver(e){if(this.drivers.has(e))return this.drivers.get(e);let t=this.config.connections[e];if(!t)throw new Error(`Queue connection "${e}" is not defined.`);let s;switch(t.driver){case"sync":s=new Ct;break;case"memory":s=new xs;break;case"database":s=new V(t.table??"svelar_jobs",this.jobRegistry);break;case"redis":s=new Pt(t,this.jobRegistry);break;default:throw new Error(`Unknown queue driver: ${t.driver}`)}return this.drivers.set(e,s),s}},Ts=class extends Q{remainingJobs;constructor(e){super(),this.remainingJobs=[...e],this.maxAttempts=1}async handle(){for(let e of this.remainingJobs){let t=null,s=!1;for(let r=1;r<=e.maxAttempts;r++){e.attempts=r;try{await e.handle(),s=!0;break}catch(n){t=n,r<e.maxAttempts&&(e.retrying(r),e.retryDelay>0&&await new Promise(i=>setTimeout(i,e.retryDelay*1e3)))}}if(!s&&t)throw e.failed(t),new Error(`Chain stopped: ${e.constructor.name} failed after ${e.maxAttempts} attempt(s). Remaining jobs: [${this.remainingJobs.slice(this.remainingJobs.indexOf(e)+1).map(r=>r.constructor.name).join(", ")}]`)}}serialize(){return JSON.stringify({jobs:this.remainingJobs.map(e=>({jobClass:e.constructor.name,payload:e.serialize()}))})}},Rs=w("svelar.queue",()=>new Ss)});var N,Es=v(()=>{"use strict";x();N=class l{tableName;selectColumns=["*"];whereClauses=[];joinClauses=[];orderClauses=[];groupByColumns=[];havingClauses=[];limitValue=null;offsetValue=null;eagerLoads=[];isDistinct=!1;connectionName;cteClauses=[];unionClauses=[];modelClass;constructor(e,t,s){this.tableName=e,this.modelClass=t,this.connectionName=s}select(...e){return this.selectColumns=e.length>0?e:["*"],this}addSelect(...e){return this.selectColumns[0]==="*"?this.selectColumns=e:this.selectColumns.push(...e),this}distinct(){return this.isDistinct=!0,this}from(e){return this.tableName=e,this}where(e,t,s){return s===void 0?this.whereClauses.push({type:"basic",column:e,operator:"=",value:t,boolean:"AND"}):this.whereClauses.push({type:"basic",column:e,operator:t,value:s,boolean:"AND"}),this}orWhere(e,t,s){return s===void 0?this.whereClauses.push({type:"basic",column:e,operator:"=",value:t,boolean:"OR"}):this.whereClauses.push({type:"basic",column:e,operator:t,value:s,boolean:"OR"}),this}whereIn(e,t){return this.whereClauses.push({type:"in",column:e,values:t,boolean:"AND"}),this}whereNotIn(e,t){return this.whereClauses.push({type:"notIn",column:e,values:t,boolean:"AND"}),this}whereNull(e){return this.whereClauses.push({type:"null",column:e,boolean:"AND"}),this}whereNotNull(e){return this.whereClauses.push({type:"notNull",column:e,boolean:"AND"}),this}whereBetween(e,t){return this.whereClauses.push({type:"between",column:e,values:t,boolean:"AND"}),this}whereRaw(e,t=[]){return this.whereClauses.push({type:"raw",raw:e,values:t,boolean:"AND"}),this}whereNested(e,t="AND"){let s=new l(this.tableName,this.modelClass,this.connectionName);if(e(s),s.whereClauses.length>0){let{whereSQL:r,whereBindings:n}=s.buildWhere(),i=r.replace(/^WHERE /,"");this.whereClauses.push({type:"raw",raw:`(${i})`,values:n,boolean:t})}return this}orWhereNested(e){return this.whereNested(e,"OR")}whereExists(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.whereClauses.push({type:"exists",subSQL:s,subBindings:r,boolean:"AND"}),this}whereNotExists(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.whereClauses.push({type:"notExists",subSQL:s,subBindings:r,boolean:"AND"}),this}whereSub(e,t,s){let r=new l("__placeholder__",void 0,this.connectionName);s(r);let{sql:n,bindings:i}=r.toSQL();return this.whereClauses.push({type:"sub",column:e,operator:t,subSQL:n,subBindings:i,boolean:"AND"}),this}orWhereRaw(e,t=[]){return this.whereClauses.push({type:"raw",raw:e,values:t,boolean:"OR"}),this}orWhereIn(e,t){return this.whereClauses.push({type:"in",column:e,values:t,boolean:"OR"}),this}orWhereNull(e){return this.whereClauses.push({type:"null",column:e,boolean:"OR"}),this}orWhereNotNull(e){return this.whereClauses.push({type:"notNull",column:e,boolean:"OR"}),this}withCTE(e,t,s=!1){let r=new l("__placeholder__",void 0,this.connectionName);t(r);let{sql:n,bindings:i}=r.toSQL();return this.cteClauses.push({name:e,sql:n,bindings:i,recursive:s}),this}withRecursiveCTE(e,t){return this.withCTE(e,t,!0)}withRawCTE(e,t,s=[],r=!1){return this.cteClauses.push({name:e,sql:t,bindings:s,recursive:r}),this}union(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.unionClauses.push({sql:s,bindings:r,all:!1}),this}unionAll(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.unionClauses.push({sql:s,bindings:r,all:!0}),this}join(e,t,s,r){return this.joinClauses.push({type:"INNER",table:e,first:t,operator:s,second:r}),this}leftJoin(e,t,s,r){return this.joinClauses.push({type:"LEFT",table:e,first:t,operator:s,second:r}),this}rightJoin(e,t,s,r){return this.joinClauses.push({type:"RIGHT",table:e,first:t,operator:s,second:r}),this}crossJoin(e){return this.joinClauses.push({type:"CROSS",table:e,first:"",operator:"",second:""}),this}orderBy(e,t="asc"){return this.orderClauses.push({column:e,direction:t}),this}latest(e="created_at"){return this.orderBy(e,"desc")}oldest(e="created_at"){return this.orderBy(e,"asc")}groupBy(...e){return this.groupByColumns.push(...e),this}having(e,t,s){return this.havingClauses.push({type:"basic",column:e,operator:t,value:s,boolean:"AND"}),this}limit(e){return this.limitValue=e,this}offset(e){return this.offsetValue=e,this}take(e){return this.limit(e)}skip(e){return this.offset(e)}with(...e){return this.eagerLoads.push(...e),this}async get(){let{sql:e,bindings:t}=this.toSQL(),s=await g.raw(e,t,this.connectionName),r=this.hydrateMany(s);return this.eagerLoads.length>0&&this.modelClass&&await this.loadRelations(r),r}async first(){return this.limitValue=1,(await this.get())[0]??null}async firstOrFail(){let e=await this.first();if(!e)throw new Error(`No results found for query on "${this.tableName}".`);return e}async find(e,t="id"){return this.where(t,e).first()}async findOrFail(e,t="id"){return this.where(t,e).firstOrFail()}async count(e="*"){let{sql:t,bindings:s}=this.buildAggregate(`COUNT(${e})`),r=await g.raw(t,s,this.connectionName);return Number(r[0]?.aggregate??0)}async sum(e){let{sql:t,bindings:s}=this.buildAggregate(`SUM(${e})`),r=await g.raw(t,s,this.connectionName);return Number(r[0]?.aggregate??0)}async avg(e){let{sql:t,bindings:s}=this.buildAggregate(`AVG(${e})`),r=await g.raw(t,s,this.connectionName);return Number(r[0]?.aggregate??0)}async max(e){let{sql:t,bindings:s}=this.buildAggregate(`MAX(${e})`);return(await g.raw(t,s,this.connectionName))[0]?.aggregate??null}async min(e){let{sql:t,bindings:s}=this.buildAggregate(`MIN(${e})`);return(await g.raw(t,s,this.connectionName))[0]?.aggregate??null}async exists(){return await this.count()>0}async doesntExist(){return!await this.exists()}async pluck(e){return this.selectColumns=[e],(await g.raw(this.toSQL().sql,this.toSQL().bindings,this.connectionName)).map(s=>s[e])}async value(e){return this.selectColumns=[e],this.limitValue=1,(await g.raw(this.toSQL().sql,this.toSQL().bindings,this.connectionName))[0]?.[e]??null}async chunk(e,t){let s=1,r=!0;for(;r;){let n=this.clone();n.limitValue=e,n.offsetValue=(s-1)*e;let i=await n.get();if(i.length===0||await t(i,s)===!1||i.length<e)break;s++}}when(e,t){return e&&t(this),this}selectRaw(e){return this.selectColumns[0]==="*"?this.selectColumns=[e]:this.selectColumns.push(e),this}async upsert(e,t,s){let r=g.getDriver(this.connectionName),n=Object.keys(e),i=Object.values(e),a=i.map(()=>"?").join(", "),o=s??n.filter(u=>!t.includes(u)),c;if(r==="postgres"){let u=o.map(d=>`${d} = EXCLUDED.${d}`).join(", ");c=`INSERT INTO ${this.tableName} (${n.join(", ")}) VALUES (${a}) ON CONFLICT (${t.join(", ")}) DO UPDATE SET ${u}`}else if(r==="mysql"){let u=o.map(d=>`${d} = VALUES(${d})`).join(", ");c=`INSERT INTO ${this.tableName} (${n.join(", ")}) VALUES (${a}) ON DUPLICATE KEY UPDATE ${u}`}else{let u=o.map(d=>`${d} = excluded.${d}`).join(", ");c=`INSERT INTO ${this.tableName} (${n.join(", ")}) VALUES (${a}) ON CONFLICT (${t.join(", ")}) DO UPDATE SET ${u}`}return g.raw(c,i,this.connectionName)}async insertMany(e){if(e.length===0)return;let t=Object.keys(e[0]),s=[],r=[];for(let i of e){let a=t.map(o=>i[o]);s.push(...a),r.push(`(${a.map(()=>"?").join(", ")})`)}let n=`INSERT INTO ${this.tableName} (${t.join(", ")}) VALUES ${r.join(", ")}`;return g.raw(n,s,this.connectionName)}async firstOrCreate(e,t={}){for(let[i,a]of Object.entries(e))this.where(i,a);let s=await this.first();if(s)return s;let r={...e,...t},n=await new l(this.tableName,this.modelClass,this.connectionName).insertGetId(r);return new l(this.tableName,this.modelClass,this.connectionName).findOrFail(n)}async updateOrCreate(e,t){let s=new l(this.tableName,this.modelClass,this.connectionName);for(let[a,o]of Object.entries(e))s.where(a,o);let r=await s.first();if(r)return await new l(this.tableName,this.modelClass,this.connectionName).where(this.modelClass?.primaryKey??"id",r[this.modelClass?.primaryKey??"id"]).update(t),new l(this.tableName,this.modelClass,this.connectionName).findOrFail(r[this.modelClass?.primaryKey??"id"]);let n={...e,...t},i=await new l(this.tableName,this.modelClass,this.connectionName).insertGetId(n);return new l(this.tableName,this.modelClass,this.connectionName).findOrFail(i)}whereColumn(e,t,s){return s===void 0?this.whereClauses.push({type:"raw",raw:`${e} = ${t}`,values:[],boolean:"AND"}):this.whereClauses.push({type:"raw",raw:`${e} ${t} ${s}`,values:[],boolean:"AND"}),this}havingRaw(e,t=[]){return this.havingClauses.push({type:"raw",raw:e,values:t,boolean:"AND"}),this}orderByRaw(e){return this.orderClauses.push({column:e,direction:"asc"}),this.orderClauses[this.orderClauses.length-1].__raw=!0,this}selectSub(e,t){let s=new l("__placeholder__",void 0,this.connectionName);e(s);let{sql:r,bindings:n}=s.toSQL(),i=`(${r}) as ${t}`;return this.selectColumns[0]==="*"?this.selectColumns=[i]:this.selectColumns.push(i),this._selectBindings||(this._selectBindings=[]),this._selectBindings.push(...n),this}_selectBindings;async truncate(){g.getDriver(this.connectionName)==="sqlite"?(await g.raw(`DELETE FROM ${this.tableName}`,[],this.connectionName),await g.raw("DELETE FROM sqlite_sequence WHERE name = ?",[this.tableName],this.connectionName)):await g.raw(`TRUNCATE TABLE ${this.tableName}`,[],this.connectionName)}async paginate(e=1,t=15){let s=await this.clone().count(),r=Math.ceil(s/t);return this.limitValue=t,this.offsetValue=(e-1)*t,{data:await this.get(),total:s,page:e,perPage:t,lastPage:r,hasMore:e<r}}async insert(e){let t=Object.keys(e),s=Object.values(e),r=s.map(()=>"?").join(", "),n=`INSERT INTO ${this.tableName} (${t.join(", ")}) VALUES (${r})`;return g.raw(n,s,this.connectionName)}async insertGetId(e,t="id"){let s=g.getDriver(this.connectionName),r=Object.keys(e),n=Object.values(e),i=n.map(()=>"?").join(", "),a=`INSERT INTO ${this.tableName} (${r.join(", ")}) VALUES (${i})`;return s==="postgres"?(a+=` RETURNING ${t}`,(await g.raw(a,n,this.connectionName))[0]?.[t]):(await g.raw(a,n,this.connectionName),s==="sqlite"?(await g.raw("SELECT last_insert_rowid() as id",[],this.connectionName))[0]?.id:s==="mysql"?(await g.raw("SELECT LAST_INSERT_ID() as id",[],this.connectionName))[0]?.id:0)}async update(e){let t=Object.keys(e),s=Object.values(e),r=t.map(o=>`${o} = ?`).join(", "),{whereSQL:n,whereBindings:i}=this.buildWhere(),a=`UPDATE ${this.tableName} SET ${r}${n}`;return await g.raw(a,[...s,...i],this.connectionName),1}async delete(){let{whereSQL:e,whereBindings:t}=this.buildWhere(),s=`DELETE FROM ${this.tableName}${e}`;return await g.raw(s,t,this.connectionName),1}async increment(e,t=1){let{whereSQL:s,whereBindings:r}=this.buildWhere(),n=`UPDATE ${this.tableName} SET ${e} = ${e} + ?${s}`;await g.raw(n,[t,...r],this.connectionName)}async decrement(e,t=1){return this.increment(e,-t)}toSQL(){let e=[],t=[];if(this.cteClauses.length>0){let a=this.cteClauses.some(c=>c.recursive)?"WITH RECURSIVE":"WITH",o=this.cteClauses.map(c=>(t.push(...c.bindings),`${c.name} AS (${c.sql})`));e.push(`${a} ${o.join(", ")}`)}this._selectBindings?.length&&t.push(...this._selectBindings);let s=this.isDistinct?"DISTINCT ":"";e.push(`SELECT ${s}${this.selectColumns.join(", ")}`),e.push(`FROM ${this.tableName}`);for(let i of this.joinClauses)i.type==="CROSS"?e.push(`CROSS JOIN ${i.table}`):e.push(`${i.type} JOIN ${i.table} ON ${i.first} ${i.operator} ${i.second}`);let{whereSQL:r,whereBindings:n}=this.buildWhere();if(r&&(e.push(r.trim()),t.push(...n)),this.groupByColumns.length>0&&e.push(`GROUP BY ${this.groupByColumns.join(", ")}`),this.havingClauses.length>0){let i=[];for(let a of this.havingClauses)a.type==="raw"?(i.push(a.raw),a.values&&t.push(...a.values)):(i.push(`${a.column} ${a.operator} ?`),t.push(a.value));e.push(`HAVING ${i.join(" AND ")}`)}if(this.orderClauses.length>0){let i=this.orderClauses.map(a=>a.__raw?a.column:`${a.column} ${a.direction.toUpperCase()}`);e.push(`ORDER BY ${i.join(", ")}`)}if(this.limitValue!==null&&e.push(`LIMIT ${this.limitValue}`),this.offsetValue!==null&&e.push(`OFFSET ${this.offsetValue}`),this.unionClauses.length>0)for(let i of this.unionClauses)e.push(i.all?"UNION ALL":"UNION"),e.push(i.sql),t.push(...i.bindings);return{sql:e.join(" "),bindings:t}}clone(){let e=new l(this.tableName,this.modelClass,this.connectionName);return e.selectColumns=[...this.selectColumns],e.whereClauses=[...this.whereClauses],e.joinClauses=[...this.joinClauses],e.orderClauses=[...this.orderClauses],e.groupByColumns=[...this.groupByColumns],e.havingClauses=[...this.havingClauses],e.limitValue=this.limitValue,e.offsetValue=this.offsetValue,e.eagerLoads=[...this.eagerLoads],e.isDistinct=this.isDistinct,e.cteClauses=[...this.cteClauses],e.unionClauses=[...this.unionClauses],e}buildWhere(){if(this.whereClauses.length===0)return{whereSQL:"",whereBindings:[]};let e=[],t=[];for(let s=0;s<this.whereClauses.length;s++){let r=this.whereClauses[s],n=s===0?"WHERE":r.boolean;switch(r.type){case"basic":if((r.operator==="IS"||r.operator==="IS NOT")&&r.value===null){let o=(r.operator==="IS","null");e.push(`${n} ${r.column} ${r.operator} ${o}`)}else e.push(`${n} ${r.column} ${r.operator} ?`),t.push(r.value);break;case"in":let i=r.values.map(()=>"?").join(", ");e.push(`${n} ${r.column} IN (${i})`),t.push(...r.values);break;case"notIn":let a=r.values.map(()=>"?").join(", ");e.push(`${n} ${r.column} NOT IN (${a})`),t.push(...r.values);break;case"null":e.push(`${n} ${r.column} IS NULL`);break;case"notNull":e.push(`${n} ${r.column} IS NOT NULL`);break;case"between":e.push(`${n} ${r.column} BETWEEN ? AND ?`),t.push(r.values[0],r.values[1]);break;case"raw":e.push(`${n} ${r.raw}`),r.values&&t.push(...r.values);break;case"exists":e.push(`${n} EXISTS (${r.subSQL})`),r.subBindings&&t.push(...r.subBindings);break;case"notExists":e.push(`${n} NOT EXISTS (${r.subSQL})`),r.subBindings&&t.push(...r.subBindings);break;case"sub":e.push(`${n} ${r.column} ${r.operator} (${r.subSQL})`),r.subBindings&&t.push(...r.subBindings);break}}return{whereSQL:e.join(" "),whereBindings:t}}buildAggregate(e){let t=this.selectColumns;this.selectColumns=[`${e} as aggregate`];let s=this.toSQL();return this.selectColumns=t,s}hydrateMany(e){return this.modelClass?e.map(t=>this.modelClass.hydrate(t)):e}async loadRelations(e){if(!(!this.modelClass||e.length===0))for(let t of this.eagerLoads){let r=new this.modelClass()[t]?.();r&&typeof r.eagerLoad=="function"&&await r.eagerLoad(e,t)}}}});var Y,X,Z,ee,te,ks=v(()=>{"use strict";x();Y=class{parentModel;relatedModel;constructor(e,t){this.parentModel=e,this.relatedModel=t}query(){return this.relatedModel.query()}},X=class extends Y{constructor(t,s,r,n="id"){super(t,s);this.foreignKey=r;this.localKey=n}async load(t){let s=t.getAttribute(this.localKey);return this.relatedModel.query().where(this.foreignKey,s).first()}async eagerLoad(t,s){let r=t.map(a=>a.getAttribute(this.localKey)),n=await this.relatedModel.query().whereIn(this.foreignKey,r).get(),i=new Map;for(let a of n)i.set(a.getAttribute(this.foreignKey),a);for(let a of t){let o=a.getAttribute(this.localKey);a.setRelation(s,i.get(o)??null)}}async create(t){let s=this.parentModel.getAttribute(this.localKey);return this.relatedModel.create({...t,[this.foreignKey]:s})}},Z=class extends Y{constructor(t,s,r,n="id"){super(t,s);this.foreignKey=r;this.localKey=n}async load(t){let s=t.getAttribute(this.localKey);return this.relatedModel.query().where(this.foreignKey,s).get()}async eagerLoad(t,s){let r=t.map(a=>a.getAttribute(this.localKey)),n=await this.relatedModel.query().whereIn(this.foreignKey,r).get(),i=new Map;for(let a of n){let o=a.getAttribute(this.foreignKey);i.has(o)||i.set(o,[]),i.get(o).push(a)}for(let a of t){let o=a.getAttribute(this.localKey);a.setRelation(s,i.get(o)??[])}}async create(t){let s=this.parentModel.getAttribute(this.localKey);return this.relatedModel.create({...t,[this.foreignKey]:s})}async createMany(t){let s=[];for(let r of t)s.push(await this.create(r));return s}},ee=class extends Y{constructor(t,s,r,n="id"){super(t,s);this.foreignKey=r;this.ownerKey=n}async load(t){let s=t.getAttribute(this.foreignKey);return s==null?null:this.relatedModel.query().where(this.ownerKey,s).first()}async eagerLoad(t,s){let r=t.map(a=>a.getAttribute(this.foreignKey)).filter(a=>a!=null);if(r.length===0){for(let a of t)a.setRelation(s,null);return}let n=await this.relatedModel.query().whereIn(this.ownerKey,r).get(),i=new Map;for(let a of n)i.set(a.getAttribute(this.ownerKey),a);for(let a of t){let o=a.getAttribute(this.foreignKey);a.setRelation(s,i.get(o)??null)}}associate(t){return this.parentModel.setAttribute(this.foreignKey,t.getAttribute(this.ownerKey)),this.parentModel}dissociate(){return this.parentModel.setAttribute(this.foreignKey,null),this.parentModel}},te=class extends Y{constructor(t,s,r,n,i,a="id",o="id"){super(t,s);this.pivotTable=r;this.foreignPivotKey=n;this.relatedPivotKey=i;this.parentKey=a;this.relatedKey=o}async load(t){let s=t.getAttribute(this.parentKey),r=this.relatedModel.tableName,i=(await g.raw(`SELECT ${this.relatedPivotKey} FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ?`,[s])).map(a=>a[this.relatedPivotKey]);return i.length===0?[]:this.relatedModel.query().whereIn(this.relatedKey,i).get()}async eagerLoad(t,s){let r=t.map(d=>d.getAttribute(this.parentKey));if(r.length===0){for(let d of t)d.setRelation(s,[]);return}let n=r.map(()=>"?").join(", "),i=await g.raw(`SELECT * FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} IN (${n})`,r),a=[...new Set(i.map(d=>d[this.relatedPivotKey]))],o=a.length>0?await this.relatedModel.query().whereIn(this.relatedKey,a).get():[],c=new Map;for(let d of o)c.set(d.getAttribute(this.relatedKey),d);let u=new Map;for(let d of i){let p=d[this.foreignPivotKey],f=d[this.relatedPivotKey],y=c.get(f);y&&(u.has(p)||u.set(p,[]),u.get(p).push(y))}for(let d of t){let p=d.getAttribute(this.parentKey);d.setRelation(s,u.get(p)??[])}}async attach(t,s){let r=this.parentModel.getAttribute(this.parentKey),n={[this.foreignPivotKey]:r,[this.relatedPivotKey]:t,...s},i=Object.keys(n),a=Object.values(n),o=a.map(()=>"?").join(", ");await g.raw(`INSERT INTO ${this.pivotTable} (${i.join(", ")}) VALUES (${o})`,a)}async detach(t){let s=this.parentModel.getAttribute(this.parentKey);t?await g.raw(`DELETE FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ? AND ${this.relatedPivotKey} = ?`,[s,t]):await g.raw(`DELETE FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ?`,[s])}async sync(t){await this.detach();for(let s of t)await this.attach(s)}async toggle(t){let s=this.parentModel.getAttribute(this.parentKey);for(let r of t)(await g.raw(`SELECT 1 FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ? AND ${this.relatedPivotKey} = ? LIMIT 1`,[s,r])).length>0?await this.detach(r):await this.attach(r)}}});var mi=v(()=>{"use strict"});var $e,$s=v(()=>{"use strict";$e=class{app;constructor(e){this.app=e}boot(){}}});var pi=v(()=>{"use strict";$s();kt()});var Ae,se,kt=v(()=>{"use strict";T();mi();pi();Ae=class{listeners=new Map;wildcardListeners=[];onceListeners=new Map;listen(e,t){let s=typeof e=="string"?e:e.name;return this.listeners.has(s)||this.listeners.set(s,[]),this.listeners.get(s).push(t),()=>{let r=this.listeners.get(s);if(r){let n=r.indexOf(t);n>=0&&r.splice(n,1)}}}once(e,t){let s=typeof e=="string"?e:e.name;return this.onceListeners.has(s)||this.onceListeners.set(s,[]),this.onceListeners.get(s).push(t),()=>{let r=this.onceListeners.get(s);if(r){let n=r.indexOf(t);n>=0&&r.splice(n,1)}}}onAny(e){return this.wildcardListeners.push(e),()=>{let t=this.wildcardListeners.indexOf(e);t>=0&&this.wildcardListeners.splice(t,1)}}async dispatch(e){let t=e.constructor.name,s=this.listeners.get(t)??[];for(let n of s)await n(e);let r=this.onceListeners.get(t)??[];for(let n of r)await n(e);this.onceListeners.delete(t);for(let n of this.wildcardListeners)await n(t,e)}async emit(e,t){let s=this.listeners.get(e)??[];for(let n of s)await n(t);let r=this.onceListeners.get(e)??[];for(let n of r)await n(t);this.onceListeners.delete(e);for(let n of this.wildcardListeners)await n(e,t)}subscribe(e){e.subscribe(this)}forget(e){let t=typeof e=="string"?e:e.name;this.listeners.delete(t),this.onceListeners.delete(t)}flush(){this.listeners.clear(),this.onceListeners.clear(),this.wildcardListeners=[]}hasListeners(e){let t=typeof e=="string"?e:e.name;return(this.listeners.get(t)?.length??0)>0||(this.onceListeners.get(t)?.length??0)>0||this.wildcardListeners.length>0}listenerCount(e){let t=typeof e=="string"?e:e.name;return(this.listeners.get(t)?.length??0)+(this.onceListeners.get(t)?.length??0)}},se=w("svelar.event",()=>new Ae)});var $t,hi=v(()=>{"use strict";Es();ks();kt();$t=class l{static table;static primaryKey="id";static incrementing=!0;static timestamps=!0;static createdAt="created_at";static updatedAt="updated_at";static casts={};static fillable=[];static hidden=[];static connection=void 0;static hooks=new Map;static observers=new Map;static events=[];attributes={};originalAttributes={};relations={};exists=!1;constructor(e){return e&&this.fill(e),new Proxy(this,{get(t,s,r){return typeof s=="symbol"||s in t||typeof s!="string"||["table","primaryKey","incrementing","timestamps","casts","fillable","hidden","connection"].includes(s)?Reflect.get(t,s,r):typeof t[s]=="function"?t[s].bind(t):t.getAttribute(s)},set(t,s,r){return typeof s=="symbol"||s in t?Reflect.set(t,s,r):(t.setAttribute(s,r),!0)}})}static query(){let e=new this,t=this;return new N(t.table,this,t.connection)}static async find(e){let t=this;return this.query().find(e,t.primaryKey)}static async findOrFail(e){let t=this;return this.query().findOrFail(e,t.primaryKey)}static async all(){return this.query().get()}static async first(){return this.query().first()}static async firstOrFail(){return this.query().firstOrFail()}static where(e,t,s){return this.query().where(e,t,s)}static whereIn(e,t){return this.query().whereIn(e,t)}static whereNull(e){return this.query().whereNull(e)}static whereNotNull(e){return this.query().whereNotNull(e)}static orderBy(e,t){return this.query().orderBy(e,t)}static latest(e){return this.query().latest(e)}static oldest(e){return this.query().oldest(e)}static with(...e){return this.query().with(...e)}static async count(){return this.query().count()}static async create(e){let t=new this,s=this;if(t.fill(e),await t.fireHook("creating"),await t.fireHook("saving"),s.timestamps){let a=new Date().toISOString();t.setAttribute(s.createdAt,a),t.setAttribute(s.updatedAt,a)}let r=t.getInsertableAttributes(),i=await new N(s.table,this,s.connection).insertGetId(r,s.primaryKey);return s.incrementing&&i&&t.setAttribute(s.primaryKey,i),t.syncOriginal(),t.exists=!0,await t.fireHook("created"),await t.fireHook("saved"),t}async save(){let e=this.constructor;if(this.exists){await this.fireHook("updating"),await this.fireHook("saving"),e.timestamps&&this.setAttribute(e.updatedAt,new Date().toISOString());let t=this.getDirty();if(Object.keys(t).length>0){let s=this.getAttribute(e.primaryKey);await new N(e.table,this.constructor,e.connection).where(e.primaryKey,s).update(t)}this.syncOriginal(),await this.fireHook("updated"),await this.fireHook("saved")}else{if(await this.fireHook("creating"),await this.fireHook("saving"),e.timestamps){let n=new Date().toISOString();this.getAttribute(e.createdAt)||this.setAttribute(e.createdAt,n),this.setAttribute(e.updatedAt,n)}let t=this.getInsertableAttributes(),r=await new N(e.table,this.constructor,e.connection).insertGetId(t,e.primaryKey);e.incrementing&&r&&this.setAttribute(e.primaryKey,r),this.syncOriginal(),this.exists=!0,await this.fireHook("created"),await this.fireHook("saved")}}async update(e){this.fill(e),await this.save()}async delete(){let e=this.constructor;await this.fireHook("deleting");let t=this.getAttribute(e.primaryKey);await new N(e.table,this.constructor,e.connection).where(e.primaryKey,t).delete(),this.exists=!1,await this.fireHook("deleted")}async refresh(){let e=this.constructor,t=this.getAttribute(e.primaryKey),s=await this.constructor.find(t);s&&(this.attributes={...s.attributes},this.syncOriginal())}getAttribute(e){let t=this.constructor,s=this.attributes[e],r=t.casts[e];if(r&&s!==void 0&&s!==null)switch(r){case"number":return Number(s);case"boolean":return!!s;case"string":return String(s);case"date":return new Date(s);case"json":return typeof s=="string"?JSON.parse(s):s}return s}setAttribute(e,t){this.constructor.casts[e]==="json"&&typeof t!="string"?this.attributes[e]=JSON.stringify(t):this.attributes[e]=t}fill(e){let t=this.constructor;for(let[s,r]of Object.entries(e))t.fillable.length>0&&!t.fillable.includes(s)||this.setAttribute(s,r)}getAttributes(){return{...this.attributes}}getOriginal(e){return e?this.originalAttributes[e]:{...this.originalAttributes}}getDirty(){let e={};for(let[t,s]of Object.entries(this.attributes))s!==this.originalAttributes[t]&&(e[t]=s);return e}isDirty(...e){let t=this.getDirty();return e.length===0?Object.keys(t).length>0:e.some(s=>s in t)}isClean(...e){return!this.isDirty(...e)}wasChanged(...e){return this.isDirty(...e)}hasOne(e,t,s){return new X(this,e,t,s??this.constructor.primaryKey)}hasMany(e,t,s){return new Z(this,e,t,s??this.constructor.primaryKey)}belongsTo(e,t,s){return new ee(this,e,t,s??e.primaryKey)}belongsToMany(e,t,s,r,n,i){return new te(this,e,t,s,r,n??this.constructor.primaryKey,i??e.primaryKey)}setRelation(e,t){this.relations[e]=t}getRelation(e){return this.relations[e]}relationLoaded(e){return e in this.relations}toJSON(){let e=this.constructor,t={};for(let[s,r]of Object.entries(this.attributes))e.hidden.includes(s)||(t[s]=this.getAttribute(s));for(let[s,r]of Object.entries(this.relations))Array.isArray(r)?t[s]=r.map(n=>n instanceof l?n.toJSON():n):r instanceof l?t[s]=r.toJSON():t[s]=r;return t}toObject(){return this.toJSON()}static hydrate(e){let t=new this;return t.attributes={...e},t.syncOriginal(),t.exists=!0,t}static boot(e){this.hooks.set(this.name,e)}static observe(e){let t=this.observers.get(this.name)??[];t.push(e),this.observers.set(this.name,t)}static removeObservers(){this.observers.delete(this.name)}async fireHook(e){let t=this.constructor,s=l.hooks.get(t.name);s?.[e]&&await s[e](this),typeof this[e]=="function"&&await this[e]();let r=l.observers.get(t.name)??[];for(let i of r){let a=i[e];typeof a=="function"&&await a.call(i,this)}let n=t.name.toLowerCase();await se.emit(`${n}.${e}`,this)}async fireEvent(e){let t=this.constructor;if(!t.events.includes(e))throw new Error(`Event "${e}" is not declared in ${t.name}.events. Add it to: static events = ['${e}', ...];`);let s=l.observers.get(t.name)??[];for(let n of s){let i=n[e];typeof i=="function"&&await i.call(n,this)}let r=t.name.toLowerCase();await se.emit(`${r}.${e}`,this)}syncOriginal(){this.originalAttributes={...this.attributes}}getInsertableAttributes(){let e=this.constructor,t={...this.attributes};return e.incrementing&&t[e.primaryKey]===void 0&&delete t[e.primaryKey],t}static get tableName(){return this.table}}});var At,gi=v(()=>{"use strict";At=class{async call(e){await new e().run()}}});var H,fi,As=v(()=>{"use strict";T();H=class{bindings=new Map;aliases=new Map;resolved=new Set;bind(e,t){this.bindings.set(e,{factory:t,singleton:!1,tags:[]})}singleton(e,t){this.bindings.set(e,{factory:t,singleton:!0,tags:[]})}instance(e,t){this.bindings.set(e,{factory:()=>t,singleton:!0,instance:t,tags:[]})}alias(e,t){this.aliases.set(e,t)}async make(e){let t=this.resolveAlias(e),s=this.bindings.get(t);if(!s)throw new Error(`No binding found for "${e}" in the container.`);if(s.singleton&&s.instance!==void 0)return s.instance;let r=await s.factory(this);return s.singleton&&(s.instance=r),this.resolved.add(t),r}makeSync(e){let t=this.resolveAlias(e),s=this.bindings.get(t);if(!s)throw new Error(`No binding found for "${e}" in the container.`);if(s.singleton&&s.instance!==void 0)return s.instance;let r=s.factory(this);if(r instanceof Promise)throw new Error(`Binding "${e}" has an async factory. Use container.make() instead of container.makeSync().`);return s.singleton&&(s.instance=r),this.resolved.add(t),r}has(e){let t=this.resolveAlias(e);return this.bindings.has(t)}isResolved(e){return this.resolved.has(this.resolveAlias(e))}tag(e,t){for(let s of e){let r=this.bindings.get(s);r&&r.tags.push(t)}}async tagged(e){let t=[];for(let[s,r]of this.bindings)r.tags.includes(e)&&t.push(await this.make(s));return t}flush(){for(let e of this.bindings.values())e.singleton&&(e.instance=void 0);this.resolved.clear()}forget(e){this.bindings.delete(e),this.resolved.delete(e)}getBindings(){return[...this.bindings.keys()]}resolveAlias(e){return this.aliases.get(e)??e}},fi=w("svelar.container",()=>new H)});var Dt,yi=v(()=>{"use strict";As();Dt=class{container;providers=[];booted=!1;constructor(e){this.container=e??new H,this.container.instance("app",this),this.container.instance("container",this.container)}register(e){let t=new e(this.container);return this.providers.push(t),this}async bootstrap(){if(this.booted)return this;for(let e of this.providers)await e.register();for(let e of this.providers)await e.boot();return this.booted=!0,this}isBooted(){return this.booted}async make(e){return this.container.make(e)}getProviders(){return[...this.providers]}}});var R,q,Nt,re,_t,ie,ne,Mt,ae,oe=v(()=>{"use strict";R=class{},q=class{middleware=[];namedMiddleware=new Map;use(e){return typeof e=="function"&&"prototype"in e&&typeof e.prototype?.handle=="function"?this.middleware.push(new e):this.middleware.push(e),this}register(e,t){return typeof t=="function"&&"prototype"in t&&typeof t.prototype?.handle=="function"?this.namedMiddleware.set(e,new t):this.namedMiddleware.set(e,t),this}get(e){return this.namedMiddleware.get(e)}async execute(e,t,s){let r=[...this.middleware];if(s)for(let i of s){let a=this.namedMiddleware.get(i);a&&r.push(a)}let n=t;for(let i=r.length-1;i>=0;i--){let a=r[i],o=n;typeof a.handle=="function"?n=()=>a.handle(e,o):n=()=>a(e,o)}return n()}count(){return this.middleware.length}},Nt=class extends R{constructor(t={}){super();this.options=t}async handle(t,s){let r=await s();if(!r)return;let n=Array.isArray(this.options.origin)?this.options.origin.join(", "):this.options.origin??"*";return r.headers.set("Access-Control-Allow-Origin",n),r.headers.set("Access-Control-Allow-Methods",(this.options.methods??["GET","POST","PUT","DELETE","PATCH","OPTIONS"]).join(", ")),r.headers.set("Access-Control-Allow-Headers",(this.options.headers??["Content-Type","Authorization"]).join(", ")),this.options.credentials&&r.headers.set("Access-Control-Allow-Credentials","true"),this.options.maxAge&&r.headers.set("Access-Control-Max-Age",String(this.options.maxAge)),t.event.request.method==="OPTIONS"?new Response(null,{status:204,headers:r.headers}):r}},re=class extends R{requests=new Map;maxRequests;windowMs;constructor(e={}){super(),this.maxRequests=e.maxRequests??60,this.windowMs=e.windowMs??6e4}async handle(e,t){let s=e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown",r=Date.now(),n=this.requests.get(s);if(n&&r<n.resetAt){if(n.count>=this.maxRequests)return new Response(JSON.stringify({error:"Too many requests"}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(Math.ceil((n.resetAt-r)/1e3))}});n.count++}else this.requests.set(s,{count:1,resetAt:r+this.windowMs});return t()}},_t=class extends R{async handle(e,t){let s=Date.now(),r=e.event.request.method,n=e.event.url.pathname,i=await t(),a=Date.now()-s,o=i instanceof Response?i.status:200;return console.log(`[${new Date().toISOString()}] ${r} ${n} \u2192 ${o} (${a}ms)`),i}},ie=class extends R{cookieName;headerName;fieldName;excludePaths;onlyPaths;constructor(e={}){super(),this.cookieName=e.cookieName??"XSRF-TOKEN",this.headerName=e.headerName??"X-CSRF-Token",this.fieldName=e.fieldName??"_csrf",this.excludePaths=e.excludePaths??[],this.onlyPaths=e.onlyPaths??null}async handle(e,t){let{event:s}=e,r=s.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(r))return this.setTokenAndContinue(e,t);if((s.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let i=s.url.pathname;if(this.onlyPaths&&!this.onlyPaths.some(c=>i.startsWith(c)))return this.setTokenAndContinue(e,t);if(this.excludePaths.some(c=>i.startsWith(c)))return t();let a=this.getCookieToken(s),o=s.request.headers.get(this.headerName)??await this.getBodyToken(s);return!a||!o||!this.timingSafeEqual(a,o)?new Response(JSON.stringify({message:"CSRF token mismatch"}),{status:419,headers:{"Content-Type":"application/json"}}):t()}async setTokenAndContinue(e,t){let s=await t();if(!(s instanceof Response))return s;let n=this.getCookieToken(e.event)||this.generateToken();return s.headers.append("Set-Cookie",`${this.cookieName}=${n}; Path=/; SameSite=Lax`),s}getCookieToken(e){let s=(e.request.headers.get("cookie")??"").match(new RegExp(`${this.cookieName}=([^;]+)`));return s?s[1]:null}async getBodyToken(e){try{let t=e.request.headers.get("content-type")??"";if(t.includes("application/x-www-form-urlencoded")||t.includes("multipart/form-data"))return(await e.request.clone().formData()).get(this.fieldName);if(t.includes("application/json"))return(await e.request.clone().json())[this.fieldName]??null}catch{}return null}generateToken(){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}timingSafeEqual(e,t){if(e.length!==t.length)return!1;let s=new TextEncoder,r=s.encode(e),n=s.encode(t),i=0;for(let a=0;a<r.length;a++)i|=r[a]^n[a];return i===0}},ne=class extends R{allowedOrigins;constructor(e={}){super(),this.allowedOrigins=new Set(e.allowedOrigins??[])}async handle(e,t){let{event:s}=e,r=s.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(r)||(s.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let i=s.request.headers.get("origin");if(!i)return t();let a=s.url.origin;return i===a||this.allowedOrigins.has(i)?t():new Response(JSON.stringify({message:"Cross-origin request blocked"}),{status:403,headers:{"Content-Type":"application/json"}})}},Mt=class extends R{secret;tolerance;signatureHeader;timestampHeader;onlyPaths;constructor(e){super(),this.secret=e.secret,this.tolerance=e.tolerance??300,this.signatureHeader=e.signatureHeader??"X-Signature",this.timestampHeader=e.timestampHeader??"X-Timestamp",this.onlyPaths=e.onlyPaths??null}async handle(e,t){let{event:s}=e;if(this.onlyPaths){let S=s.url.pathname;if(!this.onlyPaths.some($=>S.startsWith($)))return t()}let r=s.request.headers.get(this.signatureHeader),n=s.request.headers.get(this.timestampHeader);if(!r||!n)return new Response(JSON.stringify({message:"Missing request signature"}),{status:401,headers:{"Content-Type":"application/json"}});let i=parseInt(n,10),a=Math.floor(Date.now()/1e3);if(isNaN(i)||Math.abs(a-i)>this.tolerance)return new Response(JSON.stringify({message:"Request signature expired"}),{status:401,headers:{"Content-Type":"application/json"}});let c=await s.request.clone().text(),{createHmac:u}=await import("crypto"),d=s.request.method.toUpperCase(),p=s.url.pathname+s.url.search,f=`${n}.${d}.${p}.${c}`,y=u("sha256",this.secret).update(f).digest("hex");return r.length!==y.length||!this.timingSafeCompare(r,y)?new Response(JSON.stringify({message:"Invalid request signature"}),{status:401,headers:{"Content-Type":"application/json"}}):t()}timingSafeCompare(e,t){if(e.length!==t.length)return!1;let s=new TextEncoder,r=s.encode(e),n=s.encode(t),i=0;for(let a=0;a<r.length;a++)i|=r[a]^n[a];return i===0}static sign(e,t,s,r,n){let{createHmac:i}=He("crypto"),a=n??Math.floor(Date.now()/1e3),o=`${a}.${t.toUpperCase()}.${s}.${r}`;return{signature:i("sha256",e).update(o).digest("hex"),timestamp:a}}},ae=class extends R{attempts=new Map;maxAttempts;decayMinutes;constructor(e={}){super(),this.maxAttempts=e.maxAttempts??5,this.decayMinutes=e.decayMinutes??1}async handle(e,t){let r=`${e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown"}:${e.event.url.pathname}`,n=Date.now(),i=this.attempts.get(r);if(i){if(n<i.blockedUntil){let o=Math.ceil((i.blockedUntil-n)/1e3);return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:o}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(o)}})}if(i.count>=this.maxAttempts){i.blockedUntil=n+this.decayMinutes*6e4,i.count=0;let o=this.decayMinutes*60;return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:o}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(o)}})}}let a=await t();if(a instanceof Response&&a.status>=400&&a.status<500){let o=this.attempts.get(r)??{count:0,blockedUntil:0};if(o.count++,this.attempts.set(r,o),this.attempts.size>1e4)for(let[c,u]of this.attempts)n>u.blockedUntil+this.decayMinutes*6e4*2&&this.attempts.delete(c)}return a}}});import{z as le}from"zod";function vi(l,e=!1){let t=new l;return e?{GET:t.handle("show"),PUT:t.handle("update"),PATCH:t.handle("update"),DELETE:t.handle("destroy")}:{GET:t.handle("index"),POST:t.handle("store")}}var Ot,U,De,Ne,_e,bi=v(()=>{"use strict";oe();Ot=class{controllerMiddleware=[];middleware(e,t){let s;typeof e=="function"&&e.prototype instanceof R?s=new e:s=e,this.controllerMiddleware.push({middleware:s,only:t?.only,except:t?.except})}handle(e){return async t=>{try{let s=this.controllerMiddleware.filter(r=>!(r.only&&!r.only.includes(e)||r.except&&r.except.includes(e)));if(s.length>0){let r=new q;for(let{middleware:a}of s)r.use(a);let n={event:t,params:t.params,locals:t.locals},i=await r.execute(n,async()=>this.callMethod(e,t));if(i instanceof Response)return i}return await this.callMethod(e,t)}catch(s){return this.handleError(s,t)}}}json(e,t=200,s={}){let r=JSON.stringify(e,null,2);return new Response(r,{status:t,headers:{"Content-Type":"application/json",...s}})}text(e,t=200){return new Response(e,{status:t,headers:{"Content-Type":"text/plain"}})}html(e,t=200){return new Response(e,{status:t,headers:{"Content-Type":"text/html"}})}redirect(e,t=302){return new Response(null,{status:t,headers:{Location:e}})}noContent(){return new Response(null,{status:204})}created(e){return e?this.json(e,201):new Response(null,{status:201})}async validate(e,t){let s=t instanceof le.ZodObject?t:le.object(t),r,n=e.request.headers.get("content-type")??"";if(n.includes("application/json"))r=await e.request.json();else if(n.includes("multipart/form-data")||n.includes("application/x-www-form-urlencoded")){let a=await e.request.formData();r=Object.fromEntries(a)}else r=Object.fromEntries(e.url.searchParams);let i=s.safeParse(r);if(!i.success)throw new U(i.error);return i.data}validateQuery(e,t){let s=t instanceof le.ZodObject?t:le.object(t),r=Object.fromEntries(e.url.searchParams),n=s.safeParse(r);if(!n.success)throw new U(n.error);return n.data}validateParams(e,t){let r=(t instanceof le.ZodObject?t:le.object(t)).safeParse(e.params);if(!r.success)throw new U(r.error);return r.data}handleError(e,t){return e instanceof U?this.json({message:"Validation failed",errors:e.errors},422):e instanceof De?this.json({message:e.message||"Not found"},404):e instanceof Ne?this.json({message:e.message||"Unauthorized"},401):e instanceof _e?this.json({message:e.message||"Forbidden"},403):(console.error("[Svelar] Controller error:",e),this.json({message:process.env.NODE_ENV==="production"?"Internal server error":e.message},500))}async callMethod(e,t){let s=this[e];if(typeof s!="function")throw new Error(`Method "${e}" not found on controller "${this.constructor.name}".`);let r=await s.call(this,t);return r instanceof Response?r:this.json(r)}};U=class extends Error{errors;constructor(e){super("Validation failed"),this.name="ValidationError",this.errors={};for(let t of e.issues){let s=t.path.join(".");this.errors[s]||(this.errors[s]=[]),this.errors[s].push(t.message)}}},De=class extends Error{constructor(e="Not found"){super(e),this.name="NotFoundError"}},Ne=class extends Error{constructor(e="Unauthorized"){super(e),this.name="UnauthorizedError"}},_e=class extends Error{constructor(e="Forbidden"){super(e),this.name="ForbiddenError"}}});import{randomBytes as Ja,createHmac as wi,timingSafeEqual as Va}from"crypto";import{promises as F}from"fs";import{join as Me}from"path";var ce,Oe,z,jt,It,Lt,de,Ds=v(()=>{"use strict";oe();ce=class l{constructor(e,t){this.id=e;t&&(this.data={...t},this.data._flash&&(this.previousFlashData=this.data._flash,delete this.data._flash))}data={};dirty=!1;flashData={};previousFlashData={};get(e,t){return e in this.flashData?this.flashData[e]:e in this.previousFlashData?this.previousFlashData[e]:e in this.data?this.data[e]:t}set(e,t){this.data[e]=t,this.dirty=!0}has(e){return e in this.data||e in this.flashData||e in this.previousFlashData}forget(e){delete this.data[e],this.dirty=!0}flush(){this.data={},this.dirty=!0}flash(e,t){this.flashData[e]=t,this.dirty=!0}all(){return{...this.data,...this.previousFlashData,...this.flashData}}isDirty(){return this.dirty||Object.keys(this.flashData).length>0}toPersist(){let e={...this.data};return Object.keys(this.flashData).length>0&&(e._flash=this.flashData),e}regenerateId(){let e=this.id,t=l.generateId();this.id=t,this.dirty=!0;let s=Oe.get(e);return s instanceof z&&s.markOldSessionId(e),Oe.delete(e),t}static generateId(){return Ja(32).toString("hex")}},Oe=new Map,z=class{sessions=new Map;oldSessionIds=new Set;async read(e){if(this.oldSessionIds.has(e))return null;let t=this.sessions.get(e);return t?Date.now()>t.expiresAt?(this.sessions.delete(e),null):t.data:null}async write(e,t,s){this.sessions.set(e,{data:t,expiresAt:Date.now()+s*1e3}),Oe.set(e,this)}async destroy(e){this.sessions.delete(e),Oe.delete(e)}async gc(e){let t=Date.now();for(let[s,r]of this.sessions)t>r.expiresAt&&(this.sessions.delete(s),Oe.delete(s))}markOldSessionId(e){this.oldSessionIds.add(e),this.sessions.delete(e)}},jt=class{constructor(e="sessions",t){this.tableName=e;this.connectionName=t}tableEnsured=!1;async ensureTable(){if(!this.tableEnsured)try{let{Connection:e}=await Promise.resolve().then(()=>(x(),C));switch(e.getDriver(this.connectionName)){case"sqlite":await e.raw(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
41
|
+
ORDER BY created_at ASC LIMIT 1`,[e,s]);if(!r||r.length===0)return null;let i=r[0];await t.raw(`UPDATE ${this.table} SET reserved_at = ?, attempts = attempts + 1 WHERE id = ?`,[s,i.id]);let n=JSON.parse(i.payload),a=this.registry.resolve(n.jobClass,n.payload);return{id:i.id,jobClass:n.jobClass,payload:n.payload,queue:i.queue,attempts:i.attempts+1,maxAttempts:i.max_attempts,availableAt:i.available_at*1e3,createdAt:i.created_at*1e3,job:a}}async size(e="default"){return(await(await this.getConnection()).raw(`SELECT COUNT(*) as count FROM ${this.table} WHERE queue = ? AND reserved_at IS NULL`,[e]))?.[0]?.count??0}async clear(e){let t=await this.getConnection();e?await t.raw(`DELETE FROM ${this.table} WHERE queue = ?`,[e]):await t.raw(`DELETE FROM ${this.table}`,[])}async delete(e){await(await this.getConnection()).raw(`DELETE FROM ${this.table} WHERE id = ?`,[e])}async release(e,t=0){let s=await this.getConnection(),r=Math.floor(Date.now()/1e3)+t;await s.raw(`UPDATE ${this.table} SET reserved_at = NULL, available_at = ? WHERE id = ?`,[r,e])}},xt=class{queues=new Map;config;registry;_bullmq=null;constructor(e,t){this.config=e,this.registry=t}async getBullMQ(){if(this._bullmq)return this._bullmq;try{return this._bullmq=await Function('return import("bullmq")')(),this._bullmq}catch{throw new Error("bullmq is required for the Redis queue driver. Install it with: npm install bullmq")}}getRedisConnection(){if(this.config.url){let e=new URL(this.config.url);return{host:e.hostname||"localhost",port:parseInt(e.port)||6379,password:e.password||this.config.password||void 0,db:parseInt(e.pathname?.slice(1)||"0")||this.config.db||0}}return{host:this.config.host??"localhost",port:this.config.port??6379,password:this.config.password,db:this.config.db??0}}async getQueue(e){if(this.queues.has(e))return this.queues.get(e);let t=await this.getBullMQ(),s=this.getRedisConnection(),r=this.config.prefix??"svelar",i=new t.Queue(e,{connection:s,prefix:r,defaultJobOptions:{removeOnComplete:this.config.defaultJobOptions?.removeOnComplete??100,removeOnFail:this.config.defaultJobOptions?.removeOnFail??500}});return this.queues.set(e,i),i}async push(e){let t=await this.getQueue(e.queue),s=Math.max(0,e.availableAt-Date.now());await t.add(e.jobClass,{jobClass:e.jobClass,payload:e.payload},{jobId:e.id,delay:s>0?s:void 0,attempts:e.maxAttempts,backoff:{type:"fixed",delay:(e.job.retryDelay??60)*1e3}})}async pop(e){return null}async size(e="default"){let s=await(await this.getQueue(e)).getJobCounts("waiting","delayed","active");return s.waiting+s.delayed+s.active}async clear(e){if(e)await(await this.getQueue(e)).obliterate({force:!0});else for(let t of this.queues.values())await t.obliterate({force:!0})}async createWorker(e,t,s,r){let i=await this.getBullMQ(),n=this.getRedisConnection(),a=this.config.prefix??"svelar",o=new i.Worker(e,async c=>{let u=c.data,d=t.resolve(u.jobClass,u.payload);d.attempts=c.attemptsMade+1,await d.handle()},{connection:n,prefix:a,concurrency:r?.concurrency??1});return o.on("failed",async(c,u)=>{let d=c?.data;if(d)try{let p=t.resolve(d.jobClass,d.payload);c.attemptsMade>=(c.opts?.attempts??3)&&(p.failed(u),await s.store({id:c.id,jobClass:d.jobClass,payload:d.payload,queue:e,attempts:c.attemptsMade,maxAttempts:c.opts?.attempts??3,availableAt:Date.now(),createdAt:c.timestamp??Date.now(),job:p},u))}catch{console.error("[Queue] Failed to resolve job for failure handler:",u.message)}}),o}},ws=class{table="svelar_failed_jobs";async getConnection(){let{Connection:e}=await Promise.resolve().then(()=>(x(),P));return e}async store(e,t){try{await(await this.getConnection()).raw(`INSERT INTO ${this.table} (id, queue, job_class, payload, exception, failed_at)
|
|
42
|
+
VALUES (?, ?, ?, ?, ?, ?)`,[crypto.randomUUID(),e.queue,e.jobClass,e.payload,t.stack??t.message,Math.floor(Date.now()/1e3)])}catch{console.error("[Queue] Could not persist failed job (run migration to create svelar_failed_jobs table)")}}async all(){return(await(await this.getConnection()).raw(`SELECT * FROM ${this.table} ORDER BY failed_at DESC`,[])??[]).map(s=>({id:s.id,queue:s.queue,jobClass:s.job_class,payload:s.payload,exception:s.exception,failedAt:s.failed_at}))}async find(e){let s=await(await this.getConnection()).raw(`SELECT * FROM ${this.table} WHERE id = ? LIMIT 1`,[e]);if(!s||s.length===0)return null;let r=s[0];return{id:r.id,queue:r.queue,jobClass:r.job_class,payload:r.payload,exception:r.exception,failedAt:r.failed_at}}async forget(e){return await(await this.getConnection()).raw(`DELETE FROM ${this.table} WHERE id = ?`,[e]),!0}async flush(){let e=await this.getConnection(),s=(await e.raw(`SELECT COUNT(*) as count FROM ${this.table}`,[]))?.[0]?.count??0;return await e.raw(`DELETE FROM ${this.table}`,[]),s}},xs=class{jobs=new Map;register(e){this.jobs.set(e.name,e)}registerAll(e){for(let t of e)this.register(t)}resolve(e,t){let s=this.jobs.get(e);if(!s)throw new Error(`Job class "${e}" is not registered. Call Queue.register(${e}) in your app bootstrap. Registered jobs: [${[...this.jobs.keys()].join(", ")}]`);let r=Object.create(s.prototype);r.attempts=0,r.maxAttempts=3,r.retryDelay=60,r.queue="default";try{let i=JSON.parse(t);r.restore(i)}catch{}return r}has(e){return this.jobs.has(e)}},Cs=class{config={default:"sync",connections:{sync:{driver:"sync"}}};drivers=new Map;processing=!1;jobRegistry=new xs;failedStore=new ws;_activeWorker=null;configure(e){this.config=e,this.drivers.clear()}register(e){this.jobRegistry.register(e)}registerAll(e){this.jobRegistry.registerAll(e)}async dispatch(e,t){let s=this.config.default,r=this.config.connections[s],i=this.resolveDriver(s);t?.queue&&(e.queue=t.queue),t?.maxAttempts!==void 0&&(e.maxAttempts=t.maxAttempts);let n={id:crypto.randomUUID(),jobClass:e.constructor.name,payload:e.serialize(),queue:e.queue??r?.queue??"default",attempts:0,maxAttempts:e.maxAttempts,availableAt:Date.now()+(t?.delay??0)*1e3,createdAt:Date.now(),job:e};await i.push(n)}async dispatchSync(e){let t=new wt,s={id:crypto.randomUUID(),jobClass:e.constructor.name,payload:e.serialize(),queue:e.queue,attempts:0,maxAttempts:e.maxAttempts,availableAt:Date.now(),createdAt:Date.now(),job:e};await t.push(s)}async chain(e,t){if(e.length===0)return;let s=new Ps(e);t?.queue&&(s.queue=t.queue),t?.maxAttempts!==void 0&&(s.maxAttempts=t.maxAttempts),await this.dispatch(s,t)}async work(e){let t=this.config.default,s=this.resolveDriver(t),r=e?.queue??"default";if(s instanceof xt){let o=await s.createWorker(r,this.jobRegistry,this.failedStore,{concurrency:e?.concurrency??1});return this.processing=!0,this._activeWorker=o,await new Promise(c=>{let u=()=>{this.processing?setTimeout(u,500):o.close().then(c).catch(c)};u()}),0}let i=e?.maxJobs??1/0,n=(e?.sleep??1)*1e3,a=0;for(this.processing=!0;this.processing&&a<i;){let o=await s.pop(r);if(!o){if(i===1/0){await new Promise(c=>setTimeout(c,n));continue}break}o.attempts++,o.job.attempts=o.attempts;try{await o.job.handle(),a++,s instanceof J&&await s.delete(o.id)}catch(c){if(o.attempts<o.maxAttempts){o.job.retrying(o.attempts);let u=o.job.retryDelay??60;s instanceof J?await s.release(o.id,u):(o.availableAt=Date.now()+u*1e3,await s.push(o))}else o.job.failed(c),await this.failedStore.store(o,c),s instanceof J&&await s.delete(o.id)}}return a}async stop(){this.processing=!1,this._activeWorker&&(await this._activeWorker.close(),this._activeWorker=null)}async size(e){return this.resolveDriver(this.config.default).size(e)}async clear(e){return this.resolveDriver(this.config.default).clear(e)}async failed(){return this.failedStore.all()}async retry(e){let t=await this.failedStore.find(e);if(!t)return!1;let s=this.jobRegistry.resolve(t.jobClass,t.payload);return s.queue=t.queue,await this.dispatch(s,{queue:t.queue}),await this.failedStore.forget(e),!0}async retryAll(){let e=await this.failedStore.all(),t=0;for(let s of e)try{let r=this.jobRegistry.resolve(s.jobClass,s.payload);r.queue=s.queue,await this.dispatch(r,{queue:s.queue}),await this.failedStore.forget(s.id),t++}catch{}return t}async forgetFailed(e){return this.failedStore.forget(e)}async flushFailed(){return this.failedStore.flush()}resolveDriver(e){if(this.drivers.has(e))return this.drivers.get(e);let t=this.config.connections[e];if(!t)throw new Error(`Queue connection "${e}" is not defined.`);let s;switch(t.driver){case"sync":s=new wt;break;case"memory":s=new bs;break;case"database":s=new J(t.table??"svelar_jobs",this.jobRegistry);break;case"redis":s=new xt(t,this.jobRegistry);break;default:throw new Error(`Unknown queue driver: ${t.driver}`)}return this.drivers.set(e,s),s}},Ps=class extends V{remainingJobs;constructor(e){super(),this.remainingJobs=[...e],this.maxAttempts=1}async handle(){for(let e of this.remainingJobs){let t=null,s=!1;for(let r=1;r<=e.maxAttempts;r++){e.attempts=r;try{await e.handle(),s=!0;break}catch(i){t=i,r<e.maxAttempts&&(e.retrying(r),e.retryDelay>0&&await new Promise(n=>setTimeout(n,e.retryDelay*1e3)))}}if(!s&&t)throw e.failed(t),new Error(`Chain stopped: ${e.constructor.name} failed after ${e.maxAttempts} attempt(s). Remaining jobs: [${this.remainingJobs.slice(this.remainingJobs.indexOf(e)+1).map(r=>r.constructor.name).join(", ")}]`)}}serialize(){return JSON.stringify({jobs:this.remainingJobs.map(e=>({jobClass:e.constructor.name,payload:e.serialize()}))})}},Ss=w("svelar.queue",()=>new Cs)});var D,Rs=y(()=>{"use strict";x();D=class l{tableName;selectColumns=["*"];whereClauses=[];joinClauses=[];orderClauses=[];groupByColumns=[];havingClauses=[];limitValue=null;offsetValue=null;eagerLoads=[];isDistinct=!1;connectionName;cteClauses=[];unionClauses=[];modelClass;constructor(e,t,s){this.tableName=e,this.modelClass=t,this.connectionName=s}select(...e){return this.selectColumns=e.length>0?e:["*"],this}addSelect(...e){return this.selectColumns[0]==="*"?this.selectColumns=e:this.selectColumns.push(...e),this}distinct(){return this.isDistinct=!0,this}from(e){return this.tableName=e,this}where(e,t,s){return s===void 0?this.whereClauses.push({type:"basic",column:e,operator:"=",value:t,boolean:"AND"}):this.whereClauses.push({type:"basic",column:e,operator:t,value:s,boolean:"AND"}),this}orWhere(e,t,s){return s===void 0?this.whereClauses.push({type:"basic",column:e,operator:"=",value:t,boolean:"OR"}):this.whereClauses.push({type:"basic",column:e,operator:t,value:s,boolean:"OR"}),this}whereIn(e,t){return this.whereClauses.push({type:"in",column:e,values:t,boolean:"AND"}),this}whereNotIn(e,t){return this.whereClauses.push({type:"notIn",column:e,values:t,boolean:"AND"}),this}whereNull(e){return this.whereClauses.push({type:"null",column:e,boolean:"AND"}),this}whereNotNull(e){return this.whereClauses.push({type:"notNull",column:e,boolean:"AND"}),this}whereBetween(e,t){return this.whereClauses.push({type:"between",column:e,values:t,boolean:"AND"}),this}whereRaw(e,t=[]){return this.whereClauses.push({type:"raw",raw:e,values:t,boolean:"AND"}),this}whereNested(e,t="AND"){let s=new l(this.tableName,this.modelClass,this.connectionName);if(e(s),s.whereClauses.length>0){let{whereSQL:r,whereBindings:i}=s.buildWhere(),n=r.replace(/^WHERE /,"");this.whereClauses.push({type:"raw",raw:`(${n})`,values:i,boolean:t})}return this}orWhereNested(e){return this.whereNested(e,"OR")}whereExists(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.whereClauses.push({type:"exists",subSQL:s,subBindings:r,boolean:"AND"}),this}whereNotExists(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.whereClauses.push({type:"notExists",subSQL:s,subBindings:r,boolean:"AND"}),this}whereSub(e,t,s){let r=new l("__placeholder__",void 0,this.connectionName);s(r);let{sql:i,bindings:n}=r.toSQL();return this.whereClauses.push({type:"sub",column:e,operator:t,subSQL:i,subBindings:n,boolean:"AND"}),this}orWhereRaw(e,t=[]){return this.whereClauses.push({type:"raw",raw:e,values:t,boolean:"OR"}),this}orWhereIn(e,t){return this.whereClauses.push({type:"in",column:e,values:t,boolean:"OR"}),this}orWhereNull(e){return this.whereClauses.push({type:"null",column:e,boolean:"OR"}),this}orWhereNotNull(e){return this.whereClauses.push({type:"notNull",column:e,boolean:"OR"}),this}withCTE(e,t,s=!1){let r=new l("__placeholder__",void 0,this.connectionName);t(r);let{sql:i,bindings:n}=r.toSQL();return this.cteClauses.push({name:e,sql:i,bindings:n,recursive:s}),this}withRecursiveCTE(e,t){return this.withCTE(e,t,!0)}withRawCTE(e,t,s=[],r=!1){return this.cteClauses.push({name:e,sql:t,bindings:s,recursive:r}),this}union(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.unionClauses.push({sql:s,bindings:r,all:!1}),this}unionAll(e){let t=new l("__placeholder__",void 0,this.connectionName);e(t);let{sql:s,bindings:r}=t.toSQL();return this.unionClauses.push({sql:s,bindings:r,all:!0}),this}join(e,t,s,r){return this.joinClauses.push({type:"INNER",table:e,first:t,operator:s,second:r}),this}leftJoin(e,t,s,r){return this.joinClauses.push({type:"LEFT",table:e,first:t,operator:s,second:r}),this}rightJoin(e,t,s,r){return this.joinClauses.push({type:"RIGHT",table:e,first:t,operator:s,second:r}),this}crossJoin(e){return this.joinClauses.push({type:"CROSS",table:e,first:"",operator:"",second:""}),this}orderBy(e,t="asc"){return this.orderClauses.push({column:e,direction:t}),this}latest(e="created_at"){return this.orderBy(e,"desc")}oldest(e="created_at"){return this.orderBy(e,"asc")}groupBy(...e){return this.groupByColumns.push(...e),this}having(e,t,s){return this.havingClauses.push({type:"basic",column:e,operator:t,value:s,boolean:"AND"}),this}limit(e){return this.limitValue=e,this}offset(e){return this.offsetValue=e,this}take(e){return this.limit(e)}skip(e){return this.offset(e)}with(...e){return this.eagerLoads.push(...e),this}async get(){let{sql:e,bindings:t}=this.toSQL(),s=await g.raw(e,t,this.connectionName),r=this.hydrateMany(s);return this.eagerLoads.length>0&&this.modelClass&&await this.loadRelations(r),r}async first(){return this.limitValue=1,(await this.get())[0]??null}async firstOrFail(){let e=await this.first();if(!e)throw new Error(`No results found for query on "${this.tableName}".`);return e}async find(e,t="id"){return this.where(t,e).first()}async findOrFail(e,t="id"){return this.where(t,e).firstOrFail()}async count(e="*"){let{sql:t,bindings:s}=this.buildAggregate(`COUNT(${e})`),r=await g.raw(t,s,this.connectionName);return Number(r[0]?.aggregate??0)}async sum(e){let{sql:t,bindings:s}=this.buildAggregate(`SUM(${e})`),r=await g.raw(t,s,this.connectionName);return Number(r[0]?.aggregate??0)}async avg(e){let{sql:t,bindings:s}=this.buildAggregate(`AVG(${e})`),r=await g.raw(t,s,this.connectionName);return Number(r[0]?.aggregate??0)}async max(e){let{sql:t,bindings:s}=this.buildAggregate(`MAX(${e})`);return(await g.raw(t,s,this.connectionName))[0]?.aggregate??null}async min(e){let{sql:t,bindings:s}=this.buildAggregate(`MIN(${e})`);return(await g.raw(t,s,this.connectionName))[0]?.aggregate??null}async exists(){return await this.count()>0}async doesntExist(){return!await this.exists()}async pluck(e){return this.selectColumns=[e],(await g.raw(this.toSQL().sql,this.toSQL().bindings,this.connectionName)).map(s=>s[e])}async value(e){return this.selectColumns=[e],this.limitValue=1,(await g.raw(this.toSQL().sql,this.toSQL().bindings,this.connectionName))[0]?.[e]??null}async chunk(e,t){let s=1,r=!0;for(;r;){let i=this.clone();i.limitValue=e,i.offsetValue=(s-1)*e;let n=await i.get();if(n.length===0||await t(n,s)===!1||n.length<e)break;s++}}when(e,t){return e&&t(this),this}selectRaw(e){return this.selectColumns[0]==="*"?this.selectColumns=[e]:this.selectColumns.push(e),this}async upsert(e,t,s){let r=g.getDriver(this.connectionName),i=Object.keys(e),n=Object.values(e),a=n.map(()=>"?").join(", "),o=s??i.filter(u=>!t.includes(u)),c;if(r==="postgres"){let u=o.map(d=>`${d} = EXCLUDED.${d}`).join(", ");c=`INSERT INTO ${this.tableName} (${i.join(", ")}) VALUES (${a}) ON CONFLICT (${t.join(", ")}) DO UPDATE SET ${u}`}else if(r==="mysql"){let u=o.map(d=>`${d} = VALUES(${d})`).join(", ");c=`INSERT INTO ${this.tableName} (${i.join(", ")}) VALUES (${a}) ON DUPLICATE KEY UPDATE ${u}`}else{let u=o.map(d=>`${d} = excluded.${d}`).join(", ");c=`INSERT INTO ${this.tableName} (${i.join(", ")}) VALUES (${a}) ON CONFLICT (${t.join(", ")}) DO UPDATE SET ${u}`}return g.raw(c,n,this.connectionName)}async insertMany(e){if(e.length===0)return;let t=Object.keys(e[0]),s=[],r=[];for(let n of e){let a=t.map(o=>n[o]);s.push(...a),r.push(`(${a.map(()=>"?").join(", ")})`)}let i=`INSERT INTO ${this.tableName} (${t.join(", ")}) VALUES ${r.join(", ")}`;return g.raw(i,s,this.connectionName)}async firstOrCreate(e,t={}){for(let[n,a]of Object.entries(e))this.where(n,a);let s=await this.first();if(s)return s;let r={...e,...t},i=await new l(this.tableName,this.modelClass,this.connectionName).insertGetId(r);return new l(this.tableName,this.modelClass,this.connectionName).findOrFail(i)}async updateOrCreate(e,t){let s=new l(this.tableName,this.modelClass,this.connectionName);for(let[a,o]of Object.entries(e))s.where(a,o);let r=await s.first();if(r)return await new l(this.tableName,this.modelClass,this.connectionName).where(this.modelClass?.primaryKey??"id",r[this.modelClass?.primaryKey??"id"]).update(t),new l(this.tableName,this.modelClass,this.connectionName).findOrFail(r[this.modelClass?.primaryKey??"id"]);let i={...e,...t},n=await new l(this.tableName,this.modelClass,this.connectionName).insertGetId(i);return new l(this.tableName,this.modelClass,this.connectionName).findOrFail(n)}whereColumn(e,t,s){return s===void 0?this.whereClauses.push({type:"raw",raw:`${e} = ${t}`,values:[],boolean:"AND"}):this.whereClauses.push({type:"raw",raw:`${e} ${t} ${s}`,values:[],boolean:"AND"}),this}havingRaw(e,t=[]){return this.havingClauses.push({type:"raw",raw:e,values:t,boolean:"AND"}),this}orderByRaw(e){return this.orderClauses.push({column:e,direction:"asc"}),this.orderClauses[this.orderClauses.length-1].__raw=!0,this}selectSub(e,t){let s=new l("__placeholder__",void 0,this.connectionName);e(s);let{sql:r,bindings:i}=s.toSQL(),n=`(${r}) as ${t}`;return this.selectColumns[0]==="*"?this.selectColumns=[n]:this.selectColumns.push(n),this._selectBindings||(this._selectBindings=[]),this._selectBindings.push(...i),this}_selectBindings;async truncate(){g.getDriver(this.connectionName)==="sqlite"?(await g.raw(`DELETE FROM ${this.tableName}`,[],this.connectionName),await g.raw("DELETE FROM sqlite_sequence WHERE name = ?",[this.tableName],this.connectionName)):await g.raw(`TRUNCATE TABLE ${this.tableName}`,[],this.connectionName)}async paginate(e=1,t=15){let s=await this.clone().count(),r=Math.ceil(s/t);return this.limitValue=t,this.offsetValue=(e-1)*t,{data:await this.get(),total:s,page:e,perPage:t,lastPage:r,hasMore:e<r}}async insert(e){let t=Object.keys(e),s=Object.values(e),r=s.map(()=>"?").join(", "),i=`INSERT INTO ${this.tableName} (${t.join(", ")}) VALUES (${r})`;return g.raw(i,s,this.connectionName)}async insertGetId(e,t="id"){let s=g.getDriver(this.connectionName),r=Object.keys(e),i=Object.values(e),n=i.map(()=>"?").join(", "),a=`INSERT INTO ${this.tableName} (${r.join(", ")}) VALUES (${n})`;return s==="postgres"?(a+=` RETURNING ${t}`,(await g.raw(a,i,this.connectionName))[0]?.[t]):(await g.raw(a,i,this.connectionName),s==="sqlite"?(await g.raw("SELECT last_insert_rowid() as id",[],this.connectionName))[0]?.id:s==="mysql"?(await g.raw("SELECT LAST_INSERT_ID() as id",[],this.connectionName))[0]?.id:0)}async update(e){let t=Object.keys(e),s=Object.values(e),r=t.map(o=>`${o} = ?`).join(", "),{whereSQL:i,whereBindings:n}=this.buildWhere(),a=`UPDATE ${this.tableName} SET ${r}${i}`;return await g.raw(a,[...s,...n],this.connectionName),1}async delete(){let{whereSQL:e,whereBindings:t}=this.buildWhere(),s=`DELETE FROM ${this.tableName}${e}`;return await g.raw(s,t,this.connectionName),1}async increment(e,t=1){let{whereSQL:s,whereBindings:r}=this.buildWhere(),i=`UPDATE ${this.tableName} SET ${e} = ${e} + ?${s}`;await g.raw(i,[t,...r],this.connectionName)}async decrement(e,t=1){return this.increment(e,-t)}toSQL(){let e=[],t=[];if(this.cteClauses.length>0){let a=this.cteClauses.some(c=>c.recursive)?"WITH RECURSIVE":"WITH",o=this.cteClauses.map(c=>(t.push(...c.bindings),`${c.name} AS (${c.sql})`));e.push(`${a} ${o.join(", ")}`)}this._selectBindings?.length&&t.push(...this._selectBindings);let s=this.isDistinct?"DISTINCT ":"";e.push(`SELECT ${s}${this.selectColumns.join(", ")}`),e.push(`FROM ${this.tableName}`);for(let n of this.joinClauses)n.type==="CROSS"?e.push(`CROSS JOIN ${n.table}`):e.push(`${n.type} JOIN ${n.table} ON ${n.first} ${n.operator} ${n.second}`);let{whereSQL:r,whereBindings:i}=this.buildWhere();if(r&&(e.push(r.trim()),t.push(...i)),this.groupByColumns.length>0&&e.push(`GROUP BY ${this.groupByColumns.join(", ")}`),this.havingClauses.length>0){let n=[];for(let a of this.havingClauses)a.type==="raw"?(n.push(a.raw),a.values&&t.push(...a.values)):(n.push(`${a.column} ${a.operator} ?`),t.push(a.value));e.push(`HAVING ${n.join(" AND ")}`)}if(this.orderClauses.length>0){let n=this.orderClauses.map(a=>a.__raw?a.column:`${a.column} ${a.direction.toUpperCase()}`);e.push(`ORDER BY ${n.join(", ")}`)}if(this.limitValue!==null&&e.push(`LIMIT ${this.limitValue}`),this.offsetValue!==null&&e.push(`OFFSET ${this.offsetValue}`),this.unionClauses.length>0)for(let n of this.unionClauses)e.push(n.all?"UNION ALL":"UNION"),e.push(n.sql),t.push(...n.bindings);return{sql:e.join(" "),bindings:t}}clone(){let e=new l(this.tableName,this.modelClass,this.connectionName);return e.selectColumns=[...this.selectColumns],e.whereClauses=[...this.whereClauses],e.joinClauses=[...this.joinClauses],e.orderClauses=[...this.orderClauses],e.groupByColumns=[...this.groupByColumns],e.havingClauses=[...this.havingClauses],e.limitValue=this.limitValue,e.offsetValue=this.offsetValue,e.eagerLoads=[...this.eagerLoads],e.isDistinct=this.isDistinct,e.cteClauses=[...this.cteClauses],e.unionClauses=[...this.unionClauses],e}buildWhere(){if(this.whereClauses.length===0)return{whereSQL:"",whereBindings:[]};let e=[],t=[];for(let s=0;s<this.whereClauses.length;s++){let r=this.whereClauses[s],i=s===0?"WHERE":r.boolean;switch(r.type){case"basic":if((r.operator==="IS"||r.operator==="IS NOT")&&r.value===null){let o=(r.operator==="IS","null");e.push(`${i} ${r.column} ${r.operator} ${o}`)}else e.push(`${i} ${r.column} ${r.operator} ?`),t.push(r.value);break;case"in":let n=r.values.map(()=>"?").join(", ");e.push(`${i} ${r.column} IN (${n})`),t.push(...r.values);break;case"notIn":let a=r.values.map(()=>"?").join(", ");e.push(`${i} ${r.column} NOT IN (${a})`),t.push(...r.values);break;case"null":e.push(`${i} ${r.column} IS NULL`);break;case"notNull":e.push(`${i} ${r.column} IS NOT NULL`);break;case"between":e.push(`${i} ${r.column} BETWEEN ? AND ?`),t.push(r.values[0],r.values[1]);break;case"raw":e.push(`${i} ${r.raw}`),r.values&&t.push(...r.values);break;case"exists":e.push(`${i} EXISTS (${r.subSQL})`),r.subBindings&&t.push(...r.subBindings);break;case"notExists":e.push(`${i} NOT EXISTS (${r.subSQL})`),r.subBindings&&t.push(...r.subBindings);break;case"sub":e.push(`${i} ${r.column} ${r.operator} (${r.subSQL})`),r.subBindings&&t.push(...r.subBindings);break}}return{whereSQL:e.join(" "),whereBindings:t}}buildAggregate(e){let t=this.selectColumns;this.selectColumns=[`${e} as aggregate`];let s=this.toSQL();return this.selectColumns=t,s}hydrateMany(e){return this.modelClass?e.map(t=>this.modelClass.hydrate(t)):e}async loadRelations(e){if(!(!this.modelClass||e.length===0))for(let t of this.eagerLoads){let r=new this.modelClass()[t]?.();r&&typeof r.eagerLoad=="function"&&await r.eagerLoad(e,t)}}}});var G,Y,X,Z,ee,Ts=y(()=>{"use strict";x();G=class{parentModel;relatedModel;constructor(e,t){this.parentModel=e,this.relatedModel=t}query(){return this.relatedModel.query()}},Y=class extends G{constructor(t,s,r,i="id"){super(t,s);this.foreignKey=r;this.localKey=i}async load(t){let s=t.getAttribute(this.localKey);return this.relatedModel.query().where(this.foreignKey,s).first()}async eagerLoad(t,s){let r=t.map(a=>a.getAttribute(this.localKey)),i=await this.relatedModel.query().whereIn(this.foreignKey,r).get(),n=new Map;for(let a of i)n.set(a.getAttribute(this.foreignKey),a);for(let a of t){let o=a.getAttribute(this.localKey);a.setRelation(s,n.get(o)??null)}}async create(t){let s=this.parentModel.getAttribute(this.localKey);return this.relatedModel.create({...t,[this.foreignKey]:s})}},X=class extends G{constructor(t,s,r,i="id"){super(t,s);this.foreignKey=r;this.localKey=i}async load(t){let s=t.getAttribute(this.localKey);return this.relatedModel.query().where(this.foreignKey,s).get()}async eagerLoad(t,s){let r=t.map(a=>a.getAttribute(this.localKey)),i=await this.relatedModel.query().whereIn(this.foreignKey,r).get(),n=new Map;for(let a of i){let o=a.getAttribute(this.foreignKey);n.has(o)||n.set(o,[]),n.get(o).push(a)}for(let a of t){let o=a.getAttribute(this.localKey);a.setRelation(s,n.get(o)??[])}}async create(t){let s=this.parentModel.getAttribute(this.localKey);return this.relatedModel.create({...t,[this.foreignKey]:s})}async createMany(t){let s=[];for(let r of t)s.push(await this.create(r));return s}},Z=class extends G{constructor(t,s,r,i="id"){super(t,s);this.foreignKey=r;this.ownerKey=i}async load(t){let s=t.getAttribute(this.foreignKey);return s==null?null:this.relatedModel.query().where(this.ownerKey,s).first()}async eagerLoad(t,s){let r=t.map(a=>a.getAttribute(this.foreignKey)).filter(a=>a!=null);if(r.length===0){for(let a of t)a.setRelation(s,null);return}let i=await this.relatedModel.query().whereIn(this.ownerKey,r).get(),n=new Map;for(let a of i)n.set(a.getAttribute(this.ownerKey),a);for(let a of t){let o=a.getAttribute(this.foreignKey);a.setRelation(s,n.get(o)??null)}}associate(t){return this.parentModel.setAttribute(this.foreignKey,t.getAttribute(this.ownerKey)),this.parentModel}dissociate(){return this.parentModel.setAttribute(this.foreignKey,null),this.parentModel}},ee=class extends G{constructor(t,s,r,i,n,a="id",o="id"){super(t,s);this.pivotTable=r;this.foreignPivotKey=i;this.relatedPivotKey=n;this.parentKey=a;this.relatedKey=o}async load(t){let s=t.getAttribute(this.parentKey),r=this.relatedModel.tableName,n=(await g.raw(`SELECT ${this.relatedPivotKey} FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ?`,[s])).map(a=>a[this.relatedPivotKey]);return n.length===0?[]:this.relatedModel.query().whereIn(this.relatedKey,n).get()}async eagerLoad(t,s){let r=t.map(d=>d.getAttribute(this.parentKey));if(r.length===0){for(let d of t)d.setRelation(s,[]);return}let i=r.map(()=>"?").join(", "),n=await g.raw(`SELECT * FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} IN (${i})`,r),a=[...new Set(n.map(d=>d[this.relatedPivotKey]))],o=a.length>0?await this.relatedModel.query().whereIn(this.relatedKey,a).get():[],c=new Map;for(let d of o)c.set(d.getAttribute(this.relatedKey),d);let u=new Map;for(let d of n){let p=d[this.foreignPivotKey],f=d[this.relatedPivotKey],b=c.get(f);b&&(u.has(p)||u.set(p,[]),u.get(p).push(b))}for(let d of t){let p=d.getAttribute(this.parentKey);d.setRelation(s,u.get(p)??[])}}async attach(t,s){let r=this.parentModel.getAttribute(this.parentKey),i={[this.foreignPivotKey]:r,[this.relatedPivotKey]:t,...s},n=Object.keys(i),a=Object.values(i),o=a.map(()=>"?").join(", ");await g.raw(`INSERT INTO ${this.pivotTable} (${n.join(", ")}) VALUES (${o})`,a)}async detach(t){let s=this.parentModel.getAttribute(this.parentKey);t?await g.raw(`DELETE FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ? AND ${this.relatedPivotKey} = ?`,[s,t]):await g.raw(`DELETE FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ?`,[s])}async sync(t){await this.detach();for(let s of t)await this.attach(s)}async toggle(t){let s=this.parentModel.getAttribute(this.parentKey);for(let r of t)(await g.raw(`SELECT 1 FROM ${this.pivotTable} WHERE ${this.foreignPivotKey} = ? AND ${this.relatedPivotKey} = ? LIMIT 1`,[s,r])).length>0?await this.detach(r):await this.attach(r)}}});var di=y(()=>{"use strict"});var ke,Es=y(()=>{"use strict";ke=class{app;constructor(e){this.app=e}boot(){}}});var ui=y(()=>{"use strict";Es();Tt()});var $e,te,Tt=y(()=>{"use strict";R();di();ui();$e=class{listeners=new Map;wildcardListeners=[];onceListeners=new Map;listen(e,t){let s=typeof e=="string"?e:e.name;return this.listeners.has(s)||this.listeners.set(s,[]),this.listeners.get(s).push(t),()=>{let r=this.listeners.get(s);if(r){let i=r.indexOf(t);i>=0&&r.splice(i,1)}}}once(e,t){let s=typeof e=="string"?e:e.name;return this.onceListeners.has(s)||this.onceListeners.set(s,[]),this.onceListeners.get(s).push(t),()=>{let r=this.onceListeners.get(s);if(r){let i=r.indexOf(t);i>=0&&r.splice(i,1)}}}onAny(e){return this.wildcardListeners.push(e),()=>{let t=this.wildcardListeners.indexOf(e);t>=0&&this.wildcardListeners.splice(t,1)}}async dispatch(e){let t=e.constructor.name,s=this.listeners.get(t)??[];for(let i of s)await i(e);let r=this.onceListeners.get(t)??[];for(let i of r)await i(e);this.onceListeners.delete(t);for(let i of this.wildcardListeners)await i(t,e)}async emit(e,t){let s=this.listeners.get(e)??[];for(let i of s)await i(t);let r=this.onceListeners.get(e)??[];for(let i of r)await i(t);this.onceListeners.delete(e);for(let i of this.wildcardListeners)await i(e,t)}subscribe(e){e.subscribe(this)}forget(e){let t=typeof e=="string"?e:e.name;this.listeners.delete(t),this.onceListeners.delete(t)}flush(){this.listeners.clear(),this.onceListeners.clear(),this.wildcardListeners=[]}hasListeners(e){let t=typeof e=="string"?e:e.name;return(this.listeners.get(t)?.length??0)>0||(this.onceListeners.get(t)?.length??0)>0||this.wildcardListeners.length>0}listenerCount(e){let t=typeof e=="string"?e:e.name;return(this.listeners.get(t)?.length??0)+(this.onceListeners.get(t)?.length??0)}},te=w("svelar.event",()=>new $e)});var Et,mi=y(()=>{"use strict";Rs();Ts();Tt();Et=class l{static table;static primaryKey="id";static incrementing=!0;static timestamps=!0;static createdAt="created_at";static updatedAt="updated_at";static casts={};static fillable=[];static hidden=[];static connection=void 0;static hooks=new Map;static observers=new Map;static events=[];attributes={};originalAttributes={};relations={};exists=!1;constructor(e){return e&&this.fill(e),new Proxy(this,{get(t,s,r){return typeof s=="symbol"||s in t||typeof s!="string"||["table","primaryKey","incrementing","timestamps","casts","fillable","hidden","connection"].includes(s)?Reflect.get(t,s,r):typeof t[s]=="function"?t[s].bind(t):t.getAttribute(s)},set(t,s,r){return typeof s=="symbol"||s in t?Reflect.set(t,s,r):(t.setAttribute(s,r),!0)}})}static query(){let e=new this,t=this;return new D(t.table,this,t.connection)}static async find(e){let t=this;return this.query().find(e,t.primaryKey)}static async findOrFail(e){let t=this;return this.query().findOrFail(e,t.primaryKey)}static async all(){return this.query().get()}static async first(){return this.query().first()}static async firstOrFail(){return this.query().firstOrFail()}static where(e,t,s){return this.query().where(e,t,s)}static whereIn(e,t){return this.query().whereIn(e,t)}static whereNull(e){return this.query().whereNull(e)}static whereNotNull(e){return this.query().whereNotNull(e)}static orderBy(e,t){return this.query().orderBy(e,t)}static latest(e){return this.query().latest(e)}static oldest(e){return this.query().oldest(e)}static with(...e){return this.query().with(...e)}static async count(){return this.query().count()}static async create(e){let t=new this,s=this;if(t.fill(e),await t.fireHook("creating"),await t.fireHook("saving"),s.timestamps){let a=new Date().toISOString();t.setAttribute(s.createdAt,a),t.setAttribute(s.updatedAt,a)}let r=t.getInsertableAttributes(),n=await new D(s.table,this,s.connection).insertGetId(r,s.primaryKey);return s.incrementing&&n&&t.setAttribute(s.primaryKey,n),t.syncOriginal(),t.exists=!0,await t.fireHook("created"),await t.fireHook("saved"),t}async save(){let e=this.constructor;if(this.exists){await this.fireHook("updating"),await this.fireHook("saving"),e.timestamps&&this.setAttribute(e.updatedAt,new Date().toISOString());let t=this.getDirty();if(Object.keys(t).length>0){let s=this.getAttribute(e.primaryKey);await new D(e.table,this.constructor,e.connection).where(e.primaryKey,s).update(t)}this.syncOriginal(),await this.fireHook("updated"),await this.fireHook("saved")}else{if(await this.fireHook("creating"),await this.fireHook("saving"),e.timestamps){let i=new Date().toISOString();this.getAttribute(e.createdAt)||this.setAttribute(e.createdAt,i),this.setAttribute(e.updatedAt,i)}let t=this.getInsertableAttributes(),r=await new D(e.table,this.constructor,e.connection).insertGetId(t,e.primaryKey);e.incrementing&&r&&this.setAttribute(e.primaryKey,r),this.syncOriginal(),this.exists=!0,await this.fireHook("created"),await this.fireHook("saved")}}async update(e){this.fill(e),await this.save()}async delete(){let e=this.constructor;await this.fireHook("deleting");let t=this.getAttribute(e.primaryKey);await new D(e.table,this.constructor,e.connection).where(e.primaryKey,t).delete(),this.exists=!1,await this.fireHook("deleted")}async refresh(){let e=this.constructor,t=this.getAttribute(e.primaryKey),s=await this.constructor.find(t);s&&(this.attributes={...s.attributes},this.syncOriginal())}getAttribute(e){let t=this.constructor,s=this.attributes[e],r=t.casts[e];if(r&&s!==void 0&&s!==null)switch(r){case"number":return Number(s);case"boolean":return!!s;case"string":return String(s);case"date":return new Date(s);case"json":return typeof s=="string"?JSON.parse(s):s}return s}setAttribute(e,t){this.constructor.casts[e]==="json"&&typeof t!="string"?this.attributes[e]=JSON.stringify(t):this.attributes[e]=t}fill(e){let t=this.constructor;for(let[s,r]of Object.entries(e))t.fillable.length>0&&!t.fillable.includes(s)||this.setAttribute(s,r)}getAttributes(){return{...this.attributes}}getOriginal(e){return e?this.originalAttributes[e]:{...this.originalAttributes}}getDirty(){let e={};for(let[t,s]of Object.entries(this.attributes))s!==this.originalAttributes[t]&&(e[t]=s);return e}isDirty(...e){let t=this.getDirty();return e.length===0?Object.keys(t).length>0:e.some(s=>s in t)}isClean(...e){return!this.isDirty(...e)}wasChanged(...e){return this.isDirty(...e)}hasOne(e,t,s){return new Y(this,e,t,s??this.constructor.primaryKey)}hasMany(e,t,s){return new X(this,e,t,s??this.constructor.primaryKey)}belongsTo(e,t,s){return new Z(this,e,t,s??e.primaryKey)}belongsToMany(e,t,s,r,i,n){return new ee(this,e,t,s,r,i??this.constructor.primaryKey,n??e.primaryKey)}setRelation(e,t){this.relations[e]=t}getRelation(e){return this.relations[e]}relationLoaded(e){return e in this.relations}toJSON(){let e=this.constructor,t={};for(let[s,r]of Object.entries(this.attributes))e.hidden.includes(s)||(t[s]=this.getAttribute(s));for(let[s,r]of Object.entries(this.relations))Array.isArray(r)?t[s]=r.map(i=>i instanceof l?i.toJSON():i):r instanceof l?t[s]=r.toJSON():t[s]=r;return t}toObject(){return this.toJSON()}static hydrate(e){let t=new this;return t.attributes={...e},t.syncOriginal(),t.exists=!0,t}static boot(e){this.hooks.set(this.name,e)}static observe(e){let t=this.observers.get(this.name)??[];t.push(e),this.observers.set(this.name,t)}static removeObservers(){this.observers.delete(this.name)}async fireHook(e){let t=this.constructor,s=l.hooks.get(t.name);s?.[e]&&await s[e](this),typeof this[e]=="function"&&await this[e]();let r=l.observers.get(t.name)??[];for(let n of r){let a=n[e];typeof a=="function"&&await a.call(n,this)}let i=t.name.toLowerCase();await te.emit(`${i}.${e}`,this)}async fireEvent(e){let t=this.constructor;if(!t.events.includes(e))throw new Error(`Event "${e}" is not declared in ${t.name}.events. Add it to: static events = ['${e}', ...];`);let s=l.observers.get(t.name)??[];for(let i of s){let n=i[e];typeof n=="function"&&await n.call(i,this)}let r=t.name.toLowerCase();await te.emit(`${r}.${e}`,this)}syncOriginal(){this.originalAttributes={...this.attributes}}getInsertableAttributes(){let e=this.constructor,t={...this.attributes};return e.incrementing&&t[e.primaryKey]===void 0&&delete t[e.primaryKey],t}static get tableName(){return this.table}}});var kt,pi=y(()=>{"use strict";kt=class{async call(e){await new e().run()}}});var F,hi,ks=y(()=>{"use strict";R();F=class{bindings=new Map;aliases=new Map;resolved=new Set;bind(e,t){this.bindings.set(e,{factory:t,singleton:!1,tags:[]})}singleton(e,t){this.bindings.set(e,{factory:t,singleton:!0,tags:[]})}instance(e,t){this.bindings.set(e,{factory:()=>t,singleton:!0,instance:t,tags:[]})}alias(e,t){this.aliases.set(e,t)}async make(e){let t=this.resolveAlias(e),s=this.bindings.get(t);if(!s)throw new Error(`No binding found for "${e}" in the container.`);if(s.singleton&&s.instance!==void 0)return s.instance;let r=await s.factory(this);return s.singleton&&(s.instance=r),this.resolved.add(t),r}makeSync(e){let t=this.resolveAlias(e),s=this.bindings.get(t);if(!s)throw new Error(`No binding found for "${e}" in the container.`);if(s.singleton&&s.instance!==void 0)return s.instance;let r=s.factory(this);if(r instanceof Promise)throw new Error(`Binding "${e}" has an async factory. Use container.make() instead of container.makeSync().`);return s.singleton&&(s.instance=r),this.resolved.add(t),r}has(e){let t=this.resolveAlias(e);return this.bindings.has(t)}isResolved(e){return this.resolved.has(this.resolveAlias(e))}tag(e,t){for(let s of e){let r=this.bindings.get(s);r&&r.tags.push(t)}}async tagged(e){let t=[];for(let[s,r]of this.bindings)r.tags.includes(e)&&t.push(await this.make(s));return t}flush(){for(let e of this.bindings.values())e.singleton&&(e.instance=void 0);this.resolved.clear()}forget(e){this.bindings.delete(e),this.resolved.delete(e)}getBindings(){return[...this.bindings.keys()]}resolveAlias(e){return this.aliases.get(e)??e}},hi=w("svelar.container",()=>new F)});var $t,gi=y(()=>{"use strict";ks();$t=class{container;providers=[];booted=!1;constructor(e){this.container=e??new F,this.container.instance("app",this),this.container.instance("container",this.container)}register(e){let t=new e(this.container);return this.providers.push(t),this}async bootstrap(){if(this.booted)return this;for(let e of this.providers)await e.register();for(let e of this.providers)await e.boot();return this.booted=!0,this}isBooted(){return this.booted}async make(e){return this.container.make(e)}getProviders(){return[...this.providers]}}});var T,L,At,se,Dt,re,ie,Mt,ne,ae=y(()=>{"use strict";T=class{},L=class{middleware=[];namedMiddleware=new Map;use(e){return typeof e=="function"&&"prototype"in e&&typeof e.prototype?.handle=="function"?this.middleware.push(new e):this.middleware.push(e),this}register(e,t){return typeof t=="function"&&"prototype"in t&&typeof t.prototype?.handle=="function"?this.namedMiddleware.set(e,new t):this.namedMiddleware.set(e,t),this}get(e){return this.namedMiddleware.get(e)}async execute(e,t,s){let r=[...this.middleware];if(s)for(let n of s){let a=this.namedMiddleware.get(n);a&&r.push(a)}let i=t;for(let n=r.length-1;n>=0;n--){let a=r[n],o=i;typeof a.handle=="function"?i=()=>a.handle(e,o):i=()=>a(e,o)}return i()}count(){return this.middleware.length}},At=class extends T{constructor(t={}){super();this.options=t}async handle(t,s){let r=await s();if(!r)return;let i=Array.isArray(this.options.origin)?this.options.origin.join(", "):this.options.origin??"*";return r.headers.set("Access-Control-Allow-Origin",i),r.headers.set("Access-Control-Allow-Methods",(this.options.methods??["GET","POST","PUT","DELETE","PATCH","OPTIONS"]).join(", ")),r.headers.set("Access-Control-Allow-Headers",(this.options.headers??["Content-Type","Authorization"]).join(", ")),this.options.credentials&&r.headers.set("Access-Control-Allow-Credentials","true"),this.options.maxAge&&r.headers.set("Access-Control-Max-Age",String(this.options.maxAge)),t.event.request.method==="OPTIONS"?new Response(null,{status:204,headers:r.headers}):r}},se=class extends T{requests=new Map;maxRequests;windowMs;constructor(e={}){super(),this.maxRequests=e.maxRequests??60,this.windowMs=e.windowMs??6e4}async handle(e,t){let s=e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown",r=Date.now(),i=this.requests.get(s);if(i&&r<i.resetAt){if(i.count>=this.maxRequests)return new Response(JSON.stringify({error:"Too many requests"}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(Math.ceil((i.resetAt-r)/1e3))}});i.count++}else this.requests.set(s,{count:1,resetAt:r+this.windowMs});return t()}},Dt=class extends T{async handle(e,t){let s=Date.now(),r=e.event.request.method,i=e.event.url.pathname,n=await t(),a=Date.now()-s,o=n instanceof Response?n.status:200;return console.log(`[${new Date().toISOString()}] ${r} ${i} \u2192 ${o} (${a}ms)`),n}},re=class extends T{cookieName;headerName;fieldName;excludePaths;onlyPaths;constructor(e={}){super(),this.cookieName=e.cookieName??"XSRF-TOKEN",this.headerName=e.headerName??"X-CSRF-Token",this.fieldName=e.fieldName??"_csrf",this.excludePaths=e.excludePaths??[],this.onlyPaths=e.onlyPaths??null}async handle(e,t){let{event:s}=e,r=s.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(r))return this.setTokenAndContinue(e,t);if((s.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let n=s.url.pathname;if(this.onlyPaths&&!this.onlyPaths.some(c=>n.startsWith(c)))return this.setTokenAndContinue(e,t);if(this.excludePaths.some(c=>n.startsWith(c)))return t();let a=this.getCookieToken(s),o=s.request.headers.get(this.headerName)??await this.getBodyToken(s);return!a||!o||!this.timingSafeEqual(a,o)?new Response(JSON.stringify({message:"CSRF token mismatch"}),{status:419,headers:{"Content-Type":"application/json"}}):t()}async setTokenAndContinue(e,t){let s=await t();if(!(s instanceof Response))return s;let i=this.getCookieToken(e.event)||this.generateToken();return s.headers.append("Set-Cookie",`${this.cookieName}=${i}; Path=/; SameSite=Lax`),s}getCookieToken(e){let s=(e.request.headers.get("cookie")??"").match(new RegExp(`${this.cookieName}=([^;]+)`));return s?s[1]:null}async getBodyToken(e){try{let t=e.request.headers.get("content-type")??"";if(t.includes("application/x-www-form-urlencoded")||t.includes("multipart/form-data"))return(await e.request.clone().formData()).get(this.fieldName);if(t.includes("application/json"))return(await e.request.clone().json())[this.fieldName]??null}catch{}return null}generateToken(){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}timingSafeEqual(e,t){if(e.length!==t.length)return!1;let s=new TextEncoder,r=s.encode(e),i=s.encode(t),n=0;for(let a=0;a<r.length;a++)n|=r[a]^i[a];return n===0}},ie=class extends T{allowedOrigins;constructor(e={}){super(),this.allowedOrigins=new Set(e.allowedOrigins??[])}async handle(e,t){let{event:s}=e,r=s.request.method.toUpperCase();if(["GET","HEAD","OPTIONS"].includes(r)||(s.request.headers.get("authorization")??"").startsWith("Bearer "))return t();let n=s.request.headers.get("origin");if(!n)return t();let a=s.url.origin;return n===a||this.allowedOrigins.has(n)?t():new Response(JSON.stringify({message:"Cross-origin request blocked"}),{status:403,headers:{"Content-Type":"application/json"}})}},Mt=class extends T{secret;tolerance;signatureHeader;timestampHeader;onlyPaths;constructor(e){super(),this.secret=e.secret,this.tolerance=e.tolerance??300,this.signatureHeader=e.signatureHeader??"X-Signature",this.timestampHeader=e.timestampHeader??"X-Timestamp",this.onlyPaths=e.onlyPaths??null}async handle(e,t){let{event:s}=e;if(this.onlyPaths){let C=s.url.pathname;if(!this.onlyPaths.some(k=>C.startsWith(k)))return t()}let r=s.request.headers.get(this.signatureHeader),i=s.request.headers.get(this.timestampHeader);if(!r||!i)return new Response(JSON.stringify({message:"Missing request signature"}),{status:401,headers:{"Content-Type":"application/json"}});let n=parseInt(i,10),a=Math.floor(Date.now()/1e3);if(isNaN(n)||Math.abs(a-n)>this.tolerance)return new Response(JSON.stringify({message:"Request signature expired"}),{status:401,headers:{"Content-Type":"application/json"}});let c=await s.request.clone().text(),{createHmac:u}=await import("crypto"),d=s.request.method.toUpperCase(),p=s.url.pathname+s.url.search,f=`${i}.${d}.${p}.${c}`,b=u("sha256",this.secret).update(f).digest("hex");return r.length!==b.length||!this.timingSafeCompare(r,b)?new Response(JSON.stringify({message:"Invalid request signature"}),{status:401,headers:{"Content-Type":"application/json"}}):t()}timingSafeCompare(e,t){if(e.length!==t.length)return!1;let s=new TextEncoder,r=s.encode(e),i=s.encode(t),n=0;for(let a=0;a<r.length;a++)n|=r[a]^i[a];return n===0}static sign(e,t,s,r,i){let{createHmac:n}=Fe("crypto"),a=i??Math.floor(Date.now()/1e3),o=`${a}.${t.toUpperCase()}.${s}.${r}`;return{signature:n("sha256",e).update(o).digest("hex"),timestamp:a}}},ne=class extends T{attempts=new Map;maxAttempts;decayMinutes;constructor(e={}){super(),this.maxAttempts=e.maxAttempts??5,this.decayMinutes=e.decayMinutes??1}async handle(e,t){let r=`${e.event.request.headers.get("x-forwarded-for")??e.event.getClientAddress?.()??"unknown"}:${e.event.url.pathname}`,i=Date.now(),n=this.attempts.get(r);if(n){if(i<n.blockedUntil){let o=Math.ceil((n.blockedUntil-i)/1e3);return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:o}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(o)}})}if(n.count>=this.maxAttempts){n.blockedUntil=i+this.decayMinutes*6e4,n.count=0;let o=this.decayMinutes*60;return new Response(JSON.stringify({message:"Too many attempts. Please try again later.",retry_after:o}),{status:429,headers:{"Content-Type":"application/json","Retry-After":String(o)}})}}let a=await t();if(a instanceof Response&&a.status>=400&&a.status<500){let o=this.attempts.get(r)??{count:0,blockedUntil:0};if(o.count++,this.attempts.set(r,o),this.attempts.size>1e4)for(let[c,u]of this.attempts)i>u.blockedUntil+this.decayMinutes*6e4*2&&this.attempts.delete(c)}return a}}});import{z as oe}from"zod";function fi(l,e=!1){let t=new l;return e?{GET:t.handle("show"),PUT:t.handle("update"),PATCH:t.handle("update"),DELETE:t.handle("destroy")}:{GET:t.handle("index"),POST:t.handle("store")}}var Nt,q,Ae,De,Me,yi=y(()=>{"use strict";ae();Nt=class{controllerMiddleware=[];middleware(e,t){let s;typeof e=="function"&&e.prototype instanceof T?s=new e:s=e,this.controllerMiddleware.push({middleware:s,only:t?.only,except:t?.except})}handle(e){return async t=>{try{let s=this.controllerMiddleware.filter(r=>!(r.only&&!r.only.includes(e)||r.except&&r.except.includes(e)));if(s.length>0){let r=new L;for(let{middleware:a}of s)r.use(a);let i={event:t,params:t.params,locals:t.locals},n=await r.execute(i,async()=>this.callMethod(e,t));if(n instanceof Response)return n}return await this.callMethod(e,t)}catch(s){return this.handleError(s,t)}}}json(e,t=200,s={}){let r=JSON.stringify(e,null,2);return new Response(r,{status:t,headers:{"Content-Type":"application/json",...s}})}text(e,t=200){return new Response(e,{status:t,headers:{"Content-Type":"text/plain"}})}html(e,t=200){return new Response(e,{status:t,headers:{"Content-Type":"text/html"}})}redirect(e,t=302){return new Response(null,{status:t,headers:{Location:e}})}noContent(){return new Response(null,{status:204})}created(e){return e?this.json(e,201):new Response(null,{status:201})}async validate(e,t){let s=t instanceof oe.ZodObject?t:oe.object(t),r,i=e.request.headers.get("content-type")??"";if(i.includes("application/json"))r=await e.request.json();else if(i.includes("multipart/form-data")||i.includes("application/x-www-form-urlencoded")){let a=await e.request.formData();r=Object.fromEntries(a)}else r=Object.fromEntries(e.url.searchParams);let n=s.safeParse(r);if(!n.success)throw new q(n.error);return n.data}validateQuery(e,t){let s=t instanceof oe.ZodObject?t:oe.object(t),r=Object.fromEntries(e.url.searchParams),i=s.safeParse(r);if(!i.success)throw new q(i.error);return i.data}validateParams(e,t){let r=(t instanceof oe.ZodObject?t:oe.object(t)).safeParse(e.params);if(!r.success)throw new q(r.error);return r.data}handleError(e,t){return e instanceof q?this.json({message:"Validation failed",errors:e.errors},422):e instanceof Ae?this.json({message:e.message||"Not found"},404):e instanceof De?this.json({message:e.message||"Unauthorized"},401):e instanceof Me?this.json({message:e.message||"Forbidden"},403):(console.error("[Svelar] Controller error:",e),this.json({message:process.env.NODE_ENV==="production"?"Internal server error":e.message},500))}async callMethod(e,t){let s=this[e];if(typeof s!="function")throw new Error(`Method "${e}" not found on controller "${this.constructor.name}".`);let r=await s.call(this,t);return r instanceof Response?r:this.json(r)}};q=class extends Error{errors;constructor(e){super("Validation failed"),this.name="ValidationError",this.errors={};for(let t of e.issues){let s=t.path.join(".");this.errors[s]||(this.errors[s]=[]),this.errors[s].push(t.message)}}},Ae=class extends Error{constructor(e="Not found"){super(e),this.name="NotFoundError"}},De=class extends Error{constructor(e="Unauthorized"){super(e),this.name="UnauthorizedError"}},Me=class extends Error{constructor(e="Forbidden"){super(e),this.name="ForbiddenError"}}});import{randomBytes as Fa,createHmac as vi,timingSafeEqual as Ha}from"crypto";import{promises as U}from"fs";import{join as Ne}from"path";var le,_e,H,_t,It,jt,ce,$s=y(()=>{"use strict";ae();le=class l{constructor(e,t){this.id=e;t&&(this.data={...t},this.data._flash&&(this.previousFlashData=this.data._flash,delete this.data._flash))}data={};dirty=!1;flashData={};previousFlashData={};get(e,t){return e in this.flashData?this.flashData[e]:e in this.previousFlashData?this.previousFlashData[e]:e in this.data?this.data[e]:t}set(e,t){this.data[e]=t,this.dirty=!0}has(e){return e in this.data||e in this.flashData||e in this.previousFlashData}forget(e){delete this.data[e],this.dirty=!0}flush(){this.data={},this.dirty=!0}flash(e,t){this.flashData[e]=t,this.dirty=!0}all(){return{...this.data,...this.previousFlashData,...this.flashData}}isDirty(){return this.dirty||Object.keys(this.flashData).length>0}toPersist(){let e={...this.data};return Object.keys(this.flashData).length>0&&(e._flash=this.flashData),e}regenerateId(){let e=this.id,t=l.generateId();this.id=t,this.dirty=!0;let s=_e.get(e);return s instanceof H&&s.markOldSessionId(e),_e.delete(e),t}static generateId(){return Fa(32).toString("hex")}},_e=new Map,H=class{sessions=new Map;oldSessionIds=new Set;async read(e){if(this.oldSessionIds.has(e))return null;let t=this.sessions.get(e);return t?Date.now()>t.expiresAt?(this.sessions.delete(e),null):t.data:null}async write(e,t,s){this.sessions.set(e,{data:t,expiresAt:Date.now()+s*1e3}),_e.set(e,this)}async destroy(e){this.sessions.delete(e),_e.delete(e)}async gc(e){let t=Date.now();for(let[s,r]of this.sessions)t>r.expiresAt&&(this.sessions.delete(s),_e.delete(s))}markOldSessionId(e){this.oldSessionIds.add(e),this.sessions.delete(e)}},_t=class{constructor(e="sessions",t){this.tableName=e;this.connectionName=t}tableEnsured=!1;async ensureTable(){if(!this.tableEnsured)try{let{Connection:e}=await Promise.resolve().then(()=>(x(),P));switch(e.getDriver(this.connectionName)){case"sqlite":await e.raw(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
43
43
|
id TEXT PRIMARY KEY,
|
|
44
44
|
payload TEXT NOT NULL,
|
|
45
45
|
expires_at TEXT NOT NULL
|
|
@@ -51,17 +51,17 @@ var wn=Object.defineProperty;var He=(l=>typeof require<"u"?require:typeof Proxy<
|
|
|
51
51
|
id VARCHAR(255) PRIMARY KEY,
|
|
52
52
|
payload TEXT NOT NULL,
|
|
53
53
|
expires_at DATETIME NOT NULL
|
|
54
|
-
) ENGINE=InnoDB`,[],this.connectionName);break}this.tableEnsured=!0}catch{}}async read(e){await this.ensureTable();let{Connection:t}=await Promise.resolve().then(()=>(x(),
|
|
55
|
-
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, expires_at = excluded.expires_at`,[e,n
|
|
56
|
-
ON CONFLICT(id) DO UPDATE SET payload = $2, expires_at = $3`,[e,n
|
|
57
|
-
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,n
|
|
54
|
+
) ENGINE=InnoDB`,[],this.connectionName);break}this.tableEnsured=!0}catch{}}async read(e){await this.ensureTable();let{Connection:t}=await Promise.resolve().then(()=>(x(),P)),s=await t.raw(`SELECT payload, expires_at FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName);if(s.length===0)return null;let r=s[0];if(new Date(r.expires_at)<new Date)return await this.destroy(e),null;try{return JSON.parse(r.payload)}catch{return null}}async write(e,t,s){await this.ensureTable();let{Connection:r}=await Promise.resolve().then(()=>(x(),P)),i=JSON.stringify(t),n=new Date(Date.now()+s*1e3).toISOString(),a=r.getDriver(this.connectionName);a==="sqlite"?await r.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
55
|
+
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, expires_at = excluded.expires_at`,[e,i,n],this.connectionName):a==="postgres"?await r.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES ($1, $2, $3)
|
|
56
|
+
ON CONFLICT(id) DO UPDATE SET payload = $2, expires_at = $3`,[e,i,n],this.connectionName):await r.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
57
|
+
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,i,n],this.connectionName)}async destroy(e){let{Connection:t}=await Promise.resolve().then(()=>(x(),P));await t.raw(`DELETE FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName)}async gc(e){let{Connection:t}=await Promise.resolve().then(()=>(x(),P));await t.raw(`DELETE FROM ${this.tableName} WHERE expires_at < ?`,[new Date().toISOString()],this.connectionName)}},It=class{dir;constructor(e){this.dir=e??Ne(process.cwd(),"storage","sessions")}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return Ne(this.dir,`${t}.json`)}async ensureDir(){await U.mkdir(this.dir,{recursive:!0})}async read(e){try{let t=await U.readFile(this.filePath(e),"utf-8"),s=JSON.parse(t);return new Date(s.expiresAt)<new Date?(await this.destroy(e),null):s.data}catch{return null}}async write(e,t,s){await this.ensureDir();let r={data:t,expiresAt:new Date(Date.now()+s*1e3).toISOString()};await U.writeFile(this.filePath(e),JSON.stringify(r),"utf-8")}async destroy(e){try{await U.unlink(this.filePath(e))}catch{}}async gc(e){try{let t=await U.readdir(this.dir),s=new Date;for(let r of t)if(r.endsWith(".json"))try{let i=await U.readFile(Ne(this.dir,r),"utf-8"),n=JSON.parse(i);new Date(n.expiresAt)<s&&await U.unlink(Ne(this.dir,r))}catch{await U.unlink(Ne(this.dir,r)).catch(()=>{})}}catch{}}},jt=class{redis;prefix;constructor(e){this.prefix=e?.prefix??"svelar_session:",e?.client?this.redis=e.client:this._url=e?.url}_url;_connecting;async getClient(){return this.redis?this.redis:(this._connecting||(this._connecting=(async()=>{try{let{default:e}=await import("ioredis");return this.redis=this._url?new e(this._url):new e,this.redis}catch{throw new Error('RedisSessionStore requires "ioredis" package. Install it: npm install ioredis')}})()),this._connecting)}async read(e){let s=await(await this.getClient()).get(this.prefix+e);if(!s)return null;try{return JSON.parse(s)}catch{return null}}async write(e,t,s){await(await this.getClient()).set(this.prefix+e,JSON.stringify(t),"EX",s)}async destroy(e){await(await this.getClient()).del(this.prefix+e)}async gc(e){}},ce=class extends T{config;constructor(e){if(super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:"",path:"/",domain:"",secure:process.env.NODE_ENV==="production",httpOnly:!0,sameSite:"lax",...e},!this.config.secret)throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")}async handle(e,t){let s=e.event.request.headers.get("cookie")??"",r=this.getSessionIdFromCookie(s),i=null;if(r){let o=this.verifySignedId(r);o?(i=await this.config.store.read(o),r=o):r=null}r||(r=le.generateId());let n=new le(r,i??{});e.event.locals.session=n,e.locals.session=n;let a=await t();if(n.isDirty()&&await this.config.store.write(n.id,n.toPersist(),this.config.lifetime),a instanceof Response){let o=this.signId(n.id),c=this.buildCookieString(o);a.headers.append("Set-Cookie",c)}return a}getSessionIdFromCookie(e){let t=e.split(";").map(s=>s.trim());for(let s of t){let[r,...i]=s.split("=");if(r===this.config.cookieName)return decodeURIComponent(i.join("="))}return null}signId(e){let t=vi("sha256",this.config.secret).update(e).digest("base64url");return`${e}.${t}`}verifySignedId(e){let t=e.lastIndexOf(".");if(t===-1)return null;let s=e.slice(0,t),r=e.slice(t+1),i=vi("sha256",this.config.secret).update(s).digest("base64url");if(r.length!==i.length)return null;let n=Buffer.from(r),a=Buffer.from(i);if(n.length!==a.length)return null;try{if(Ha(n,a))return s}catch{}return null}buildCookieString(e){let t=[`${this.config.cookieName}=${encodeURIComponent(e)}`];return t.push(`Path=${this.config.path}`),t.push(`Max-Age=${this.config.lifetime}`),this.config.domain&&t.push(`Domain=${this.config.domain}`),this.config.secure&&t.push("Secure"),this.config.httpOnly&&t.push("HttpOnly"),t.push(`SameSite=${this.config.sameSite}`),t.join("; ")}}});var Ie={};E(Ie,{Hash:()=>Ms});import{randomBytes as As,scrypt as bi,timingSafeEqual as za}from"crypto";async function Ka(l,e=16384){let t=As(16),s=64,r=await new Promise((i,n)=>{bi(l,t,s,{N:e,r:8,p:1},(a,o)=>{a?n(a):i(o)})});return`$scrypt$N=${e}$${t.toString("base64")}$${r.toString("base64")}`}async function Wa(l,e){let t=e.split("$");if(t.length!==5||t[1]!=="scrypt")return!1;let s=parseInt(t[2].replace("N=",""),10),r=Buffer.from(t[3],"base64"),i=Buffer.from(t[4],"base64"),n=i.length,a=await new Promise((o,c)=>{bi(l,r,n,{N:s,r:8,p:1},(u,d)=>{u?c(u):o(d)})});return za(a,i)}var Ds,Ms,de=y(()=>{"use strict";R();Ds=class{config={driver:"scrypt",scryptCost:16384,bcryptRounds:12};configure(e){Object.assign(this.config,e)}async make(e){switch(this.config.driver){case"scrypt":return Ka(e,this.config.scryptCost);case"bcrypt":try{return(await import("bcrypt")).default.hash(e,this.config.bcryptRounds??12)}catch{throw new Error('bcrypt driver requires the "bcrypt" package. Install it: npm install bcrypt')}case"argon2":try{return(await import("argon2")).default.hash(e)}catch{throw new Error('argon2 driver requires the "argon2" package. Install it: npm install argon2')}default:throw new Error(`Unsupported hash driver: ${this.config.driver}`)}}async verify(e,t){if(t.startsWith("$scrypt$"))return Wa(e,t);if(t.startsWith("$2b$")||t.startsWith("$2a$")||t.startsWith("$2y$"))try{return(await import("bcrypt")).default.compare(e,t)}catch{throw new Error("bcrypt package required to verify bcrypt hashes.")}if(t.startsWith("$argon2"))try{return(await import("argon2")).default.verify(t,e)}catch{throw new Error("argon2 package required to verify argon2 hashes.")}return!1}needsRehash(e){if(this.config.driver==="scrypt"&&e.startsWith("$scrypt$")){let t=e.match(/N=(\d+)/);if(t)return parseInt(t[1],10)!==this.config.scryptCost}if(this.config.driver==="bcrypt"&&(e.startsWith("$2b$")||e.startsWith("$2a$"))){let t=e.match(/\$2[aby]\$(\d+)\$/);if(t)return parseInt(t[1],10)!==this.config.bcryptRounds}return this.config.driver==="scrypt"&&!e.startsWith("$scrypt$")||this.config.driver==="bcrypt"&&!e.startsWith("$2")||this.config.driver==="argon2"&&!e.startsWith("$argon2")}randomString(e=32){return As(Math.ceil(e/2)).toString("hex").slice(0,e)}randomToken(e=32){return As(e).toString("base64url")}},Ms=w("svelar.hash",()=>new Ds)});var wi={};E(wi,{EmailTemplates:()=>Ja});import{randomUUID as _}from"crypto";var Ns,Ja,xi=y(()=>{"use strict";R();Ns=class{config={driver:"memory"};templates=new Map;constructor(){this.registerDefaults()}configure(e){this.config=e}async register(e){let t={...e,id:_(),createdAt:Date.now(),updatedAt:Date.now()};if(this.config.driver==="memory")this.templates.set(e.name,t);else if(this.config.driver==="database")try{let{Connection:s}=await Promise.resolve().then(()=>(x(),P));await s.connection()}catch{this.templates.set(e.name,t)}return t}async render(e,t){let s=await this.get(e);if(!s)throw new Error(`Template "${e}" not found`);let r=this.interpolate(s.subject,t),i=this.interpolate(s.html,t),n=s.text?this.interpolate(s.text,t):void 0;return{subject:r,html:i,text:n}}async get(e){return this.templates.get(e)||null}async list(e){let t=Array.from(this.templates.values());return e&&(t=t.filter(s=>s.category===e)),t}async update(e,t){let s=this.templates.get(e);return s?(Object.assign(s,t,{updatedAt:Date.now()}),s):null}async delete(e){return this.templates.delete(e)}registerDefaults(){this.templates.set("welcome",{id:_(),name:"welcome",subject:"Welcome to {{appName}}, {{user.name}}!",html:`
|
|
58
58
|
<h1>Welcome, {{user.name}}!</h1>
|
|
59
59
|
<p>Thank you for joining {{appName}}.</p>
|
|
60
60
|
<p>Your account has been created with the email: <strong>{{user.email}}</strong></p>
|
|
61
61
|
<p><a href="{{confirmUrl}}">Confirm your email address</a></p>
|
|
62
62
|
`,text:`Welcome, {{user.name}}!
|
|
63
63
|
|
|
64
|
-
Confirm your email: {{confirmUrl}}`,variables:["appName","user.name","user.email","confirmUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("password-reset",{id:
|
|
64
|
+
Confirm your email: {{confirmUrl}}`,variables:["appName","user.name","user.email","confirmUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("password-reset",{id:_(),name:"password-reset",subject:"Reset your {{appName}} password",html:`
|
|
65
65
|
<h1>Password Reset</h1>
|
|
66
66
|
<p>Hi {{user.name}},</p>
|
|
67
67
|
<p>Click the link below to reset your password. This link expires in 1 hour.</p>
|
|
@@ -69,18 +69,18 @@ Confirm your email: {{confirmUrl}}`,variables:["appName","user.name","user.email
|
|
|
69
69
|
<p>If you didn't request this, you can ignore this email.</p>
|
|
70
70
|
`,text:`Reset your password: {{resetUrl}}
|
|
71
71
|
|
|
72
|
-
This link expires in 1 hour.`,variables:["appName","user.name","resetUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("email-verification",{id:
|
|
72
|
+
This link expires in 1 hour.`,variables:["appName","user.name","resetUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("email-verification",{id:_(),name:"email-verification",subject:"Verify your email address",html:`
|
|
73
73
|
<h1>Verify Your Email</h1>
|
|
74
74
|
<p>Hi {{user.name}},</p>
|
|
75
75
|
<p><a href="{{verifyUrl}}">Click here to verify your email address</a></p>
|
|
76
76
|
<p>This link expires in 24 hours.</p>
|
|
77
|
-
`,text:"Verify your email: {{verifyUrl}}",variables:["user.name","verifyUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("team-invitation",{id:
|
|
77
|
+
`,text:"Verify your email: {{verifyUrl}}",variables:["user.name","verifyUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("team-invitation",{id:_(),name:"team-invitation",subject:"{{inviter.name}} invited you to join {{team.name}}",html:`
|
|
78
78
|
<h1>Team Invitation</h1>
|
|
79
79
|
<p>Hi {{user.name}},</p>
|
|
80
80
|
<p><strong>{{inviter.name}}</strong> has invited you to join the team <strong>{{team.name}}</strong>.</p>
|
|
81
81
|
<p><a href="{{acceptUrl}}">Accept Invitation</a></p>
|
|
82
82
|
<p>This invitation expires in 3 days.</p>
|
|
83
|
-
`,text:"Accept: {{acceptUrl}}",variables:["user.name","inviter.name","team.name","acceptUrl"],category:"notification",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("invoice",{id:
|
|
83
|
+
`,text:"Accept: {{acceptUrl}}",variables:["user.name","inviter.name","team.name","acceptUrl"],category:"notification",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("invoice",{id:_(),name:"invoice",subject:"Invoice #{{invoice.number}} from {{appName}}",html:`
|
|
84
84
|
<h1>Invoice #{{invoice.number}}</h1>
|
|
85
85
|
<p>Hi {{customer.name}},</p>
|
|
86
86
|
<p>Thank you for your purchase!</p>
|
|
@@ -88,14 +88,14 @@ This link expires in 1 hour.`,variables:["appName","user.name","resetUrl"],categ
|
|
|
88
88
|
<p><strong>Date:</strong> {{invoice.date}}</p>
|
|
89
89
|
<p><a href="{{invoiceUrl}}">View Invoice</a></p>
|
|
90
90
|
`,text:`Invoice #{{invoice.number}}
|
|
91
|
-
Amount: {{invoice.amount}}`,variables:["appName","customer.name","invoice.number","invoice.amount","invoice.date","invoiceUrl"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("subscription-confirmation",{id:
|
|
91
|
+
Amount: {{invoice.amount}}`,variables:["appName","customer.name","invoice.number","invoice.amount","invoice.date","invoiceUrl"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("subscription-confirmation",{id:_(),name:"subscription-confirmation",subject:"Subscription Confirmation",html:`
|
|
92
92
|
<h1>Subscription Confirmed</h1>
|
|
93
93
|
<p>Hi {{user.name}},</p>
|
|
94
94
|
<p>Your {{plan.name}} plan is now active.</p>
|
|
95
95
|
<p><strong>Price:</strong> {{plan.price}} / {{plan.interval}}</p>
|
|
96
96
|
<p>Your next billing date is {{nextBillingDate}}.</p>
|
|
97
97
|
`,text:`Your {{plan.name}} plan is active.
|
|
98
|
-
Next billing: {{nextBillingDate}}`,variables:["user.name","plan.name","plan.price","plan.interval","nextBillingDate"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("otp-code",{id:
|
|
98
|
+
Next billing: {{nextBillingDate}}`,variables:["user.name","plan.name","plan.price","plan.interval","nextBillingDate"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("otp-code",{id:_(),name:"otp-code",subject:"Your {{appName}} verification code: {{code}}",html:`
|
|
99
99
|
<h1>Your Verification Code</h1>
|
|
100
100
|
<p>Hi {{user.name}},</p>
|
|
101
101
|
<p>Your one-time verification code is:</p>
|
|
@@ -104,28 +104,28 @@ Next billing: {{nextBillingDate}}`,variables:["user.name","plan.name","plan.pric
|
|
|
104
104
|
<p>If you didn't request this code, you can safely ignore this email.</p>
|
|
105
105
|
`,text:`Your verification code: {{code}}
|
|
106
106
|
|
|
107
|
-
This code expires in {{expiresMinutes}} minutes.`,variables:["appName","user.name","code","expiresMinutes","purpose"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("subscription-canceled",{id:
|
|
107
|
+
This code expires in {{expiresMinutes}} minutes.`,variables:["appName","user.name","code","expiresMinutes","purpose"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("subscription-canceled",{id:_(),name:"subscription-canceled",subject:"Your subscription has been canceled",html:`
|
|
108
108
|
<h1>Subscription Canceled</h1>
|
|
109
109
|
<p>Hi {{user.name}},</p>
|
|
110
110
|
<p>Your {{plan.name}} subscription has been canceled.</p>
|
|
111
111
|
<p>You have access until {{accessUntilDate}}.</p>
|
|
112
112
|
`,text:`Your {{plan.name}} subscription is canceled.
|
|
113
|
-
Access until: {{accessUntilDate}}`,variables:["user.name","plan.name","accessUntilDate"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()})}interpolate(e,t){let s=e;return s=s.replace(/\{\{#if\s+(\w+(?:\.\w+)*)\}\}([\s\S]*?)\{\{\/if\}\}/g,(r,n,i)=>this.getNestedValue(t,n)?i:""),s=s.replace(/\{\{#each\s+(\w+(?:\.\w+)*)\}\}([\s\S]*?)\{\{\/each\}\}/g,(r,n,i)=>{let a=this.getNestedValue(t,n);return Array.isArray(a)?a.map((o,c)=>{let u={...t,this:o,$index:c};return this.interpolate(i,u)}).join(""):""}),s=s.replace(/\{\{([\w.$]+)\}\}/g,(r,n)=>{let i=this.getNestedValue(t,n);return i!=null?String(i):""}),s}getNestedValue(e,t){return t.split(".").reduce((s,r)=>s?.[r],e)}},Xa=w("svelar.emailTemplates",()=>new Os)});var Hs={};E(Hs,{Mailable:()=>Ie,Mailer:()=>Bs});function Fs(l){return typeof l=="string"?l:`${l.name} <${l.address}>`}function A(l){return l?Array.isArray(l)?l:[l]:[]}function Si(l){return Buffer.isBuffer(l)?l.toString("base64"):Buffer.from(l).toString("base64")}var qt,js,Is,Ls,qs,Ie,Us,Bs,Ut=v(()=>{"use strict";T();qt=class{async send(e){let t=A(e.to);return console.log(`[Mail] To: ${t.join(", ")} | Subject: ${e.subject}`),e.text&&console.log(`[Mail] Body: ${e.text.slice(0,200)}`),{accepted:t,rejected:[]}}},js=class{async send(e){return{accepted:A(e.to),rejected:[]}}},Is=class{constructor(e){this.config=e}async send(e){try{let r=await(await import("nodemailer")).createTransport({host:this.config.host,port:this.config.port??587,secure:this.config.secure??!1,auth:this.config.auth}).sendMail({from:e.from?Fs(e.from):void 0,to:A(e.to).join(", "),cc:A(e.cc).join(", ")||void 0,bcc:A(e.bcc).join(", ")||void 0,replyTo:e.replyTo,subject:e.subject,text:e.text,html:e.html,attachments:e.attachments});return{accepted:r.accepted,rejected:r.rejected,messageId:r.messageId}}catch(t){throw t.code==="MODULE_NOT_FOUND"?new Error("SMTP driver requires nodemailer. Install: npm install nodemailer"):t}}},Ls=class{constructor(e){this.config=e}async send(e){let t=this.config.apiToken;if(!t)throw new Error("Postmark apiToken is required. Set it in your mailer config or POSTMARK_API_TOKEN env var.");let s=A(e.to),r=A(e.cc),n=A(e.bcc),i={From:e.from?Fs(e.from):void 0,To:s.join(", "),Subject:e.subject,MessageStream:this.config.messageStream||"outbound"};r.length>0&&(i.Cc=r.join(", ")),n.length>0&&(i.Bcc=n.join(", ")),e.replyTo&&(i.ReplyTo=e.replyTo),e.html&&(i.HtmlBody=e.html),e.text&&(i.TextBody=e.text),!i.HtmlBody&&!i.TextBody&&(i.TextBody=""),e.tags&&(i.Tag=Object.values(e.tags)[0]),e.attachments?.length&&(i.Attachments=e.attachments.map(c=>({Name:c.filename,Content:Si(c.content),ContentType:c.contentType||"application/octet-stream"})));let a=await fetch("https://api.postmarkapp.com/email",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","X-Postmark-Server-Token":t},body:JSON.stringify(i)});if(!a.ok){let c=await a.json().catch(()=>({Message:a.statusText}));throw new Error(`Postmark error ${a.status}: ${c.Message||JSON.stringify(c)}`)}let o=await a.json();return{accepted:s,rejected:[],messageId:o.MessageID}}},qs=class{constructor(e){this.config=e}async send(e){let t=this.config.apiKey;if(!t)throw new Error("Resend apiKey is required. Set it in your mailer config or RESEND_API_KEY env var.");let s=A(e.to),r=A(e.cc),n=A(e.bcc),i={from:e.from?Fs(e.from):void 0,to:s,subject:e.subject};r.length>0&&(i.cc=r),n.length>0&&(i.bcc=n),e.replyTo&&(i.reply_to=[e.replyTo]),e.html&&(i.html=e.html),e.text&&(i.text=e.text),e.tags&&(i.tags=Object.entries(e.tags).map(([c,u])=>({name:c,value:u}))),e.attachments?.length&&(i.attachments=e.attachments.map(c=>({filename:c.filename,content:Si(c.content),content_type:c.contentType||"application/octet-stream"})));let a=await fetch("https://api.resend.com/emails",{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!a.ok){let c=await a.json().catch(()=>({message:a.statusText}));throw new Error(`Resend error ${a.status}: ${c.message||JSON.stringify(c)}`)}let o=await a.json();return{accepted:s,rejected:[],messageId:o.id}}},Ie=class{message={};to(e){return this.message.to=e,this}cc(e){return this.message.cc=e,this}bcc(e){return this.message.bcc=e,this}from(e){return this.message.from=e,this}replyTo(e){return this.message.replyTo=e,this}subject(e){return this.message.subject=e,this}text(e){return this.message.text=e,this}html(e){return this.message.html=e,this}attach(e,t,s){return this.message.attachments||(this.message.attachments=[]),this.message.attachments.push({filename:e,content:t,contentType:s}),this}tag(e,t){return this.message.tags||(this.message.tags={}),this.message.tags[e]=t,this}toMessage(){return this.message}},Us=class{config=null;transports=new Map;configure(e){this.config=e,this.transports.clear()}async send(e,t){let s=this.resolveTransport(t);return!e.from&&this.config?.from&&(e.from=this.config.from),s.send(e)}async sendMailable(e,t){e.build();let s=e.toMessage();return!s.from&&this.config?.from&&(s.from=this.config.from),this.send(s,t)}mailer(e){let t=this.resolveTransport(e);return{send:s=>(!s.from&&this.config?.from&&(s.from=this.config.from),t.send(s))}}resolveTransport(e){let t=e??this.config?.default??"log";if(this.transports.has(t))return this.transports.get(t);if(!this.config){let n=new qt;return this.transports.set(t,n),n}let s=this.config.mailers[t];if(!s)throw new Error(`Mailer "${t}" is not defined.`);let r;switch(s.driver){case"smtp":r=new Is(s);break;case"postmark":r=new Ls(s);break;case"resend":r=new qs(s);break;case"log":r=new qt;break;case"null":r=new js;break;case"custom":{if(!s.transport)throw new Error(`Custom mail driver "${t}" requires a "transport" instance.`);r=s.transport;break}default:throw new Error(`Unknown mail driver: ${s.driver}`)}return this.transports.set(t,r),r}},Bs=w("svelar.mail",()=>new Us)});import{createHmac as me,randomBytes as Le}from"crypto";function Ti(l){return(typeof l=="string"?Buffer.from(l):l).toString("base64url")}function Ri(l){return Buffer.from(l,"base64url").toString("utf-8")}function Ei(l,e,t,s){return me(s==="HS384"?"sha384":s==="HS512"?"sha512":"sha256",t).update(`${l}.${e}`).digest("base64url")}function zs(l,e,t="HS256"){let s=Ti(JSON.stringify({alg:t,typ:"JWT"})),r=Ti(JSON.stringify(l)),n=Ei(s,r,e,t);return`${s}.${r}.${n}`}function Ks(l,e){let t=l.split(".");if(t.length!==3)return null;let[s,r,n]=t,i;try{i=JSON.parse(Ri(s))}catch{return null}let a=Ei(s,r,e,i.alg);if(n!==a)return null;try{let o=JSON.parse(Ri(r));return o.exp&&Date.now()/1e3>o.exp?null:o}catch{return null}}var Ft,pe,Bt,Ws=v(()=>{"use strict";oe();Ft=class{config;currentUser=null;constructor(e){this.config={identifierColumn:"email",passwordColumn:"password",...e}}async attempt(e,t){let{Hash:s}=await Promise.resolve().then(()=>(ue(),je)),r=e[this.config.identifierColumn],n=e[this.config.passwordColumn];if(!r||!n)return null;let i=await this.config.model.where(this.config.identifierColumn,r).first();if(!i)return null;let a=i.getAttribute(this.config.passwordColumn);return await s.verify(n,a)?(this.currentUser=i,t&&(t.set("auth_user_id",i.getAttribute("id")),t.regenerateId()),i):null}async attemptJwt(e){let{Hash:t}=await Promise.resolve().then(()=>(ue(),je));if(!this.config.jwt)throw new Error("JWT configuration required for JWT guard.");let s=e[this.config.identifierColumn],r=e[this.config.passwordColumn];if(!s||!r)return null;let n=await this.config.model.where(this.config.identifierColumn,s).first();if(!n)return null;let i=n.getAttribute(this.config.passwordColumn);return await t.verify(r,i)?(this.currentUser=n,this.issueTokenPair(n)):null}async issueTokenPair(e){let t=this.config.jwt,s=t.expiresIn??3600,r=Math.floor(Date.now()/1e3),n={sub:e.getAttribute("id"),iat:r,exp:r+s,...t.issuer?{iss:t.issuer}:{}},i=zs(n,t.secret,t.algorithm),a=new Date((r+s)*1e3),o={user:e,token:i,expiresAt:a};if(t.refreshTokens){let c=t.refreshExpiresIn??604800,u=Le(32).toString("base64url"),d=me("sha256",t.secret).update(u).digest("hex"),p=new Date((r+c)*1e3),{Connection:f}=await Promise.resolve().then(()=>(x(),C)),y=t.refreshTable??"refresh_tokens";await f.raw(`INSERT INTO ${y} (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),d,p.toISOString(),new Date().toISOString()]),o.refreshToken=u,o.refreshExpiresAt=p}return o}async refreshJwt(e){if(!this.config.jwt)throw new Error("JWT configuration required.");if(!this.config.jwt.refreshTokens)throw new Error("Refresh tokens are not enabled. Set jwt.refreshTokens = true.");let t=this.config.jwt,s=me("sha256",t.secret).update(e).digest("hex"),{Connection:r}=await Promise.resolve().then(()=>(x(),C)),n=t.refreshTable??"refresh_tokens",i=await r.raw(`SELECT user_id, expires_at, revoked_at FROM ${n} WHERE token = ?`,[s]);if(i.length===0)return null;let a=i[0];if(a.revoked_at||new Date(a.expires_at)<new Date)return null;await r.raw(`UPDATE ${n} SET revoked_at = ? WHERE token = ?`,[new Date().toISOString(),s]);let o=await this.config.model.find(a.user_id);return o?(this.currentUser=o,this.issueTokenPair(o)):null}async revokeRefreshTokens(e){if(!this.config.jwt?.refreshTokens)return;let{Connection:t}=await Promise.resolve().then(()=>(x(),C)),s=this.config.jwt.refreshTable??"refresh_tokens";await t.raw(`UPDATE ${s} SET revoked_at = ? WHERE user_id = ? AND revoked_at IS NULL`,[new Date().toISOString(),e])}async resolveFromToken(e){if(!this.config.jwt)throw new Error("JWT configuration required.");let t=Ks(e,this.config.jwt.secret);if(!t)return null;let s=await this.config.model.find(t.sub);return s&&(this.currentUser=s),s}async resolveFromSession(e){let t=e.get("auth_user_id");if(!t)return null;let s=await this.config.model.find(t);return s&&(this.currentUser=s),s}async register(e){let{Hash:t}=await Promise.resolve().then(()=>(ue(),je));e[this.config.passwordColumn]&&(e[this.config.passwordColumn]=await t.make(e[this.config.passwordColumn]));let s=await this.config.model.create(e);return this.currentUser=s,s}async logout(e){this.currentUser=null,e&&(e.forget("auth_user_id"),e.regenerateId())}user(){return this.currentUser}check(){return this.currentUser!==null}id(){return this.currentUser?.getAttribute("id")??null}async generateApiToken(e,t="default"){let s=Le(32).toString("hex"),r=this.config.jwt?.secret??process.env.APP_KEY;if(!r)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let n=me("sha256",r).update(s).digest("hex"),{Connection:i}=await Promise.resolve().then(()=>(x(),C)),a=this.config.token?.table??"personal_access_tokens";return await i.raw(`INSERT INTO ${a} (user_id, name, token, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),t,n,new Date().toISOString()]),s}async resolveFromApiToken(e){let{Connection:t}=await Promise.resolve().then(()=>(x(),C)),s=this.config.token?.table??"personal_access_tokens",r=this.config.jwt?.secret??process.env.APP_KEY;if(!r)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let n=me("sha256",r).update(e).digest("hex"),i=await t.raw(`SELECT user_id FROM ${s} WHERE token = ?`,[n]);if(i.length===0)return null;let a=await this.config.model.find(i[0].user_id);return a&&(this.currentUser=a),a}async sendPasswordReset(e){let t=await this.config.model.where(this.config.identifierColumn,e).first();if(!t)return!1;let{Connection:s}=await Promise.resolve().then(()=>(x(),C)),r=this.config.passwordResets?.table??"password_resets",n=this.config.passwordResets?.expiresIn??3600;await this.ensureTable(r,`
|
|
113
|
+
Access until: {{accessUntilDate}}`,variables:["user.name","plan.name","accessUntilDate"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()})}interpolate(e,t){let s=e;return s=s.replace(/\{\{#if\s+(\w+(?:\.\w+)*)\}\}([\s\S]*?)\{\{\/if\}\}/g,(r,i,n)=>this.getNestedValue(t,i)?n:""),s=s.replace(/\{\{#each\s+(\w+(?:\.\w+)*)\}\}([\s\S]*?)\{\{\/each\}\}/g,(r,i,n)=>{let a=this.getNestedValue(t,i);return Array.isArray(a)?a.map((o,c)=>{let u={...t,this:o,$index:c};return this.interpolate(n,u)}).join(""):""}),s=s.replace(/\{\{([\w.$]+)\}\}/g,(r,i)=>{let n=this.getNestedValue(t,i);return n!=null?String(n):""}),s}getNestedValue(e,t){return t.split(".").reduce((s,r)=>s?.[r],e)}},Ja=w("svelar.emailTemplates",()=>new Ns)});var Bs={};E(Bs,{Mailable:()=>je,Mailer:()=>Us});function qs(l){return typeof l=="string"?l:`${l.name} <${l.address}>`}function $(l){return l?Array.isArray(l)?l:[l]:[]}function Ci(l){return Buffer.isBuffer(l)?l.toString("base64"):Buffer.from(l).toString("base64")}var Ot,_s,Is,js,Os,je,Ls,Us,Lt=y(()=>{"use strict";R();Ot=class{async send(e){let t=$(e.to);return console.log(`[Mail] To: ${t.join(", ")} | Subject: ${e.subject}`),e.text&&console.log(`[Mail] Body: ${e.text.slice(0,200)}`),{accepted:t,rejected:[]}}},_s=class{async send(e){return{accepted:$(e.to),rejected:[]}}},Is=class{constructor(e){this.config=e}async send(e){try{let r=await(await import("nodemailer")).createTransport({host:this.config.host,port:this.config.port??587,secure:this.config.secure??!1,auth:this.config.auth}).sendMail({from:e.from?qs(e.from):void 0,to:$(e.to).join(", "),cc:$(e.cc).join(", ")||void 0,bcc:$(e.bcc).join(", ")||void 0,replyTo:e.replyTo,subject:e.subject,text:e.text,html:e.html,attachments:e.attachments});return{accepted:r.accepted,rejected:r.rejected,messageId:r.messageId}}catch(t){throw t.code==="MODULE_NOT_FOUND"?new Error("SMTP driver requires nodemailer. Install: npm install nodemailer"):t}}},js=class{constructor(e){this.config=e}async send(e){let t=this.config.apiToken;if(!t)throw new Error("Postmark apiToken is required. Set it in your mailer config or POSTMARK_API_TOKEN env var.");let s=$(e.to),r=$(e.cc),i=$(e.bcc),n={From:e.from?qs(e.from):void 0,To:s.join(", "),Subject:e.subject,MessageStream:this.config.messageStream||"outbound"};r.length>0&&(n.Cc=r.join(", ")),i.length>0&&(n.Bcc=i.join(", ")),e.replyTo&&(n.ReplyTo=e.replyTo),e.html&&(n.HtmlBody=e.html),e.text&&(n.TextBody=e.text),!n.HtmlBody&&!n.TextBody&&(n.TextBody=""),e.tags&&(n.Tag=Object.values(e.tags)[0]),e.attachments?.length&&(n.Attachments=e.attachments.map(c=>({Name:c.filename,Content:Ci(c.content),ContentType:c.contentType||"application/octet-stream"})));let a=await fetch("https://api.postmarkapp.com/email",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","X-Postmark-Server-Token":t},body:JSON.stringify(n)});if(!a.ok){let c=await a.json().catch(()=>({Message:a.statusText}));throw new Error(`Postmark error ${a.status}: ${c.Message||JSON.stringify(c)}`)}let o=await a.json();return{accepted:s,rejected:[],messageId:o.MessageID}}},Os=class{constructor(e){this.config=e}async send(e){let t=this.config.apiKey;if(!t)throw new Error("Resend apiKey is required. Set it in your mailer config or RESEND_API_KEY env var.");let s=$(e.to),r=$(e.cc),i=$(e.bcc),n={from:e.from?qs(e.from):void 0,to:s,subject:e.subject};r.length>0&&(n.cc=r),i.length>0&&(n.bcc=i),e.replyTo&&(n.reply_to=[e.replyTo]),e.html&&(n.html=e.html),e.text&&(n.text=e.text),e.tags&&(n.tags=Object.entries(e.tags).map(([c,u])=>({name:c,value:u}))),e.attachments?.length&&(n.attachments=e.attachments.map(c=>({filename:c.filename,content:Ci(c.content),content_type:c.contentType||"application/octet-stream"})));let a=await fetch("https://api.resend.com/emails",{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(n)});if(!a.ok){let c=await a.json().catch(()=>({message:a.statusText}));throw new Error(`Resend error ${a.status}: ${c.message||JSON.stringify(c)}`)}let o=await a.json();return{accepted:s,rejected:[],messageId:o.id}}},je=class{message={};to(e){return this.message.to=e,this}cc(e){return this.message.cc=e,this}bcc(e){return this.message.bcc=e,this}from(e){return this.message.from=e,this}replyTo(e){return this.message.replyTo=e,this}subject(e){return this.message.subject=e,this}text(e){return this.message.text=e,this}html(e){return this.message.html=e,this}attach(e,t,s){return this.message.attachments||(this.message.attachments=[]),this.message.attachments.push({filename:e,content:t,contentType:s}),this}tag(e,t){return this.message.tags||(this.message.tags={}),this.message.tags[e]=t,this}toMessage(){return this.message}},Ls=class{config=null;transports=new Map;configure(e){this.config=e,this.transports.clear()}async send(e,t){let s=this.resolveTransport(t);return!e.from&&this.config?.from&&(e.from=this.config.from),s.send(e)}async sendMailable(e,t){e.build();let s=e.toMessage();return!s.from&&this.config?.from&&(s.from=this.config.from),this.send(s,t)}mailer(e){let t=this.resolveTransport(e);return{send:s=>(!s.from&&this.config?.from&&(s.from=this.config.from),t.send(s))}}resolveTransport(e){let t=e??this.config?.default??"log";if(this.transports.has(t))return this.transports.get(t);if(!this.config){let i=new Ot;return this.transports.set(t,i),i}let s=this.config.mailers[t];if(!s)throw new Error(`Mailer "${t}" is not defined.`);let r;switch(s.driver){case"smtp":r=new Is(s);break;case"postmark":r=new js(s);break;case"resend":r=new Os(s);break;case"log":r=new Ot;break;case"null":r=new _s;break;case"custom":{if(!s.transport)throw new Error(`Custom mail driver "${t}" requires a "transport" instance.`);r=s.transport;break}default:throw new Error(`Unknown mail driver: ${s.driver}`)}return this.transports.set(t,r),r}},Us=w("svelar.mail",()=>new Ls)});import{createHmac as ue,randomBytes as Oe}from"crypto";function Pi(l){return(typeof l=="string"?Buffer.from(l):l).toString("base64url")}function Si(l){return Buffer.from(l,"base64url").toString("utf-8")}function Ri(l,e,t,s){return ue(s==="HS384"?"sha384":s==="HS512"?"sha512":"sha256",t).update(`${l}.${e}`).digest("base64url")}function Fs(l,e,t="HS256"){let s=Pi(JSON.stringify({alg:t,typ:"JWT"})),r=Pi(JSON.stringify(l)),i=Ri(s,r,e,t);return`${s}.${r}.${i}`}function Hs(l,e){let t=l.split(".");if(t.length!==3)return null;let[s,r,i]=t,n;try{n=JSON.parse(Si(s))}catch{return null}let a=Ri(s,r,e,n.alg);if(i!==a)return null;try{let o=JSON.parse(Si(r));return o.exp&&Date.now()/1e3>o.exp?null:o}catch{return null}}var qt,me,Ut,zs=y(()=>{"use strict";ae();qt=class{config;currentUser=null;constructor(e){this.config={identifierColumn:"email",passwordColumn:"password",...e}}async attempt(e,t){let{Hash:s}=await Promise.resolve().then(()=>(de(),Ie)),r=e[this.config.identifierColumn],i=e[this.config.passwordColumn];if(!r||!i)return null;let n=await this.config.model.where(this.config.identifierColumn,r).first();if(!n)return null;let a=n.getAttribute(this.config.passwordColumn);return await s.verify(i,a)?(this.currentUser=n,t&&(t.set("auth_user_id",n.getAttribute("id")),t.regenerateId()),n):null}async attemptJwt(e){let{Hash:t}=await Promise.resolve().then(()=>(de(),Ie));if(!this.config.jwt)throw new Error("JWT configuration required for JWT guard.");let s=e[this.config.identifierColumn],r=e[this.config.passwordColumn];if(!s||!r)return null;let i=await this.config.model.where(this.config.identifierColumn,s).first();if(!i)return null;let n=i.getAttribute(this.config.passwordColumn);return await t.verify(r,n)?(this.currentUser=i,this.issueTokenPair(i)):null}async issueTokenPair(e){let t=this.config.jwt,s=t.expiresIn??3600,r=Math.floor(Date.now()/1e3),i={sub:e.getAttribute("id"),iat:r,exp:r+s,...t.issuer?{iss:t.issuer}:{}},n=Fs(i,t.secret,t.algorithm),a=new Date((r+s)*1e3),o={user:e,token:n,expiresAt:a};if(t.refreshTokens){let c=t.refreshExpiresIn??604800,u=Oe(32).toString("base64url"),d=ue("sha256",t.secret).update(u).digest("hex"),p=new Date((r+c)*1e3),{Connection:f}=await Promise.resolve().then(()=>(x(),P)),b=t.refreshTable??"refresh_tokens";await f.raw(`INSERT INTO ${b} (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),d,p.toISOString(),new Date().toISOString()]),o.refreshToken=u,o.refreshExpiresAt=p}return o}async refreshJwt(e){if(!this.config.jwt)throw new Error("JWT configuration required.");if(!this.config.jwt.refreshTokens)throw new Error("Refresh tokens are not enabled. Set jwt.refreshTokens = true.");let t=this.config.jwt,s=ue("sha256",t.secret).update(e).digest("hex"),{Connection:r}=await Promise.resolve().then(()=>(x(),P)),i=t.refreshTable??"refresh_tokens",n=await r.raw(`SELECT user_id, expires_at, revoked_at FROM ${i} WHERE token = ?`,[s]);if(n.length===0)return null;let a=n[0];if(a.revoked_at||new Date(a.expires_at)<new Date)return null;await r.raw(`UPDATE ${i} SET revoked_at = ? WHERE token = ?`,[new Date().toISOString(),s]);let o=await this.config.model.find(a.user_id);return o?(this.currentUser=o,this.issueTokenPair(o)):null}async revokeRefreshTokens(e){if(!this.config.jwt?.refreshTokens)return;let{Connection:t}=await Promise.resolve().then(()=>(x(),P)),s=this.config.jwt.refreshTable??"refresh_tokens";await t.raw(`UPDATE ${s} SET revoked_at = ? WHERE user_id = ? AND revoked_at IS NULL`,[new Date().toISOString(),e])}async resolveFromToken(e){if(!this.config.jwt)throw new Error("JWT configuration required.");let t=Hs(e,this.config.jwt.secret);if(!t)return null;let s=await this.config.model.find(t.sub);return s&&(this.currentUser=s),s}async resolveFromSession(e){let t=e.get("auth_user_id");if(!t)return null;let s=await this.config.model.find(t);return s&&(this.currentUser=s),s}async register(e){let{Hash:t}=await Promise.resolve().then(()=>(de(),Ie));e[this.config.passwordColumn]&&(e[this.config.passwordColumn]=await t.make(e[this.config.passwordColumn]));let s=await this.config.model.create(e);return this.currentUser=s,s}async logout(e){this.currentUser=null,e&&(e.forget("auth_user_id"),e.regenerateId())}user(){return this.currentUser}check(){return this.currentUser!==null}id(){return this.currentUser?.getAttribute("id")??null}async generateApiToken(e,t="default"){let s=Oe(32).toString("hex"),r=this.config.jwt?.secret??process.env.APP_KEY;if(!r)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let i=ue("sha256",r).update(s).digest("hex"),{Connection:n}=await Promise.resolve().then(()=>(x(),P)),a=this.config.token?.table??"personal_access_tokens";return await n.raw(`INSERT INTO ${a} (user_id, name, token, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),t,i,new Date().toISOString()]),s}async resolveFromApiToken(e){let{Connection:t}=await Promise.resolve().then(()=>(x(),P)),s=this.config.token?.table??"personal_access_tokens",r=this.config.jwt?.secret??process.env.APP_KEY;if(!r)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let i=ue("sha256",r).update(e).digest("hex"),n=await t.raw(`SELECT user_id FROM ${s} WHERE token = ?`,[i]);if(n.length===0)return null;let a=await this.config.model.find(n[0].user_id);return a&&(this.currentUser=a),a}async sendPasswordReset(e){let t=await this.config.model.where(this.config.identifierColumn,e).first();if(!t)return!1;let{Connection:s}=await Promise.resolve().then(()=>(x(),P)),r=this.config.passwordResets?.table??"password_resets",i=this.config.passwordResets?.expiresIn??3600;await this.ensureTable(r,`
|
|
114
114
|
CREATE TABLE IF NOT EXISTS ${r} (
|
|
115
115
|
email TEXT NOT NULL,
|
|
116
116
|
token TEXT NOT NULL,
|
|
117
117
|
expires_at TEXT NOT NULL,
|
|
118
118
|
created_at TEXT NOT NULL
|
|
119
119
|
)
|
|
120
|
-
`),await s.raw(`DELETE FROM ${r} WHERE email = ?`,[e]);let
|
|
120
|
+
`),await s.raw(`DELETE FROM ${r} WHERE email = ?`,[e]);let n=Oe(32).toString("base64url"),a=this.hashToken(n),o=new Date(Date.now()+i*1e3).toISOString();await s.raw(`INSERT INTO ${r} (email, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e,a,o,new Date().toISOString()]);let c=this.config.appUrl??process.env.APP_URL??"http://localhost:5173",u=this.config.appName??process.env.APP_NAME??"Svelar",d=`${c}/reset-password?token=${n}&email=${encodeURIComponent(e)}`;return await this.sendAuthEmail("password-reset",e,{appName:u,"user.name":t.getAttribute("name")??e,resetUrl:d}),!0}async resetPassword(e,t,s){let{Connection:r}=await Promise.resolve().then(()=>(x(),P)),{Hash:i}=await Promise.resolve().then(()=>(de(),Ie)),n=this.config.passwordResets?.table??"password_resets",a=this.hashToken(e),o=await r.raw(`SELECT email, expires_at FROM ${n} WHERE token = ? AND email = ?`,[a,t]);if(o.length===0)return!1;let c=o[0];if(new Date(c.expires_at)<new Date)return await r.raw(`DELETE FROM ${n} WHERE email = ?`,[t]),!1;let u=await this.config.model.where(this.config.identifierColumn,t).first();if(!u)return!1;let d=await i.make(s);return await this.config.model.where("id",u.getAttribute("id")).update({[this.config.passwordColumn]:d}),await r.raw(`DELETE FROM ${n} WHERE email = ?`,[t]),await this.revokeRefreshTokens(u.getAttribute("id")),!0}async sendVerificationEmail(e){let{Connection:t}=await Promise.resolve().then(()=>(x(),P)),s=this.config.emailVerification?.table??"email_verifications",r=this.config.emailVerification?.expiresIn??86400,i=e.getAttribute(this.config.identifierColumn);await this.ensureTable(s,`
|
|
121
121
|
CREATE TABLE IF NOT EXISTS ${s} (
|
|
122
122
|
user_id TEXT NOT NULL,
|
|
123
123
|
token TEXT NOT NULL,
|
|
124
124
|
expires_at TEXT NOT NULL,
|
|
125
125
|
created_at TEXT NOT NULL
|
|
126
126
|
)
|
|
127
|
-
`),await t.raw(`DELETE FROM ${s} WHERE user_id = ?`,[e.getAttribute("id")]);let
|
|
128
|
-
CREATE TABLE IF NOT EXISTS ${
|
|
127
|
+
`),await t.raw(`DELETE FROM ${s} WHERE user_id = ?`,[e.getAttribute("id")]);let n=Oe(32).toString("base64url"),a=this.hashToken(n),o=new Date(Date.now()+r*1e3).toISOString();await t.raw(`INSERT INTO ${s} (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),a,o,new Date().toISOString()]);let u=`${this.config.appUrl??process.env.APP_URL??"http://localhost:5173"}/verify-email?token=${n}&id=${e.getAttribute("id")}`;await this.sendAuthEmail("email-verification",i,{"user.name":e.getAttribute("name")??i,verifyUrl:u})}async verifyEmail(e,t){let{Connection:s}=await Promise.resolve().then(()=>(x(),P)),r=this.config.emailVerification?.table??"email_verifications",i=this.config.emailVerification?.verifiedColumn??"email_verified_at",n=this.hashToken(e),a=await s.raw(`SELECT user_id, expires_at FROM ${r} WHERE token = ? AND user_id = ?`,[n,t]);return a.length===0?!1:new Date(a[0].expires_at)<new Date?(await s.raw(`DELETE FROM ${r} WHERE user_id = ?`,[t]),!1):(await this.config.model.where("id",t).update({[i]:new Date().toISOString()}),await s.raw(`DELETE FROM ${r} WHERE user_id = ?`,[t]),!0)}isEmailVerified(e){let t=this.config.emailVerification?.verifiedColumn??"email_verified_at";return!!e.getAttribute(t)}async sendOtp(e,t="login"){let s=await this.config.model.where(this.config.identifierColumn,e).first();if(!s)return!1;let{Connection:r}=await Promise.resolve().then(()=>(x(),P)),i=this.config.otp?.table??"otp_codes",n=this.config.otp?.expiresIn??600,a=this.config.otp?.length??6;await this.ensureTable(i,`
|
|
128
|
+
CREATE TABLE IF NOT EXISTS ${i} (
|
|
129
129
|
email TEXT NOT NULL,
|
|
130
130
|
code TEXT NOT NULL,
|
|
131
131
|
purpose TEXT NOT NULL DEFAULT 'login',
|
|
@@ -133,32 +133,35 @@ Access until: {{accessUntilDate}}`,variables:["user.name","plan.name","accessUnt
|
|
|
133
133
|
used_at TEXT,
|
|
134
134
|
created_at TEXT NOT NULL
|
|
135
135
|
)
|
|
136
|
-
`),await r.raw(`DELETE FROM ${
|
|
136
|
+
`),await r.raw(`DELETE FROM ${i} WHERE email = ? AND purpose = ?`,[e,t]);let o=this.generateOtpCode(a),c=this.hashToken(o),u=new Date(Date.now()+n*1e3).toISOString();await r.raw(`INSERT INTO ${i} (email, code, purpose, expires_at, created_at) VALUES (?, ?, ?, ?, ?)`,[e,c,t,u,new Date().toISOString()]);let d=this.config.appName??process.env.APP_NAME??"Svelar",p=Math.ceil(n/60);return await this.sendAuthEmail("otp-code",e,{appName:d,"user.name":s.getAttribute("name")??e,code:o,purpose:t,expiresMinutes:String(p)}),!0}async verifyOtp(e,t,s="login"){let{Connection:r}=await Promise.resolve().then(()=>(x(),P)),i=this.config.otp?.table??"otp_codes",n=this.hashToken(t),a=await r.raw(`SELECT email, expires_at, used_at FROM ${i} WHERE code = ? AND email = ? AND purpose = ? AND used_at IS NULL`,[n,e,s]);if(a.length===0)return null;if(new Date(a[0].expires_at)<new Date)return await r.raw(`DELETE FROM ${i} WHERE email = ? AND purpose = ?`,[e,s]),null;await r.raw(`UPDATE ${i} SET used_at = ? WHERE code = ? AND email = ? AND purpose = ?`,[new Date().toISOString(),n,e,s]);let o=await this.config.model.where(this.config.identifierColumn,e).first();return o&&(this.currentUser=o),o}async attemptOtp(e,t,s,r="login"){let i=await this.verifyOtp(e,t,r);return i?(s&&(s.set("auth_user_id",i.getAttribute("id")),s.regenerateId()),i):null}async cleanupExpiredTokens(){let{Connection:e}=await Promise.resolve().then(()=>(x(),P)),t=new Date().toISOString(),s=0,r=0,i=0,n=this.config.passwordResets?.table??"password_resets",a=this.config.emailVerification?.table??"email_verifications",o=this.config.otp?.table??"otp_codes";try{s=(await e.raw(`DELETE FROM ${n} WHERE expires_at < ?`,[t]))?.changes??0}catch{}try{r=(await e.raw(`DELETE FROM ${a} WHERE expires_at < ?`,[t]))?.changes??0}catch{}try{i=(await e.raw(`DELETE FROM ${o} WHERE expires_at < ? OR used_at IS NOT NULL`,[t]))?.changes??0}catch{}return{passwordResets:s,verifications:r,otpCodes:i}}hashToken(e){let t=this.config.jwt?.secret??process.env.APP_KEY;if(!t)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");return ue("sha256",t).update(e).digest("hex")}generateOtpCode(e){let t=Oe(e);return Array.from(t).map(s=>(s%10).toString()).join("")}tablesEnsured=new Set;async ensureTable(e,t){if(this.tablesEnsured.has(e))return;let{Connection:s}=await Promise.resolve().then(()=>(x(),P));await s.raw(t),this.tablesEnsured.add(e)}async sendAuthEmail(e,t,s){try{let{EmailTemplates:r}=await Promise.resolve().then(()=>(xi(),wi)),{Mailer:i}=await Promise.resolve().then(()=>(Lt(),Bs)),n=await r.render(e,s);await i.send({to:t,subject:n.subject,html:n.html,text:n.text})}catch(r){console.error(`[Auth] Failed to send ${e} email to ${t}:`,r.message)}}},me=class extends T{constructor(t){super();this.authManager=t}async handle(t,s){let r=null;if(t.event.locals.session&&(r=await this.authManager.resolveFromSession(t.event.locals.session)),!r){let i=t.event.request.headers.get("authorization");if(i?.startsWith("Bearer ")){let n=i.slice(7);try{r=await this.authManager.resolveFromToken(n)}catch{r=await this.authManager.resolveFromApiToken(n)}}}return t.event.locals.user=r,t.event.locals.auth=this.authManager,s()}},Ut=class extends T{async handle(e,t){return e.event.locals.user?t():new Response(JSON.stringify({message:"Unauthenticated"}),{status:401,headers:{"Content-Type":"application/json"}})}}});import{appendFile as Va,mkdir as Qa}from"fs/promises";import{dirname as Ga}from"path";function M(){return new Date().toISOString()}var Bt,Le,Ks,Ws,Js,Vs,Qs,Ft,Gs=y(()=>{"use strict";R();Bt={debug:0,info:1,warn:2,error:3,fatal:4},Le=class{minLevel;format;constructor(e){this.minLevel=e.level??"debug",this.format=e.format??"text"}write(e){if(Bt[e.level]<Bt[this.minLevel])return;if(this.format==="json"){console.log(JSON.stringify(e));return}let t={debug:"\x1B[90m",info:"\x1B[34m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},s="\x1B[0m",r=t[e.level]??"",i=e.level.toUpperCase().padEnd(5),n=Object.keys(e.context).length>0?` ${JSON.stringify(e.context)}`:"",a=e.level==="error"||e.level==="fatal"?"error":"log";console[a](`${r}[${e.timestamp}] ${i}${s} ${e.message}${n}`)}},Ks=class{minLevel;path;format;initialized=!1;constructor(e){this.minLevel=e.level??"info",this.path=e.path??"storage/logs/app.log",this.format=e.format??"text"}async write(e){if(Bt[e.level]<Bt[this.minLevel])return;this.initialized||(await Qa(Ga(this.path),{recursive:!0}),this.initialized=!0);let t;if(this.format==="json")t=JSON.stringify(e)+`
|
|
137
137
|
`;else{let s=e.level.toUpperCase().padEnd(5),r=Object.keys(e.context).length>0?` ${JSON.stringify(e.context)}`:"";t=`[${e.timestamp}] ${s} ${e.message}${r}
|
|
138
|
-
`}await
|
|
138
|
+
`}await Va(this.path,t)}},Ws=class{minLevel="debug";channelNames;resolver;constructor(e,t){this.channelNames=e.channels??[],this.resolver=t,this.minLevel=e.level??"debug"}async write(e){for(let t of this.channelNames){let s=this.resolver(t);s&&await s.write(e)}}},Js=class{minLevel="debug";write(){}},Vs=class{config={default:"console",channels:{console:{driver:"console",level:"debug"}}};channels=new Map;configure(e){this.config=e,this.channels.clear()}channel(e){return new Qs(this.resolveChannel(e))}debug(e,t={}){this.writeToDefault({level:"debug",message:e,context:t,timestamp:M()})}info(e,t={}){this.writeToDefault({level:"info",message:e,context:t,timestamp:M()})}warn(e,t={}){this.writeToDefault({level:"warn",message:e,context:t,timestamp:M()})}error(e,t={}){this.writeToDefault({level:"error",message:e,context:t,timestamp:M()})}fatal(e,t={}){this.writeToDefault({level:"fatal",message:e,context:t,timestamp:M()})}writeToDefault(e){this.resolveChannel(this.config.default).write(e)}resolveChannel(e){if(this.channels.has(e))return this.channels.get(e);let t=this.config.channels[e];if(!t){let r=new Le({driver:"console"});return this.channels.set(e,r),r}let s=this.createChannel(t);return this.channels.set(e,s),s}createChannel(e){switch(e.driver){case"console":return new Le(e);case"file":return new Ks(e);case"stack":return new Ws(e,t=>this.resolveChannel(t));case"null":return new Js;default:return new Le(e)}}},Qs=class{constructor(e){this.channel=e}debug(e,t={}){this.channel.write({level:"debug",message:e,context:t,timestamp:M()})}info(e,t={}){this.channel.write({level:"info",message:e,context:t,timestamp:M()})}warn(e,t={}){this.channel.write({level:"warn",message:e,context:t,timestamp:M()})}error(e,t={}){this.channel.write({level:"error",message:e,context:t,timestamp:M()})}fatal(e,t={}){this.channel.write({level:"fatal",message:e,context:t,timestamp:M()})}};Ft=w("svelar.log",()=>new Vs)});function Wt(l,e){throw new A(l,e??Ya(l))}function Ti(l,e,t){l&&Wt(e,t)}function Ei(l,e,t){l||Wt(e,t)}function Ya(l){return{400:"Bad request",401:"Unauthenticated",403:"Forbidden",404:"Not found",405:"Method not allowed",409:"Conflict",419:"Page expired",422:"Unprocessable entity",429:"Too many requests",500:"Internal server error",502:"Bad gateway",503:"Service unavailable",504:"Gateway timeout"}[l]??"An error occurred"}var A,Ht,Ys,Xs,zt,Kt,pe,Zs=y(()=>{"use strict";Gs();A=class extends Error{constructor(t,s,r){super(s);this.statusCode=t;this.details=r;this.name="HttpError"}},Ht=class extends A{constructor(e="The requested resource was not found"){super(404,e),this.name="NotFoundError"}},Ys=class extends A{constructor(e="Unauthenticated"){super(401,e),this.name="UnauthorizedError"}},Xs=class extends A{constructor(e="You do not have permission to perform this action"){super(403,e),this.name="ForbiddenError"}},zt=class extends A{constructor(t,s="The given data was invalid"){super(422,s,{errors:t});this.errors=t;this.name="ValidationError"}},Kt=class extends Ht{constructor(e,t){super(t?`${e} with ID ${t} not found`:`${e} not found`),this.name="ModelNotFoundError"}};pe=class{config;constructor(e={}){this.config={debug:process.env.NODE_ENV!=="production",dontReport:[zt,Ht,Ys,Xs],...e}}async handle(e,t){let s=e instanceof Error?e:new Error(String(e));return await this.reportError(s,t),this.config.render?this.config.render(s,t):this.renderError(s)}handleSvelteKitError(){return({error:e,event:t,status:s,message:r})=>{let i=e instanceof Error?e:new Error(String(e));return this.reportError(i,t),i instanceof A?{message:i.message,status:i.statusCode,...i.details??{},...this.config.debug?{stack:i.stack}:{}}:{message:this.config.debug?i.message:"An unexpected error occurred",status:s,...this.config.debug?{stack:i.stack}:{}}}}middleware(){let e=this;return async(t,s)=>{try{return await s()}catch(r){return e.handle(r,t.event)}}}async reportError(e,t){if(this.config.dontReport){for(let r of this.config.dontReport)if(e instanceof r)return}let s={error:e.name,...t?.url?{url:t.url.toString()}:{},...e.stack?{stack:e.stack}:{}};if(Ft.error(e.message,s),this.config.report)try{await this.config.report(e,t?{url:t.url?.toString()}:void 0)}catch{}}renderError(e){if(e instanceof A){let s={message:e.message};return e instanceof zt&&(s.errors=e.errors),e.details&&Object.assign(s,e.details),this.config.debug&&(s.exception=e.name,s.stack=e.stack?.split(`
|
|
139
139
|
`).map(r=>r.trim())),new Response(JSON.stringify(s),{status:e.statusCode,headers:{"Content-Type":"application/json"}})}let t={message:this.config.debug?e.message:"Internal server error"};return this.config.debug&&(t.exception=e.name,t.stack=e.stack?.split(`
|
|
140
|
-
`).map(s=>s.trim())),new Response(JSON.stringify(t),{status:500,headers:{"Content-Type":"application/json"}})}}});function Ai(l={}){let{auth:e,secret:t=process.env.APP_KEY||(()=>{throw new Error("APP_KEY is not set. Set it in your .env file.")})(),sessionStore:s,sessionLifetime:r=86400,rateLimit:n=100,rateLimitWindow:i=6e4,csrfPaths:a=["/api/"],csrfExcludePaths:o=["/api/webhooks"],authThrottleAttempts:c=5,authThrottleDecay:u=1,debug:d=process.env.NODE_ENV!=="production",middleware:p=[],namedMiddleware:f={},i18n:y,errorConfig:S={}}=l,$=[new ne,new re({maxRequests:n,windowMs:i}),new ie({onlyPaths:a,excludePaths:o}),new de({store:s??new z,secret:t,lifetime:r})];e&&$.push(new pe(e)),$.push(...p);let ls={"auth-throttle":new ae({maxAttempts:c,decayMinutes:u}),...f},ve=new he({debug:d,...S}),Be=sr({middleware:$,namedMiddleware:ls,onError:(we,M)=>ve.handle(we,M)}),be;if(y){let{paraglideMiddleware:we,getTextDirection:M=()=>"ltr"}=y;be=rr(async({event:K,resolve:yn})=>we(K.request,({request:vn,locale:Dr})=>(K.request=vn,yn(K,{transformPageChunk:({html:bn})=>bn.replace("%lang%",Dr).replace("%dir%",M(Dr))}))),Be)}else be=Be;return{handle:be,handleError:ve.handleSvelteKitError()}}function sr(l={}){let e=new q;if(l.middleware)for(let t of l.middleware)e.use(t);if(l.namedMiddleware)for(let[t,s]of Object.entries(l.namedMiddleware))e.register(t,s);return async function({event:s,resolve:r}){let n={event:s,params:s.params??{},locals:s.locals??{}};try{l.app&&!l.app.isBooted()&&await l.app.bootstrap();let i=await e.execute(n,async()=>r(s));return i instanceof Response?i:r(s)}catch(i){if(l.onError){let a=await l.onError(i,s);if(a instanceof Response)return a}return console.error("[Svelar] Unhandled error in hooks:",i),new Response(JSON.stringify({message:process.env.NODE_ENV==="production"?"Internal server error":i.message}),{status:500,headers:{"Content-Type":"application/json"}})}}}function rr(...l){return async function({event:t,resolve:s}){let r=s;for(let n=l.length-1;n>=0;n--){let i=l[n],a=r;r=o=>i({event:o,resolve:a})}return r(t)}}var ir=v(()=>{"use strict";oe();Ds();Ws();tr()});function Di(l,e){let t=process.env[l];return t===void 0?e!==void 0?e:"":t==="true"?!0:t==="false"?!1:t==="null"?null:/^\d+$/.test(t)?Number(t):t}var nr,Ni,_i=v(()=>{"use strict";T();nr=class{items=new Map;clear(){this.items.clear()}load(e){for(let[t,s]of Object.entries(e))this.set(t,s)}async loadFromDirectory(e){let{resolve:t,basename:s,extname:r}=await import("path"),{existsSync:n,readdirSync:i}=await import("fs"),{pathToFileURL:a}=await import("url"),o=t(e);if(!n(o))return[];let c=i(o).filter(d=>(d.endsWith(".ts")||d.endsWith(".js"))&&!d.startsWith(".")),u=[];for(let d of c){let p=s(d,r(d)),f=t(o,d);try{let S=await import(a(f).href),$=S.default??S.config??S;$&&typeof $=="object"&&!Array.isArray($)&&(this.set(p,$),u.push(p))}catch{}}return u}get(e,t){let s=e.split("."),r=this.items.get(s[0]);for(let n=1;n<s.length;n++){if(r==null)return t;r=r[s[n]]}return r??t}set(e,t){let s=e.split(".");if(s.length===1){this.items.set(e,t);return}let r=this.items.get(s[0]);(r===void 0||typeof r!="object")&&(r={},this.items.set(s[0],r));let n=r;for(let i=1;i<s.length-1;i++)(n[s[i]]===void 0||typeof n[s[i]]!="object")&&(n[s[i]]={}),n=n[s[i]];n[s[s.length-1]]=t}has(e){return this.get(e)!==void 0}all(){let e={};for(let[t,s]of this.items)e[t]=s;return e}},Ni=w("svelar.config",()=>new nr)});import{z as P}from"zod";function Oi(l,e){let t=l.safeParse(e);if(t.success)return{success:!0,data:t.data};let s={};for(let r of t.error.issues){let n=r.path.length>0?r.path:["_root"],i=s;for(let o=0;o<n.length-1;o++){let c=n[o];c in i||(i[c]={}),i=i[c]}let a=n[n.length-1];i[a]||(i[a]=[]),i[a].push(r.message)}return{success:!1,errors:s}}var Mi,ji=v(()=>{"use strict";Mi={required:()=>P.string().min(1,"This field is required"),email:()=>P.string().email("Must be a valid email address"),string:(l,e)=>{let t=P.string();return l!==void 0&&(t=t.min(l)),e!==void 0&&(t=t.max(e)),t},number:(l,e)=>{let t=P.number();return l!==void 0&&(t=t.min(l)),e!==void 0&&(t=t.max(e)),t},integer:()=>P.number().int(),boolean:()=>P.boolean(),date:()=>P.coerce.date(),url:()=>P.string().url(),uuid:()=>P.string().uuid(),enum:l=>P.enum(l),array:l=>P.array(l),nullable:l=>l.nullable(),optional:l=>l.optional(),confirmed:(l="password")=>P.object({[l]:P.string(),[`${l}_confirmation`]:P.string()}).refine(e=>e[l]===e[`${l}_confirmation`],{message:"Confirmation does not match",path:[`${l}_confirmation`]}),min:l=>P.number().min(l),max:l=>P.number().max(l),between:(l,e)=>P.number().min(l).max(e),regex:(l,e)=>P.string().regex(l,e),ip:()=>P.string().refine(l=>{let e=l.split(".");return e.length!==4?!1:e.every(t=>{let s=Number(t);return Number.isInteger(s)&&s>=0&&s<=255})},{message:"Must be a valid IP address"}),json:()=>P.string().refine(l=>{try{return JSON.parse(l),!0}catch{return!1}},{message:"Must be valid JSON"})}});var Ii={};E(Ii,{FormAuthorizationError:()=>fe,FormRequest:()=>Ue,FormValidationError:()=>ge});var Ue,ge,fe,ar=v(()=>{"use strict";Ue=class{authorize(e){return!0}messages(){return{}}attributes(){return{}}passedValidation(e){return e}failedValidation(e){throw new ge(e)}failedAuthorization(){throw new fe}async parseBody(e){let t=e.request.headers.get("content-type")??"";if(t.includes("application/json"))return e.request.json();if(t.includes("multipart/form-data")||t.includes("application/x-www-form-urlencoded")){let s=await e.request.formData();return Object.fromEntries(s)}return Object.fromEntries(e.url.searchParams)}static async validate(e){let t=new this;await t.authorize(e)||t.failedAuthorization();let r=await t.parseBody(e),n={...Object.fromEntries(e.url.searchParams),...e.params,...r},a=t.rules().safeParse(n);if(!a.success){let o={},c=t.messages(),u=t.attributes();for(let d of a.error.issues){let p=d.path.join("."),f=u[p]??p;o[f]||(o[f]=[]);let y=`${p}.${d.code}`,S=c[y]??c[p];o[f].push(S??d.message)}t.failedValidation(o)}return t.passedValidation(a.data)}},ge=class extends Error{constructor(t){super("The given data was invalid.");this.errors=t;this.name="FormValidationError"}statusCode=422;toResponse(){return new Response(JSON.stringify({message:this.message,errors:this.errors}),{status:422,headers:{"Content-Type":"application/json"}})}},fe=class extends Error{statusCode=403;constructor(e="This action is unauthorized."){super(e),this.name="FormAuthorizationError"}toResponse(){return new Response(JSON.stringify({message:this.message}),{status:403,headers:{"Content-Type":"application/json"}})}}});import{readFile as Li,writeFile as ro,unlink as io,mkdir as Fe,readdir as or,stat as qi,copyFile as no,rename as ao}from"fs/promises";import{existsSync as oo}from"fs";import{join as lo,dirname as Qt}from"path";var lr,Gt,cr,Ui,Fi=v(()=>{"use strict";T();lr=class{constructor(e){this.config=e;if(!e.root)throw new Error('Local disk requires a "root" path.')}resolve(e){return lo(this.config.root,e)}async get(e){return Li(this.resolve(e))}async getText(e){return Li(this.resolve(e),"utf-8")}async put(e,t){let s=this.resolve(e);await Fe(Qt(s),{recursive:!0}),await ro(s,t)}async append(e,t){let{appendFile:s}=await import("fs/promises"),r=this.resolve(e);await Fe(Qt(r),{recursive:!0}),await s(r,t)}async exists(e){return oo(this.resolve(e))}async delete(e){try{return await io(this.resolve(e)),!0}catch{return!1}}async copy(e,t){let s=this.resolve(t);await Fe(Qt(s),{recursive:!0}),await no(this.resolve(e),s)}async move(e,t){let s=this.resolve(t);await Fe(Qt(s),{recursive:!0}),await ao(this.resolve(e),s)}async files(e=""){let t=this.resolve(e);try{return(await or(t,{withFileTypes:!0})).filter(r=>r.isFile()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async allFiles(e=""){let t=[],s=this.resolve(e);try{let r=await or(s,{withFileTypes:!0});for(let n of r){let i=e?`${e}/${n.name}`:n.name;n.isFile()?t.push(i):n.isDirectory()&&t.push(...await this.allFiles(i))}}catch{}return t}async directories(e=""){let t=this.resolve(e);try{return(await or(t,{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async makeDirectory(e){await Fe(this.resolve(e),{recursive:!0})}async deleteDirectory(e){let{rm:t}=await import("fs/promises");await t(this.resolve(e),{recursive:!0,force:!0})}async size(e){return(await qi(this.resolve(e))).size}async lastModified(e){return(await qi(this.resolve(e))).mtime}url(e){return`${this.config.urlPrefix??""}/${e}`}},Gt=class{config;_client=null;_s3Module=null;constructor(e){if(!e.bucket)throw new Error('S3 disk requires a "bucket" name.');this.config=e}async getS3(){if(this._s3Module)return this._s3Module;try{return this._s3Module=await Function('return import("@aws-sdk/client-s3")')(),this._s3Module}catch{throw new Error("S3 storage driver requires @aws-sdk/client-s3. Install it with: npm install @aws-sdk/client-s3")}}async getClient(){if(this._client)return this._client;let e=await this.getS3();return this._client=new e.S3Client({region:this.config.region??"us-east-1",endpoint:this.config.endpoint,forcePathStyle:this.config.forcePathStyle??!0,credentials:{accessKeyId:this.config.accessKeyId??"",secretAccessKey:this.config.secretAccessKey??""}}),this._client}key(e){let t=this.config.prefix;return t?`${t}/${e}`:e}async get(e){let t=await this.getS3(),n=await(await(await this.getClient()).send(new t.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).Body.transformToByteArray();return Buffer.from(n)}async getText(e){return(await this.get(e)).toString("utf-8")}async put(e,t){let s=await this.getS3(),r=await this.getClient(),n=typeof t=="string"?Buffer.from(t,"utf-8"):t;await r.send(new s.PutObjectCommand({Bucket:this.config.bucket,Key:this.key(e),Body:n}))}async append(e,t){let s=null;try{s=await this.get(e)}catch{}let r=typeof t=="string"?Buffer.from(t,"utf-8"):t,n=s?Buffer.concat([s,r]):r;await this.put(e,n)}async exists(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async delete(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.DeleteObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async copy(e,t){let s=await this.getS3();await(await this.getClient()).send(new s.CopyObjectCommand({Bucket:this.config.bucket,CopySource:`${this.config.bucket}/${this.key(e)}`,Key:this.key(t)}))}async move(e,t){await this.copy(e,t),await this.delete(e)}async files(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).Contents??[]).map(i=>i.Key).filter(i=>i!==r).map(i=>{let a=this.config.prefix;return a?i.slice(a.length+1):i})}catch{return[]}}async allFiles(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:""),n=[],i;do{let a=await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,ContinuationToken:i}));for(let o of a.Contents??[]){let c=this.config.prefix,u=c?o.Key.slice(c.length+1):o.Key;u&&n.push(u)}i=a.IsTruncated?a.NextContinuationToken:void 0}while(i);return n}async directories(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).CommonPrefixes??[]).map(i=>{let a=this.config.prefix;return(a?i.Prefix.slice(a.length+1):i.Prefix).replace(/\/$/,"")}).filter(i=>i.length>0)}catch{return[]}}async makeDirectory(e){}async deleteDirectory(e){let t=await this.allFiles(e);for(let s of t)await this.delete(s)}async size(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).ContentLength??0}async lastModified(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).LastModified??new Date}url(e){let t=this.config.urlPrefix;if(t)return`${t}/${e}`;let s=this.config.endpoint??`https://s3.${this.config.region??"us-east-1"}.amazonaws.com`;return this.config.forcePathStyle!==!1?`${s}/${this.config.bucket}/${this.key(e)}`:`${s.replace("://",`://${this.config.bucket}.`)}/${this.key(e)}`}async temporaryUrl(e,t=3600){try{let s=await Function('return import("@aws-sdk/s3-request-presigner")')(),r=await this.getS3(),n=await this.getClient(),i=new r.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)});return await s.getSignedUrl(n,i,{expiresIn:t})}catch{throw new Error("Pre-signed URLs require @aws-sdk/s3-request-presigner. Install it with: npm install @aws-sdk/s3-request-presigner")}}async ensureBucket(){let e=await this.getS3(),t=await this.getClient();try{await t.send(new e.HeadBucketCommand({Bucket:this.config.bucket}))}catch{await t.send(new e.CreateBucketCommand({Bucket:this.config.bucket}))}}},cr=class{config=null;disks=new Map;configure(e){this.config=e}disk(e){let t=e??this.config?.default??"local";if(this.disks.has(t))return this.disks.get(t);if(!this.config)throw new Error("Storage not configured. Call Storage.configure() first.");let s=this.config.disks[t];if(!s)throw new Error(`Storage disk "${t}" is not defined.`);let r=this.createDisk(s);return this.disks.set(t,r),r}async get(e){return this.disk().get(e)}async getText(e){return this.disk().getText(e)}async put(e,t){return this.disk().put(e,t)}async append(e,t){return this.disk().append(e,t)}async exists(e){return this.disk().exists(e)}async delete(e){return this.disk().delete(e)}async copy(e,t){return this.disk().copy(e,t)}async move(e,t){return this.disk().move(e,t)}async files(e){return this.disk().files(e)}async allFiles(e){return this.disk().allFiles(e)}async directories(e){return this.disk().directories(e)}async makeDirectory(e){return this.disk().makeDirectory(e)}async deleteDirectory(e){return this.disk().deleteDirectory(e)}async size(e){return this.disk().size(e)}async lastModified(e){return this.disk().lastModified(e)}url(e){return this.disk().url(e)}createDisk(e){switch(e.driver){case"local":return new lr(e);case"s3":return new Gt(e);default:throw new Error(`Unknown storage driver: ${e.driver}`)}}s3Disk(e){let t=this.disk(e);if(!(t instanceof Gt))throw new Error(`Disk "${e??this.config?.default}" is not an S3 disk.`);return t}},Ui=w("svelar.storage",()=>new cr)});import{readFile as co,writeFile as uo,unlink as Bi,mkdir as Hi}from"fs/promises";import{join as mo,dirname as po}from"path";import{createHash as ho}from"crypto";var dr,ur,mr,pr,zi,Ki=v(()=>{"use strict";T();dr=class{store=new Map;async get(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),null):t.value:null}async put(e,t,s){this.store.set(e,{value:t,expiresAt:s?Date.now()+s*1e3:null})}async forget(e){return this.store.delete(e)}async flush(){this.store.clear()}async has(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),!1):!0:!1}async increment(e,t=1){let r=(await this.get(e)??0)+t,n=this.store.get(e);return await this.put(e,r,n?.expiresAt?Math.ceil((n.expiresAt-Date.now())/1e3):void 0),r}async decrement(e,t=1){return this.increment(e,-t)}},ur=class{basePath;constructor(e){this.basePath=e.path??"storage/cache"}filePath(e){let t=ho("md5").update(e).digest("hex");return mo(this.basePath,t.slice(0,2),t)}async get(e){let t=this.filePath(e);try{let s=await co(t,"utf-8"),r=JSON.parse(s);return r.expiresAt&&Date.now()>r.expiresAt?(await Bi(t).catch(()=>{}),null):r.value}catch{return null}}async put(e,t,s){let r=this.filePath(e),n={value:t,expiresAt:s?Date.now()+s*1e3:null};await Hi(po(r),{recursive:!0}),await uo(r,JSON.stringify(n))}async forget(e){try{return await Bi(this.filePath(e)),!0}catch{return!1}}async flush(){let{rm:e}=await import("fs/promises");await e(this.basePath,{recursive:!0,force:!0}),await Hi(this.basePath,{recursive:!0})}async has(e){return await this.get(e)!==null}async increment(e,t=1){let r=(await this.get(e)??0)+t;return await this.put(e,r),r}async decrement(e,t=1){return this.increment(e,-t)}},mr=class{async get(){return null}async put(){}async forget(){return!0}async flush(){}async has(){return!1}async increment(){return 0}async decrement(){return 0}},pr=class{config={default:"memory",stores:{memory:{driver:"memory"}}};stores=new Map;configure(e){this.config=e,this.stores.clear()}store(e){let t=e??this.config.default;if(this.stores.has(t))return this.stores.get(t);let s=this.config.stores[t];if(!s)throw new Error(`Cache store "${t}" is not defined.`);let r=this.createStore(s);return this.stores.set(t,r),r}async get(e,t){let s=this.store();return await s.has(e)?s.get(e):t??null}async put(e,t,s){return this.store().put(e,t,s??this.config.stores[this.config.default]?.ttl)}async forget(e){return this.store().forget(e)}async flush(){return this.store().flush()}async has(e){return this.store().has(e)}async increment(e,t){return this.store().increment(e,t)}async decrement(e,t){return this.store().decrement(e,t)}async remember(e,t,s){let r=await this.store().get(e);if(r!==null)return r;let n=await s();return await this.store().put(e,n,t),n}async rememberForever(e,t){let s=await this.store().get(e);if(s!==null)return s;let r=await t();return await this.store().put(e,r),r}async pull(e,t){let s=await this.get(e,t);return await this.forget(e),s}createStore(e){switch(e.driver){case"memory":return new dr;case"file":return new ur(e);case"null":return new mr;case"redis":throw new Error("Redis cache requires ioredis. Install: npm install ioredis");default:throw new Error(`Unknown cache driver: ${e.driver}`)}}},zi=w("svelar.cache",()=>new pr)});var Yt,hr,gr,fr,Wi,Ji=v(()=>{"use strict";T();Yt=class{},hr=class{async send(e,t){if(!t.toMail)return;let s=t.toMail(e),r=e.routeNotificationForMail?.()??e.getAttribute("email");if(!r){console.warn("[Notifications] No email address for notifiable.");return}try{let{Mailer:n}=await Promise.resolve().then(()=>(Ut(),Hs));await n.send({to:r,subject:s.subject,html:s.html,text:s.text,from:s.from})}catch(n){console.error("[Notifications] Failed to send mail notification:",n)}}},gr=class{table;constructor(e="notifications"){this.table=e}async send(e,t){if(!t.toDatabase)return;let s=t.toDatabase(e);try{let{Connection:r}=await Promise.resolve().then(()=>(x(),C));await r.raw(`INSERT INTO ${this.table} (id, notifiable_id, type, data, read_at, created_at)
|
|
141
|
-
VALUES (?, ?, ?, ?, NULL, ?)`,[crypto.randomUUID(),e.getAttribute("id"),s.type,JSON.stringify(s.data),new Date().toISOString()])}catch(r){console.error("[Notifications] Failed to store database notification:",r)}}},
|
|
142
|
-
data: ${JSON.stringify(
|
|
140
|
+
`).map(s=>s.trim())),new Response(JSON.stringify(t),{status:500,headers:{"Content-Type":"application/json"}})}}});function ki(l={}){let{auth:e,secret:t=(()=>{throw new Error("APP_KEY is not set. Pass `secret` to createSvelarApp() \u2014 e.g. secret: env.APP_KEY (from $env/dynamic/private).")})(),sessionStore:s,sessionLifetime:r=86400,rateLimit:i=100,rateLimitWindow:n=6e4,csrfPaths:a=["/api/"],csrfExcludePaths:o=["/api/webhooks"],authThrottleAttempts:c=5,authThrottleDecay:u=1,debug:d=process.env.NODE_ENV!=="production",middleware:p=[],namedMiddleware:f={},i18n:b,errorConfig:C={}}=l,k=[new ie,new se({maxRequests:i,windowMs:n}),new re({onlyPaths:a,excludePaths:o}),new ce({store:s??new H,secret:t,lifetime:r})];e&&k.push(new me(e)),k.push(...p);let as={"auth-throttle":new ne({maxAttempts:c,decayMinutes:u}),...f},ye=new pe({debug:d,...C}),Be=er({middleware:k,namedMiddleware:as,onError:(be,N)=>ye.handle(be,N)}),ve;if(b){let{paraglideMiddleware:be,getTextDirection:N=()=>"ltr"}=b;ve=tr(async({event:z,resolve:gn})=>be(z.request,({request:fn,locale:$r})=>(z.request=fn,gn(z,{transformPageChunk:({html:yn})=>yn.replace("%lang%",$r).replace("%dir%",N($r))}))),Be)}else ve=Be;return{handle:ve,handleError:ye.handleSvelteKitError()}}function er(l={}){let e=new L;if(l.middleware)for(let t of l.middleware)e.use(t);if(l.namedMiddleware)for(let[t,s]of Object.entries(l.namedMiddleware))e.register(t,s);return async function({event:s,resolve:r}){let i={event:s,params:s.params??{},locals:s.locals??{}};try{l.app&&!l.app.isBooted()&&await l.app.bootstrap();let n=await e.execute(i,async()=>r(s));return n instanceof Response?n:r(s)}catch(n){if(l.onError){let a=await l.onError(n,s);if(a instanceof Response)return a}return console.error("[Svelar] Unhandled error in hooks:",n),new Response(JSON.stringify({message:process.env.NODE_ENV==="production"?"Internal server error":n.message}),{status:500,headers:{"Content-Type":"application/json"}})}}}function tr(...l){return async function({event:t,resolve:s}){let r=s;for(let i=l.length-1;i>=0;i--){let n=l[i],a=r;r=o=>n({event:o,resolve:a})}return r(t)}}var sr=y(()=>{"use strict";ae();$s();zs();Zs()});function $i(l,e){let t=process.env[l];return t===void 0?e!==void 0?e:"":t==="true"?!0:t==="false"?!1:t==="null"?null:/^\d+$/.test(t)?Number(t):t}var rr,Ai,Di=y(()=>{"use strict";R();rr=class{items=new Map;clear(){this.items.clear()}load(e){for(let[t,s]of Object.entries(e))this.set(t,s)}async loadFromDirectory(e){let{resolve:t,basename:s,extname:r}=await import("path"),{existsSync:i,readdirSync:n}=await import("fs"),{pathToFileURL:a}=await import("url"),o=t(e);if(!i(o))return[];let c=n(o).filter(d=>(d.endsWith(".ts")||d.endsWith(".js"))&&!d.startsWith(".")),u=[];for(let d of c){let p=s(d,r(d)),f=t(o,d);try{let C=await import(a(f).href),k=C.default??C.config??C;k&&typeof k=="object"&&!Array.isArray(k)&&(this.set(p,k),u.push(p))}catch{}}return u}get(e,t){let s=e.split("."),r=this.items.get(s[0]);for(let i=1;i<s.length;i++){if(r==null)return t;r=r[s[i]]}return r??t}set(e,t){let s=e.split(".");if(s.length===1){this.items.set(e,t);return}let r=this.items.get(s[0]);(r===void 0||typeof r!="object")&&(r={},this.items.set(s[0],r));let i=r;for(let n=1;n<s.length-1;n++)(i[s[n]]===void 0||typeof i[s[n]]!="object")&&(i[s[n]]={}),i=i[s[n]];i[s[s.length-1]]=t}has(e){return this.get(e)!==void 0}all(){let e={};for(let[t,s]of this.items)e[t]=s;return e}},Ai=w("svelar.config",()=>new rr)});import{z as S}from"zod";function Ni(l,e){let t=l.safeParse(e);if(t.success)return{success:!0,data:t.data};let s={};for(let r of t.error.issues){let i=r.path.length>0?r.path:["_root"],n=s;for(let o=0;o<i.length-1;o++){let c=i[o];c in n||(n[c]={}),n=n[c]}let a=i[i.length-1];n[a]||(n[a]=[]),n[a].push(r.message)}return{success:!1,errors:s}}var Mi,_i=y(()=>{"use strict";Mi={required:()=>S.string().min(1,"This field is required"),email:()=>S.string().email("Must be a valid email address"),string:(l,e)=>{let t=S.string();return l!==void 0&&(t=t.min(l)),e!==void 0&&(t=t.max(e)),t},number:(l,e)=>{let t=S.number();return l!==void 0&&(t=t.min(l)),e!==void 0&&(t=t.max(e)),t},integer:()=>S.number().int(),boolean:()=>S.boolean(),date:()=>S.coerce.date(),url:()=>S.string().url(),uuid:()=>S.string().uuid(),enum:l=>S.enum(l),array:l=>S.array(l),nullable:l=>l.nullable(),optional:l=>l.optional(),confirmed:(l="password")=>S.object({[l]:S.string(),[`${l}_confirmation`]:S.string()}).refine(e=>e[l]===e[`${l}_confirmation`],{message:"Confirmation does not match",path:[`${l}_confirmation`]}),min:l=>S.number().min(l),max:l=>S.number().max(l),between:(l,e)=>S.number().min(l).max(e),regex:(l,e)=>S.string().regex(l,e),ip:()=>S.string().refine(l=>{let e=l.split(".");return e.length!==4?!1:e.every(t=>{let s=Number(t);return Number.isInteger(s)&&s>=0&&s<=255})},{message:"Must be a valid IP address"}),json:()=>S.string().refine(l=>{try{return JSON.parse(l),!0}catch{return!1}},{message:"Must be valid JSON"})}});var Ii={};E(Ii,{FormAuthorizationError:()=>ge,FormRequest:()=>qe,FormValidationError:()=>he});var qe,he,ge,ir=y(()=>{"use strict";qe=class{authorize(e){return!0}messages(){return{}}attributes(){return{}}passedValidation(e){return e}failedValidation(e){throw new he(e)}failedAuthorization(){throw new ge}async parseBody(e){let t=e.request.headers.get("content-type")??"";if(t.includes("application/json"))return e.request.json();if(t.includes("multipart/form-data")||t.includes("application/x-www-form-urlencoded")){let s=await e.request.formData();return Object.fromEntries(s)}return Object.fromEntries(e.url.searchParams)}static async validate(e){let t=new this;await t.authorize(e)||t.failedAuthorization();let r=await t.parseBody(e),i={...Object.fromEntries(e.url.searchParams),...e.params,...r},a=t.rules().safeParse(i);if(!a.success){let o={},c=t.messages(),u=t.attributes();for(let d of a.error.issues){let p=d.path.join("."),f=u[p]??p;o[f]||(o[f]=[]);let b=`${p}.${d.code}`,C=c[b]??c[p];o[f].push(C??d.message)}t.failedValidation(o)}return t.passedValidation(a.data)}},he=class extends Error{constructor(t){super("The given data was invalid.");this.errors=t;this.name="FormValidationError"}statusCode=422;toResponse(){return new Response(JSON.stringify({message:this.message,errors:this.errors}),{status:422,headers:{"Content-Type":"application/json"}})}},ge=class extends Error{statusCode=403;constructor(e="This action is unauthorized."){super(e),this.name="FormAuthorizationError"}toResponse(){return new Response(JSON.stringify({message:this.message}),{status:403,headers:{"Content-Type":"application/json"}})}}});import{readFile as ji,writeFile as Xa,unlink as Za,mkdir as Ue,readdir as nr,stat as Oi,copyFile as eo,rename as to}from"fs/promises";import{existsSync as so}from"fs";import{join as ro,dirname as Jt}from"path";var ar,Vt,or,Li,qi=y(()=>{"use strict";R();ar=class{constructor(e){this.config=e;if(!e.root)throw new Error('Local disk requires a "root" path.')}resolve(e){return ro(this.config.root,e)}async get(e){return ji(this.resolve(e))}async getText(e){return ji(this.resolve(e),"utf-8")}async put(e,t){let s=this.resolve(e);await Ue(Jt(s),{recursive:!0}),await Xa(s,t)}async append(e,t){let{appendFile:s}=await import("fs/promises"),r=this.resolve(e);await Ue(Jt(r),{recursive:!0}),await s(r,t)}async exists(e){return so(this.resolve(e))}async delete(e){try{return await Za(this.resolve(e)),!0}catch{return!1}}async copy(e,t){let s=this.resolve(t);await Ue(Jt(s),{recursive:!0}),await eo(this.resolve(e),s)}async move(e,t){let s=this.resolve(t);await Ue(Jt(s),{recursive:!0}),await to(this.resolve(e),s)}async files(e=""){let t=this.resolve(e);try{return(await nr(t,{withFileTypes:!0})).filter(r=>r.isFile()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async allFiles(e=""){let t=[],s=this.resolve(e);try{let r=await nr(s,{withFileTypes:!0});for(let i of r){let n=e?`${e}/${i.name}`:i.name;i.isFile()?t.push(n):i.isDirectory()&&t.push(...await this.allFiles(n))}}catch{}return t}async directories(e=""){let t=this.resolve(e);try{return(await nr(t,{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>e?`${e}/${r.name}`:r.name)}catch{return[]}}async makeDirectory(e){await Ue(this.resolve(e),{recursive:!0})}async deleteDirectory(e){let{rm:t}=await import("fs/promises");await t(this.resolve(e),{recursive:!0,force:!0})}async size(e){return(await Oi(this.resolve(e))).size}async lastModified(e){return(await Oi(this.resolve(e))).mtime}url(e){return`${this.config.urlPrefix??""}/${e}`}},Vt=class{config;_client=null;_s3Module=null;constructor(e){if(!e.bucket)throw new Error('S3 disk requires a "bucket" name.');this.config=e}async getS3(){if(this._s3Module)return this._s3Module;try{return this._s3Module=await Function('return import("@aws-sdk/client-s3")')(),this._s3Module}catch{throw new Error("S3 storage driver requires @aws-sdk/client-s3. Install it with: npm install @aws-sdk/client-s3")}}async getClient(){if(this._client)return this._client;let e=await this.getS3();return this._client=new e.S3Client({region:this.config.region??"us-east-1",endpoint:this.config.endpoint,forcePathStyle:this.config.forcePathStyle??!0,credentials:{accessKeyId:this.config.accessKeyId??"",secretAccessKey:this.config.secretAccessKey??""}}),this._client}key(e){let t=this.config.prefix;return t?`${t}/${e}`:e}async get(e){let t=await this.getS3(),i=await(await(await this.getClient()).send(new t.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).Body.transformToByteArray();return Buffer.from(i)}async getText(e){return(await this.get(e)).toString("utf-8")}async put(e,t){let s=await this.getS3(),r=await this.getClient(),i=typeof t=="string"?Buffer.from(t,"utf-8"):t;await r.send(new s.PutObjectCommand({Bucket:this.config.bucket,Key:this.key(e),Body:i}))}async append(e,t){let s=null;try{s=await this.get(e)}catch{}let r=typeof t=="string"?Buffer.from(t,"utf-8"):t,i=s?Buffer.concat([s,r]):r;await this.put(e,i)}async exists(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async delete(e){let t=await this.getS3(),s=await this.getClient();try{return await s.send(new t.DeleteObjectCommand({Bucket:this.config.bucket,Key:this.key(e)})),!0}catch{return!1}}async copy(e,t){let s=await this.getS3();await(await this.getClient()).send(new s.CopyObjectCommand({Bucket:this.config.bucket,CopySource:`${this.config.bucket}/${this.key(e)}`,Key:this.key(t)}))}async move(e,t){await this.copy(e,t),await this.delete(e)}async files(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).Contents??[]).map(n=>n.Key).filter(n=>n!==r).map(n=>{let a=this.config.prefix;return a?n.slice(a.length+1):n})}catch{return[]}}async allFiles(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:""),i=[],n;do{let a=await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,ContinuationToken:n}));for(let o of a.Contents??[]){let c=this.config.prefix,u=c?o.Key.slice(c.length+1):o.Key;u&&i.push(u)}n=a.IsTruncated?a.NextContinuationToken:void 0}while(n);return i}async directories(e=""){let t=await this.getS3(),s=await this.getClient(),r=this.key(e?`${e}/`:"");try{return((await s.send(new t.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:r,Delimiter:"/"}))).CommonPrefixes??[]).map(n=>{let a=this.config.prefix;return(a?n.Prefix.slice(a.length+1):n.Prefix).replace(/\/$/,"")}).filter(n=>n.length>0)}catch{return[]}}async makeDirectory(e){}async deleteDirectory(e){let t=await this.allFiles(e);for(let s of t)await this.delete(s)}async size(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).ContentLength??0}async lastModified(e){let t=await this.getS3();return(await(await this.getClient()).send(new t.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(e)}))).LastModified??new Date}url(e){let t=this.config.urlPrefix;if(t)return`${t}/${e}`;let s=this.config.endpoint??`https://s3.${this.config.region??"us-east-1"}.amazonaws.com`;return this.config.forcePathStyle!==!1?`${s}/${this.config.bucket}/${this.key(e)}`:`${s.replace("://",`://${this.config.bucket}.`)}/${this.key(e)}`}async temporaryUrl(e,t=3600){try{let s=await Function('return import("@aws-sdk/s3-request-presigner")')(),r=await this.getS3(),i=await this.getClient(),n=new r.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(e)});return await s.getSignedUrl(i,n,{expiresIn:t})}catch{throw new Error("Pre-signed URLs require @aws-sdk/s3-request-presigner. Install it with: npm install @aws-sdk/s3-request-presigner")}}async ensureBucket(){let e=await this.getS3(),t=await this.getClient();try{await t.send(new e.HeadBucketCommand({Bucket:this.config.bucket}))}catch{await t.send(new e.CreateBucketCommand({Bucket:this.config.bucket}))}}},or=class{config=null;disks=new Map;configure(e){this.config=e}disk(e){let t=e??this.config?.default??"local";if(this.disks.has(t))return this.disks.get(t);if(!this.config)throw new Error("Storage not configured. Call Storage.configure() first.");let s=this.config.disks[t];if(!s)throw new Error(`Storage disk "${t}" is not defined.`);let r=this.createDisk(s);return this.disks.set(t,r),r}async get(e){return this.disk().get(e)}async getText(e){return this.disk().getText(e)}async put(e,t){return this.disk().put(e,t)}async append(e,t){return this.disk().append(e,t)}async exists(e){return this.disk().exists(e)}async delete(e){return this.disk().delete(e)}async copy(e,t){return this.disk().copy(e,t)}async move(e,t){return this.disk().move(e,t)}async files(e){return this.disk().files(e)}async allFiles(e){return this.disk().allFiles(e)}async directories(e){return this.disk().directories(e)}async makeDirectory(e){return this.disk().makeDirectory(e)}async deleteDirectory(e){return this.disk().deleteDirectory(e)}async size(e){return this.disk().size(e)}async lastModified(e){return this.disk().lastModified(e)}url(e){return this.disk().url(e)}createDisk(e){switch(e.driver){case"local":return new ar(e);case"s3":return new Vt(e);default:throw new Error(`Unknown storage driver: ${e.driver}`)}}s3Disk(e){let t=this.disk(e);if(!(t instanceof Vt))throw new Error(`Disk "${e??this.config?.default}" is not an S3 disk.`);return t}},Li=w("svelar.storage",()=>new or)});import{readFile as io,writeFile as no,unlink as Ui,mkdir as Bi}from"fs/promises";import{join as ao,dirname as oo}from"path";import{createHash as lo}from"crypto";var lr,cr,dr,ur,Fi,Hi=y(()=>{"use strict";R();lr=class{store=new Map;async get(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),null):t.value:null}async put(e,t,s){this.store.set(e,{value:t,expiresAt:s?Date.now()+s*1e3:null})}async forget(e){return this.store.delete(e)}async flush(){this.store.clear()}async has(e){let t=this.store.get(e);return t?t.expiresAt&&Date.now()>t.expiresAt?(this.store.delete(e),!1):!0:!1}async increment(e,t=1){let r=(await this.get(e)??0)+t,i=this.store.get(e);return await this.put(e,r,i?.expiresAt?Math.ceil((i.expiresAt-Date.now())/1e3):void 0),r}async decrement(e,t=1){return this.increment(e,-t)}},cr=class{basePath;constructor(e){this.basePath=e.path??"storage/cache"}filePath(e){let t=lo("md5").update(e).digest("hex");return ao(this.basePath,t.slice(0,2),t)}async get(e){let t=this.filePath(e);try{let s=await io(t,"utf-8"),r=JSON.parse(s);return r.expiresAt&&Date.now()>r.expiresAt?(await Ui(t).catch(()=>{}),null):r.value}catch{return null}}async put(e,t,s){let r=this.filePath(e),i={value:t,expiresAt:s?Date.now()+s*1e3:null};await Bi(oo(r),{recursive:!0}),await no(r,JSON.stringify(i))}async forget(e){try{return await Ui(this.filePath(e)),!0}catch{return!1}}async flush(){let{rm:e}=await import("fs/promises");await e(this.basePath,{recursive:!0,force:!0}),await Bi(this.basePath,{recursive:!0})}async has(e){return await this.get(e)!==null}async increment(e,t=1){let r=(await this.get(e)??0)+t;return await this.put(e,r),r}async decrement(e,t=1){return this.increment(e,-t)}},dr=class{async get(){return null}async put(){}async forget(){return!0}async flush(){}async has(){return!1}async increment(){return 0}async decrement(){return 0}},ur=class{config={default:"memory",stores:{memory:{driver:"memory"}}};stores=new Map;configure(e){this.config=e,this.stores.clear()}store(e){let t=e??this.config.default;if(this.stores.has(t))return this.stores.get(t);let s=this.config.stores[t];if(!s)throw new Error(`Cache store "${t}" is not defined.`);let r=this.createStore(s);return this.stores.set(t,r),r}async get(e,t){let s=this.store();return await s.has(e)?s.get(e):t??null}async put(e,t,s){return this.store().put(e,t,s??this.config.stores[this.config.default]?.ttl)}async forget(e){return this.store().forget(e)}async flush(){return this.store().flush()}async has(e){return this.store().has(e)}async increment(e,t){return this.store().increment(e,t)}async decrement(e,t){return this.store().decrement(e,t)}async remember(e,t,s){let r=await this.store().get(e);if(r!==null)return r;let i=await s();return await this.store().put(e,i,t),i}async rememberForever(e,t){let s=await this.store().get(e);if(s!==null)return s;let r=await t();return await this.store().put(e,r),r}async pull(e,t){let s=await this.get(e,t);return await this.forget(e),s}createStore(e){switch(e.driver){case"memory":return new lr;case"file":return new cr(e);case"null":return new dr;case"redis":throw new Error("Redis cache requires ioredis. Install: npm install ioredis");default:throw new Error(`Unknown cache driver: ${e.driver}`)}}},Fi=w("svelar.cache",()=>new ur)});var Qt,mr,pr,hr,zi,Ki=y(()=>{"use strict";R();Qt=class{},mr=class{async send(e,t){if(!t.toMail)return;let s=t.toMail(e),r=e.routeNotificationForMail?.()??e.getAttribute("email");if(!r){console.warn("[Notifications] No email address for notifiable.");return}try{let{Mailer:i}=await Promise.resolve().then(()=>(Lt(),Bs));await i.send({to:r,subject:s.subject,html:s.html,text:s.text,from:s.from})}catch(i){console.error("[Notifications] Failed to send mail notification:",i)}}},pr=class{table;constructor(e="notifications"){this.table=e}async send(e,t){if(!t.toDatabase)return;let s=t.toDatabase(e);try{let{Connection:r}=await Promise.resolve().then(()=>(x(),P));await r.raw(`INSERT INTO ${this.table} (id, notifiable_id, type, data, read_at, created_at)
|
|
141
|
+
VALUES (?, ?, ?, ?, NULL, ?)`,[crypto.randomUUID(),e.getAttribute("id"),s.type,JSON.stringify(s.data),new Date().toISOString()])}catch(r){console.error("[Notifications] Failed to store database notification:",r)}}},hr=class{channels=new Map;constructor(){this.channels.set("mail",new mr),this.channels.set("database",new pr)}extend(e,t){this.channels.set(e,t)}async send(e,t){let s=Array.isArray(e)?e:[e];for(let r of s){let i=t.via(r);for(let n of i){let a=this.channels.get(n);if(a)try{await a.send(r,t)}catch(o){console.error(`[Notifications] Channel "${n}" failed:`,o)}else console.warn(`[Notifications] Unknown channel: ${n}`)}}}async sendVia(e,t,s){for(let r of s){let i=this.channels.get(r);i&&await i.send(e,t)}}},zi=w("svelar.notifier",()=>new hr)});function gr(l){return l.startsWith("private-")?"private":l.startsWith("presence-")?"presence":"public"}var fr,Gt,yr,vr,Wi,Ji=y(()=>{"use strict";R();fr=class{subscribers=[];name;type;constructor(e){this.name=e,this.type=gr(e)}stream(e,t){let s=this,r=new ReadableStream({start(i){let n={channel:s.name};s.type==="presence"&&(n.members=s.getMembers());let a=`event: connected
|
|
142
|
+
data: ${JSON.stringify(n)}
|
|
143
143
|
id: ${Date.now()}
|
|
144
144
|
|
|
145
|
-
`;
|
|
145
|
+
`;i.enqueue(new TextEncoder().encode(a));let o={controller:i,userId:e,userInfo:t};s.subscribers.push(o),s.type==="presence"&&e!==void 0&&s.sendInternal("member:joined",{id:e,...t},o)},cancel(){let i=s.subscribers.findIndex(a=>a.controller===this._controller),n=s.subscribers.length;s.subscribers=s.subscribers.filter(a=>{try{return a.controller.enqueue(new TextEncoder().encode(`:
|
|
146
146
|
|
|
147
|
-
`)),!0}catch{return!1}}),s.type==="presence"&&e!==void 0&&s.subscribers.length<
|
|
147
|
+
`)),!0}catch{return!1}}),s.type==="presence"&&e!==void 0&&s.subscribers.length<n&&s.sendInternal("member:left",{id:e,...t})}});return new Response(r,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}})}send(e,t,s){let r=`event: ${e}
|
|
148
148
|
data: ${JSON.stringify(t)}
|
|
149
149
|
id: ${Date.now()}
|
|
150
150
|
|
|
151
|
-
`,
|
|
151
|
+
`,i=new TextEncoder().encode(r);for(let n of this.subscribers)if(!(s!==void 0&&n.userId!==s))try{n.controller.enqueue(i)}catch{}}sendInternal(e,t,s){let r=`event: ${e}
|
|
152
152
|
data: ${JSON.stringify(t)}
|
|
153
153
|
id: ${Date.now()}
|
|
154
154
|
|
|
155
|
-
`,
|
|
155
|
+
`,i=new TextEncoder().encode(r);for(let n of this.subscribers)if(n!==s)try{n.controller.enqueue(i)}catch{}}toUser(e){return{send:(t,s)=>this.send(t,s,e)}}subscriberCount(){return this.subscribers.length}getMembers(){return this.type!=="presence"?[]:this.subscribers.filter(e=>e.userId!==void 0).map(e=>({id:e.userId,...e.userInfo??{}}))}hasMember(e){return this.subscribers.some(t=>t.userId===e)}whisper(e,t,s){let r=`event: client-${e}
|
|
156
156
|
data: ${JSON.stringify(t)}
|
|
157
157
|
id: ${Date.now()}
|
|
158
158
|
|
|
159
|
-
`,
|
|
160
|
-
`),c=await this.hmacSha256(this.config.secret,o),u=this.config.useTLS!==!1?"https":"http",d=this.config.host??`api-${this.config.cluster??"mt1"}.pusher.com`,p=this.config.port??(this.config.useTLS!==!1?443:80),f=`${u}://${d}:${p}/apps/${this.config.appId}/events?auth_key=${this.config.key}&auth_timestamp=${
|
|
161
|
-
`);Zt("warning",e[422]??"Validation Error",a);return}}}catch{}let s=t||e[l.status]||`Error ${l.status}`,r=l.status>=500?"error":l.status===429?"warning":"error";if(l.status===401){Zt("warning",s,"Please sign in to continue.");return}Zt(r,s)}function Cr(l="XSRF-TOKEN"){if(typeof document>"u")return null;let e=l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),t=document.cookie.match(new RegExp(`(?:^|;\\s*)${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}function Xi(l,e){if(!e)return l;let t=new URL(l,"http://localhost");for(let[s,r]of Object.entries(e))r!=null&&t.searchParams.set(s,String(r));return t.pathname+t.search}var go,Gi,xr,ye,Tu,Zi=v(()=>{"use strict";go={400:"Invalid request. Please check your input.",401:"Your session has expired. Please sign in again.",403:"You don't have permission to do this.",404:"The requested resource was not found.",405:"This action is not allowed.",409:"A conflict occurred. Please refresh and try again.",419:"Page expired. Please refresh and try again.",422:"The submitted data is invalid.",429:"Too many requests. Please wait a moment.",500:"Something went wrong on our end.",502:"Service temporarily unavailable. Try again shortly.",503:"Service is under maintenance. Try again shortly.",504:"Request timed out. Please try again."},Gi=null;xr=class l{_baseUrl="";_headers={};_timeout=3e4;_retries=0;_retryDelay=1e3;_query={};constructor(e){e?.baseUrl&&(this._baseUrl=e.baseUrl),e?.headers&&(this._headers={...e.headers}),e?.timeout&&(this._timeout=e.timeout),e?.retries&&(this._retries=e.retries),e?.retryDelay&&(this._retryDelay=e.retryDelay)}clone(){let e=new l;return e._baseUrl=this._baseUrl,e._headers={...this._headers},e._timeout=this._timeout,e._retries=this._retries,e._retryDelay=this._retryDelay,e._query={...this._query},e}baseUrl(e){let t=this.clone();return t._baseUrl=e,t}withHeaders(e){let t=this.clone();return t._headers={...t._headers,...e},t}withToken(e,t="Bearer"){return this.withHeaders({Authorization:`${t} ${e}`})}withBasicAuth(e,t){let s=Buffer.from(`${e}:${t}`).toString("base64");return this.withHeaders({Authorization:`Basic ${s}`})}accept(e){return this.withHeaders({Accept:e})}contentType(e){return this.withHeaders({"Content-Type":e})}timeout(e){let t=this.clone();return t._timeout=e,t}retry(e,t=1e3){let s=this.clone();return s._retries=e,s._retryDelay=t,s}query(e){let t=this.clone();for(let[s,r]of Object.entries(e))r!=null&&(t._query[s]=String(r));return t}async get(e){return this.request("GET",e)}async post(e,t){return this.request("POST",e,t)}async put(e,t){return this.request("PUT",e,t)}async patch(e,t){return this.request("PATCH",e,t)}async delete(e,t){return this.request("DELETE",e,t)}async request(e,t,s){let r=this.buildFullUrl(t),n={...this._headers},i;s!==void 0&&(n["Content-Type"]||(n["Content-Type"]="application/json"),i=typeof s=="string"?s:JSON.stringify(s)),n.Accept||(n.Accept="application/json");let a=null,o=1+this._retries;for(let c=0;c<o;c++){c>0&&await new Promise(u=>setTimeout(u,this._retryDelay*c));try{let u=new AbortController,d=setTimeout(()=>u.abort(),this._timeout),p=await fetch(r,{method:e,headers:n,body:i,signal:u.signal});if(clearTimeout(d),!p.ok&&p.status<500&&c<o-1,!p.ok&&p.status>=500&&c<o-1){a=new ye(`HTTP ${p.status}: ${p.statusText}`,p.status,await p.text());continue}let f=p.headers.get("content-type")??"",y;if(f.includes("application/json")?y=await p.json():y=await p.text(),!p.ok)throw new ye(`HTTP ${p.status}: ${typeof y=="string"?y:JSON.stringify(y)}`,p.status,y);return{data:y,status:p.status,headers:p.headers,ok:!0}}catch(u){if(u instanceof ye)throw u;if(a=u,u.name==="AbortError"&&(a=new ye("Request timed out",0,null)),c>=o-1)break}}throw a??new Error("HTTP request failed")}buildFullUrl(e){let t=this._baseUrl?`${this._baseUrl.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`:e,s=Object.entries(this._query);if(s.length>0){let r=new URL(t);for(let[n,i]of s)r.searchParams.set(n,i);t=r.toString()}return t}},ye=class extends Error{constructor(t,s,r){super(t);this.status=s;this.body=r;this.name="HttpRequestError"}},Tu=new xr});function en(l){let{paraglideMiddleware:e,getTextDirection:t=()=>"ltr",langPlaceholder:s="%lang%",dirPlaceholder:r="%dir%"}=l;return({event:n,resolve:i})=>e(n.request,({request:a,locale:o})=>(n.request=a,i(n,{transformPageChunk:({html:c})=>c.replace(s,o).replace(r,t(o))})))}function tn(l){return e=>l.deLocalizeUrl(e.url).pathname}var sn=v(()=>{"use strict"});function rn(l,e,t={}){return async s=>{let{superValidate:r,fail:n,message:i}=await import("sveltekit-superforms"),{zod:a}=await import("sveltekit-superforms/adapters"),o=await r(s,a(l));if(!o.valid)return n(400,{form:o});try{let c=await e(o.data,s);if(t.redirectTo){let{redirect:u}=await import("@sveltejs/kit");throw u(303,t.redirectTo)}return c??{form:o}}catch(c){if(c?.status>=300&&c?.status<400)throw c;return i(o,t.errorMessage||c.message||"An error occurred",{status:c.status||400})}}}async function nn(l,e){let{superValidate:t}=await import("sveltekit-superforms"),{zod:s}=await import("sveltekit-superforms/adapters");return t(e??null,s(l))}async function an(l,e){let t=await l.request.formData(),s={};for(let[n,i]of t.entries())s[n]=i;let r=e.safeParse(s);if(!r.success){let{FormValidationError:n}=await Promise.resolve().then(()=>(ar(),Ii));throw new n(r.error.flatten().fieldErrors)}return r.data}var on=v(()=>{"use strict"});var ln={};E(ln,{Application:()=>Dt,AuthManager:()=>Ft,AuthenticateMiddleware:()=>pe,BelongsTo:()=>ee,BelongsToMany:()=>te,Broadcast:()=>Vi,Cache:()=>zi,ColumnBuilder:()=>I,Connection:()=>g,Container:()=>H,Controller:()=>Ot,CorsMiddleware:()=>Nt,CsrfMiddleware:()=>ie,DatabaseSessionStore:()=>jt,ErrorHandler:()=>he,Event:()=>se,EventDispatcher:()=>Ae,FileSessionStore:()=>It,ForbiddenError:()=>_e,FormAuthorizationError:()=>fe,FormRequest:()=>Ue,FormValidationError:()=>ge,HasMany:()=>Z,HasOne:()=>X,Hash:()=>Ms,HttpError:()=>D,Job:()=>Q,Log:()=>zt,LoggingMiddleware:()=>_t,Mailable:()=>Ie,Mailer:()=>Bs,MemorySessionStore:()=>z,Middleware:()=>R,MiddlewareStack:()=>q,Migration:()=>Ce,Migrator:()=>Pe,Model:()=>$t,ModelNotFoundError:()=>Jt,NotFoundError:()=>De,Notification:()=>Yt,Notifier:()=>Wi,OriginMiddleware:()=>ne,QueryBuilder:()=>N,Queue:()=>Rs,RateLimitMiddleware:()=>re,RedisSessionStore:()=>Lt,RequireAuthMiddleware:()=>Bt,Schema:()=>L,Seeder:()=>At,ServiceProvider:()=>$e,Session:()=>ce,SessionMiddleware:()=>de,SignatureMiddleware:()=>Mt,Storage:()=>Ui,TableBuilder:()=>J,ThrottleMiddleware:()=>ae,UnauthorizedError:()=>Ne,ValidationError:()=>U,abort:()=>Vt,abortIf:()=>ki,abortUnless:()=>$i,apiFetch:()=>Yi,buildUrl:()=>Xi,config:()=>Ni,container:()=>fi,createFormAction:()=>rn,createI18nHandle:()=>en,createReroute:()=>tn,createSvelarApp:()=>Ai,createSvelarHooks:()=>sr,env:()=>Di,getCsrfToken:()=>Cr,loadForm:()=>nn,resource:()=>vi,rules:()=>Mi,schema:()=>ri,sequence:()=>rr,signJwt:()=>zs,validate:()=>Oi,validateForm:()=>an,verifyJwt:()=>Ks,z:()=>P});var cn=v(()=>{"use strict";hi();Es();ks();x();fs();ys();gi();As();$s();yi();oe();bi();ir();_i();ji();Ws();ue();Ds();tr();kt();ar();Fi();Xs();Ki();G();Ut();Ji();Qi();Zi();sn();on();ir()});var Sr={};E(Sr,{PluginRegistry:()=>rs});var Pr,rs,is=v(()=>{"use strict";T();Pr=class{plugins=new Map;enabledPlugins=new Set;async discover(){let{join:e}=await import("path"),{existsSync:t,readdirSync:s}=await import("fs"),{readFile:r}=await import("fs/promises"),n=[],i=e(process.cwd(),"node_modules");if(!t(i))return n;try{let a=s(i,{withFileTypes:!0});for(let o of a){if(!o.isDirectory())continue;let c=o.name;if(c.startsWith("."))continue;let u=c.startsWith("svelar-");if(!u)continue;let d=e(i,c,"package.json");if(t(d))try{let p=await r(d,"utf-8"),f=JSON.parse(p),y=f.keywords?.includes("svelar-plugin");if(!u&&!y)continue;let S={name:f.name||c,version:f.version||"0.0.0",description:f.description||"",packageName:c,installed:!0,enabled:!1,hasConfig:!!f.svelar?.config,hasMigrations:!!f.svelar?.migrations};n.push(S),this.plugins.set(S.name,S)}catch{}}}catch{}return n}enable(e){let t=this.plugins.get(e);if(!t)throw new Error(`Plugin "${e}" not found in registry.`);this.enabledPlugins.add(e),t.enabled=!0}disable(e){this.enabledPlugins.delete(e);let t=this.plugins.get(e);t&&(t.enabled=!1)}isEnabled(e){return this.enabledPlugins.has(e)}list(){return[...this.plugins.values()]}listEnabled(){return[...this.plugins.values()].filter(e=>this.enabledPlugins.has(e.name))}get(e){return this.plugins.get(e)}register(e){this.plugins.set(e.name,e)}},rs=w("svelar.pluginRegistry",()=>new Pr)});var dn={};E(dn,{PluginPublisher:()=>Rr});var Tr,Rr,Er=v(()=>{"use strict";T();Tr=class{async publish(e,t){let{mkdir:s,copyFile:r}=await import("fs/promises"),{join:n,dirname:i}=await import("path"),{existsSync:a}=await import("fs"),o={configs:[],migrations:[],assets:[]},c=e.publishables?.()||{};for(let[u,d]of Object.entries(c))for(let p of d){if(t?.only&&p.type!==t.only)continue;let f=n(process.cwd(),p.dest),y=i(f);if(!(a(f)&&!t?.force))try{await s(y,{recursive:!0}),await r(p.source,f),p.type==="config"?o.configs.push(f):p.type==="migration"?o.migrations.push(f):p.type==="asset"&&o.assets.push(f)}catch(S){console.warn(`Failed to publish ${p.source} to ${f}:`,S)}}return o}async preview(e){let t={configs:[],migrations:[],assets:[]},s=e.publishables?.()||{};for(let[r,n]of Object.entries(s))for(let i of n){let a=He("path").join(process.cwd(),i.dest);i.type==="config"?t.configs.push(a):i.type==="migration"?t.migrations.push(a):i.type==="asset"&&t.assets.push(a)}return t}},Rr=w("svelar.pluginPublisher",()=>new Tr)});var un={};E(un,{PluginInstaller:()=>yo});var kr,yo,mn=v(()=>{"use strict";T();is();Er();kr=class{async install(e,t){let{spawn:s}=await import("child_process"),{promisify:r}=await import("util"),{join:n}=await import("path"),{readFile:i}=await import("fs/promises"),{existsSync:a}=await import("fs");try{await this.runNpmInstall(e);let o=rs,c=await o.discover(),u;for(let p of c)if(p.packageName===e||p.name===e){u=p;break}if(!u)return{success:!1,pluginName:e,version:"0.0.0",published:null,error:`Plugin not found after installation. Make sure ${e} is a valid Svelar plugin.`};o.enable(u.name);let d=null;if(t?.publish!==!1)try{let p=await this.loadPluginClass(u.packageName);p&&(d=await Rr.publish(new p))}catch(p){console.warn("Failed to publish plugin assets:",p)}return{success:!0,pluginName:u.name,version:u.version,published:d}}catch(o){return{success:!1,pluginName:e,version:"0.0.0",published:null,error:o?.message??String(o)}}}async uninstall(e){try{let t=rs,s=t.get(e);return s?(t.disable(e),await this.runNpmUninstall(s.packageName),!0):!1}catch(t){return console.error("Failed to uninstall plugin:",t),!1}}async runNpmInstall(e){return new Promise((t,s)=>{let{spawn:r}=He("child_process"),n=r("npm",["install",e],{cwd:process.cwd(),stdio:"inherit"});n.on("close",i=>{i===0?t():s(new Error(`npm install exited with code ${i}`))}),n.on("error",s)})}async runNpmUninstall(e){return new Promise((t,s)=>{let{spawn:r}=He("child_process"),n=r("npm",["uninstall",e],{cwd:process.cwd(),stdio:"inherit"});n.on("close",i=>{i===0?t():s(new Error(`npm uninstall exited with code ${i}`))}),n.on("error",s)})}async loadPluginClass(e){try{let t=await import(e);return t.default||Object.values(t)[0]}catch{return null}}},yo=w("svelar.pluginInstaller",()=>new kr)});import{dirname as hn,join as $r}from"path";import{fileURLToPath as gn,pathToFileURL as vo}from"url";import{register as bo}from"module";import{readFileSync as fn,existsSync as wo}from"fs";var ze=class{commands=new Map;version;constructor(e="0.1.0"){this.version=e}register(e){let t=new e;return this.commands.set(t.name,t),this}add(e){return this.commands.set(e.name,e),this}async run(e=process.argv.slice(2)){let[t,...s]=e;if(!t||t==="--help"||t==="-h"){this.showHelp();return}if(t==="--version"||t==="-v"){console.log(`Svelar v${this.version}`);return}let r=this.commands.get(t);r||(console.error(`\x1B[31mUnknown command:\x1B[0m ${t}`),console.log("Run \x1B[36msvelar --help\x1B[0m for available commands."),process.exit(1));let{args:n,flags:i}=this.parseArgs(s,r);try{await r.handle(n,i)}catch(a){let o=a instanceof Error?a.message:String(a);console.error(`\x1B[31mError:\x1B[0m ${o}`),a?.stack&&console.error(a.stack),process.exit(1)}}parseArgs(e,t){let s=[],r={};for(let n of t.flags)n.default!==void 0&&(r[n.name]=n.default);for(let n=0;n<e.length;n++){let i=e[n];if(i.startsWith("--")){let a=i.slice(2),o=a.indexOf("=");if(o!==-1){let d=a.slice(0,o);r[d]=a.slice(o+1);continue}let c=a;t.flags.find(d=>d.name===c)?.type==="boolean"?r[c]=!0:n+1<e.length&&!e[n+1].startsWith("-")?r[c]=e[++n]:r[c]=!0}else if(i.startsWith("-")&&i.length===2){let a=i.slice(1),o=t.flags.find(c=>c.alias===a);o&&(o.type==="boolean"?r[o.name]=!0:n+1<e.length&&(r[o.name]=e[++n]))}else s.push(i)}return{args:s,flags:r}}showHelp(){console.log(`
|
|
159
|
+
`,i=new TextEncoder().encode(r);for(let n of this.subscribers)if(n.userId!==s)try{n.controller.enqueue(i)}catch{}}},Gt=class{config;constructor(e){this.config=e}async send(e,t,s){let r=Array.isArray(e)?e:[e],i=JSON.stringify({name:t,channels:r,data:JSON.stringify(s)}),n=Math.floor(Date.now()/1e3).toString(),a=await this.md5(i),o=["POST",`/apps/${this.config.appId}/events`,[`auth_key=${this.config.key}`,`auth_timestamp=${n}`,"auth_version=1.0",`body_md5=${a}`].join("&")].join(`
|
|
160
|
+
`),c=await this.hmacSha256(this.config.secret,o),u=this.config.useTLS!==!1?"https":"http",d=this.config.host??`api-${this.config.cluster??"mt1"}.pusher.com`,p=this.config.port??(this.config.useTLS!==!1?443:80),f=`${u}://${d}:${p}/apps/${this.config.appId}/events?auth_key=${this.config.key}&auth_timestamp=${n}&auth_version=1.0&body_md5=${a}&auth_signature=${c}`,b=await fetch(f,{method:"POST",headers:{"Content-Type":"application/json"},body:i});if(!b.ok){let C=await b.text();throw new Error(`Pusher API error (${b.status}): ${C}`)}}async authenticate(e,t,s){let r=`${e}:${t}`,i;s&&(i=JSON.stringify(s),r+=`:${i}`);let n=await this.hmacSha256(this.config.secret,r),a=`${this.config.key}:${n}`;return i?{auth:a,channel_data:i}:{auth:a}}get key(){return this.config.key}clientConfig(){let e={key:this.config.key,cluster:this.config.cluster??"mt1"};return this.config.host&&(e.wsHost=this.config.host,e.wsPort=this.config.port??6001,e.wssPort=this.config.port??6001,e.forceTLS=this.config.useTLS??!1,e.enabledTransports=["ws","wss"],e.disableStats=!0),e}async md5(e){let{createHash:t}=await import("crypto");return t("md5").update(e).digest("hex")}async hmacSha256(e,t){let{createHmac:s}=await import("crypto");return s("sha256",e).update(t).digest("hex")}},yr=class{constructor(e,t,s){this.manager=e;this.eventName=t;this.eventData=s}channels=[];on(...e){return this.channels.push(...e),this}async send(){for(let e of this.channels)await this.manager.sendToChannel(e,this.eventName,this.eventData)}},vr=class{config={default:"sse",drivers:{sse:{driver:"sse"}}};sseChannels=new Map;channelAuth=new Map;pusherDriver=null;configure(e){this.config=e;let t=Object.values(e.drivers).find(s=>s.driver==="pusher");t&&t.driver==="pusher"&&(this.pusherDriver=new Gt(t))}channel(e,t){if(t){this.channelAuth.set(e,t);return}return this.sseChannels.has(e)||this.sseChannels.set(e,new fr(e)),this.sseChannels.get(e)}async authorize(e,t){if(gr(e)==="public")return!0;for(let[r,i]of this.channelAuth){let n=this.matchPattern(r,e);if(n!==null)return await i(t,n)}return!1}async authenticatePusher(e,t,s){if(!this.pusherDriver)throw new Error("Pusher driver is not configured. Call Broadcast.configure() first.");let r=gr(e);if(r==="public")return!1;let i=await this.authorize(e,s);if(i===!1)return!1;if(r==="presence"){let n=typeof i=="object"?{user_id:i.id??s.id,user_info:i}:{user_id:s.id,user_info:{id:s.id}};return this.pusherDriver.authenticate(t,e,n)}return this.pusherDriver.authenticate(t,e)}event(e,t){return new yr(this,e,t)}to(e,t){return{send:async(s,r)=>{await this.sendToChannel(e,s,r,t)}}}async sendToChannel(e,t,s,r){let i=this.config.drivers[this.config.default];switch(i?.driver){case"pusher":this.pusherDriver||(this.pusherDriver=new Gt(i)),await this.pusherDriver.send(e,t,s);break;case"log":console.log(`[Broadcast] ${e} \u2192 ${t}:`,JSON.stringify(s));break;default:for(let[,n]of this.sseChannels)n.name===e&&n.send(t,s,r);break}}subscribe(e,t,s){return this.channel(e).stream(t,s)}members(e){let t=this.sseChannels.get(e);return t?t.getMembers():[]}pusher(){if(!this.pusherDriver)throw new Error("Pusher driver is not configured.");return this.pusherDriver}activeChannels(){return[...new Set([...this.sseChannels.values()].map(e=>e.name))]}totalSubscribers(){let e=0;for(let t of this.sseChannels.values())e+=t.subscriberCount();return e}prune(){for(let[e,t]of this.sseChannels)t.subscriberCount()===0&&this.sseChannels.delete(e)}matchPattern(e,t){let s=[],r=e.replace(/[.*+?^${}()|[\]\\]/g,o=>o==="{"||o==="}"?o:`\\${o}`).replace(/\\\{(\w+)\\\}/g,(o,c)=>(s.push(c),"([^.]+)")).replace(/\{(\w+)\}/g,(o,c)=>(s.push(c),"([^.]+)")),i=new RegExp(`^${r}$`),n=t.match(i);if(!n)return null;let a={};for(let o=0;o<s.length;o++)a[s[o]]=n[o+1];return a}},Wi=w("svelar.broadcast",()=>new vr)});function Yt(l,e,t){Vi&&Vi(l,e,{description:t})}async function Qi(l,e={}){let{csrfCookieName:t="XSRF-TOKEN",csrfHeaderName:s="X-CSRF-Token",showToast:r=!0,errorMessages:i={},...n}=e,a=(n.method||"GET").toUpperCase(),o=new Headers(n.headers);if(["POST","PUT","PATCH","DELETE"].includes(a)){let c=wr(t);c&&o.set(s,c)}n.body&&typeof n.body=="string"&&!o.has("Content-Type")&&o.set("Content-Type","application/json"),o.has("Accept")||o.set("Accept","application/json");try{let c=await fetch(l,{...n,headers:o});return!c.ok&&r&&await uo(c,{...co,...i}),c}catch(c){throw r&&Yt("error","Network Error","Unable to connect. Check your internet connection."),c}}async function uo(l,e){let t="";try{if((l.headers.get("content-type")??"").includes("application/json")){let n=await l.clone().json();if(t=n.message??"",l.status===422&&n.errors){let a=Object.entries(n.errors).map(([o,c])=>`${o}: ${c.join(", ")}`).slice(0,3).join(`
|
|
161
|
+
`);Yt("warning",e[422]??"Validation Error",a);return}}}catch{}let s=t||e[l.status]||`Error ${l.status}`,r=l.status>=500?"error":l.status===429?"warning":"error";if(l.status===401){Yt("warning",s,"Please sign in to continue.");return}Yt(r,s)}function wr(l="XSRF-TOKEN"){if(typeof document>"u")return null;let e=l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),t=document.cookie.match(new RegExp(`(?:^|;\\s*)${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}function Gi(l,e){if(!e)return l;let t=new URL(l,"http://localhost");for(let[s,r]of Object.entries(e))r!=null&&t.searchParams.set(s,String(r));return t.pathname+t.search}var co,Vi,br,fe,fu,Yi=y(()=>{"use strict";co={400:"Invalid request. Please check your input.",401:"Your session has expired. Please sign in again.",403:"You don't have permission to do this.",404:"The requested resource was not found.",405:"This action is not allowed.",409:"A conflict occurred. Please refresh and try again.",419:"Page expired. Please refresh and try again.",422:"The submitted data is invalid.",429:"Too many requests. Please wait a moment.",500:"Something went wrong on our end.",502:"Service temporarily unavailable. Try again shortly.",503:"Service is under maintenance. Try again shortly.",504:"Request timed out. Please try again."},Vi=null;br=class l{_baseUrl="";_headers={};_timeout=3e4;_retries=0;_retryDelay=1e3;_query={};constructor(e){e?.baseUrl&&(this._baseUrl=e.baseUrl),e?.headers&&(this._headers={...e.headers}),e?.timeout&&(this._timeout=e.timeout),e?.retries&&(this._retries=e.retries),e?.retryDelay&&(this._retryDelay=e.retryDelay)}clone(){let e=new l;return e._baseUrl=this._baseUrl,e._headers={...this._headers},e._timeout=this._timeout,e._retries=this._retries,e._retryDelay=this._retryDelay,e._query={...this._query},e}baseUrl(e){let t=this.clone();return t._baseUrl=e,t}withHeaders(e){let t=this.clone();return t._headers={...t._headers,...e},t}withToken(e,t="Bearer"){return this.withHeaders({Authorization:`${t} ${e}`})}withBasicAuth(e,t){let s=Buffer.from(`${e}:${t}`).toString("base64");return this.withHeaders({Authorization:`Basic ${s}`})}accept(e){return this.withHeaders({Accept:e})}contentType(e){return this.withHeaders({"Content-Type":e})}timeout(e){let t=this.clone();return t._timeout=e,t}retry(e,t=1e3){let s=this.clone();return s._retries=e,s._retryDelay=t,s}query(e){let t=this.clone();for(let[s,r]of Object.entries(e))r!=null&&(t._query[s]=String(r));return t}async get(e){return this.request("GET",e)}async post(e,t){return this.request("POST",e,t)}async put(e,t){return this.request("PUT",e,t)}async patch(e,t){return this.request("PATCH",e,t)}async delete(e,t){return this.request("DELETE",e,t)}async request(e,t,s){let r=this.buildFullUrl(t),i={...this._headers},n;s!==void 0&&(i["Content-Type"]||(i["Content-Type"]="application/json"),n=typeof s=="string"?s:JSON.stringify(s)),i.Accept||(i.Accept="application/json");let a=null,o=1+this._retries;for(let c=0;c<o;c++){c>0&&await new Promise(u=>setTimeout(u,this._retryDelay*c));try{let u=new AbortController,d=setTimeout(()=>u.abort(),this._timeout),p=await fetch(r,{method:e,headers:i,body:n,signal:u.signal});if(clearTimeout(d),!p.ok&&p.status<500&&c<o-1,!p.ok&&p.status>=500&&c<o-1){a=new fe(`HTTP ${p.status}: ${p.statusText}`,p.status,await p.text());continue}let f=p.headers.get("content-type")??"",b;if(f.includes("application/json")?b=await p.json():b=await p.text(),!p.ok)throw new fe(`HTTP ${p.status}: ${typeof b=="string"?b:JSON.stringify(b)}`,p.status,b);return{data:b,status:p.status,headers:p.headers,ok:!0}}catch(u){if(u instanceof fe)throw u;if(a=u,u.name==="AbortError"&&(a=new fe("Request timed out",0,null)),c>=o-1)break}}throw a??new Error("HTTP request failed")}buildFullUrl(e){let t=this._baseUrl?`${this._baseUrl.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`:e,s=Object.entries(this._query);if(s.length>0){let r=new URL(t);for(let[i,n]of s)r.searchParams.set(i,n);t=r.toString()}return t}},fe=class extends Error{constructor(t,s,r){super(t);this.status=s;this.body=r;this.name="HttpRequestError"}},fu=new br});function Xi(l){let{paraglideMiddleware:e,getTextDirection:t=()=>"ltr",langPlaceholder:s="%lang%",dirPlaceholder:r="%dir%"}=l;return({event:i,resolve:n})=>e(i.request,({request:a,locale:o})=>(i.request=a,n(i,{transformPageChunk:({html:c})=>c.replace(s,o).replace(r,t(o))})))}function Zi(l){return e=>l.deLocalizeUrl(e.url).pathname}var en=y(()=>{"use strict"});function tn(l,e,t={}){return async s=>{let{superValidate:r,fail:i,message:n}=await import("sveltekit-superforms"),{zod:a}=await import("sveltekit-superforms/adapters"),o=await r(s,a(l));if(!o.valid)return i(400,{form:o});try{let c=await e(o.data,s);if(t.redirectTo){let{redirect:u}=await import("@sveltejs/kit");throw u(303,t.redirectTo)}return c??{form:o}}catch(c){if(c?.status>=300&&c?.status<400)throw c;return n(o,t.errorMessage||c.message||"An error occurred",{status:c.status||400})}}}async function sn(l,e){let{superValidate:t}=await import("sveltekit-superforms"),{zod:s}=await import("sveltekit-superforms/adapters");return t(e??null,s(l))}async function rn(l,e){let t=await l.request.formData(),s={};for(let[i,n]of t.entries())s[i]=n;let r=e.safeParse(s);if(!r.success){let{FormValidationError:i}=await Promise.resolve().then(()=>(ir(),Ii));throw new i(r.error.flatten().fieldErrors)}return r.data}var nn=y(()=>{"use strict"});var an={};E(an,{Application:()=>$t,AuthManager:()=>qt,AuthenticateMiddleware:()=>me,BelongsTo:()=>Z,BelongsToMany:()=>ee,Broadcast:()=>Wi,Cache:()=>Fi,ColumnBuilder:()=>j,Connection:()=>g,Container:()=>F,Controller:()=>Nt,CorsMiddleware:()=>At,CsrfMiddleware:()=>re,DatabaseSessionStore:()=>_t,ErrorHandler:()=>pe,Event:()=>te,EventDispatcher:()=>$e,FileSessionStore:()=>It,ForbiddenError:()=>Me,FormAuthorizationError:()=>ge,FormRequest:()=>qe,FormValidationError:()=>he,HasMany:()=>X,HasOne:()=>Y,Hash:()=>Ms,HttpError:()=>A,Job:()=>V,Log:()=>Ft,LoggingMiddleware:()=>Dt,Mailable:()=>je,Mailer:()=>Us,MemorySessionStore:()=>H,Middleware:()=>T,MiddlewareStack:()=>L,Migration:()=>xe,Migrator:()=>Ce,Model:()=>Et,ModelNotFoundError:()=>Kt,NotFoundError:()=>Ae,Notification:()=>Qt,Notifier:()=>zi,OriginMiddleware:()=>ie,QueryBuilder:()=>D,Queue:()=>Ss,RateLimitMiddleware:()=>se,RedisSessionStore:()=>jt,RequireAuthMiddleware:()=>Ut,Schema:()=>O,Seeder:()=>kt,ServiceProvider:()=>ke,Session:()=>le,SessionMiddleware:()=>ce,SignatureMiddleware:()=>Mt,Storage:()=>Li,TableBuilder:()=>W,ThrottleMiddleware:()=>ne,UnauthorizedError:()=>De,ValidationError:()=>q,abort:()=>Wt,abortIf:()=>Ti,abortUnless:()=>Ei,apiFetch:()=>Qi,buildUrl:()=>Gi,config:()=>Ai,container:()=>hi,createFormAction:()=>tn,createI18nHandle:()=>Xi,createReroute:()=>Zi,createSvelarApp:()=>ki,createSvelarHooks:()=>er,env:()=>$i,getCsrfToken:()=>wr,loadForm:()=>sn,resource:()=>fi,rules:()=>Mi,schema:()=>ti,sequence:()=>tr,signJwt:()=>Fs,validate:()=>Ni,validateForm:()=>rn,verifyJwt:()=>Hs,z:()=>S});var on=y(()=>{"use strict";mi();Rs();Ts();x();hs();gs();pi();ks();Es();gi();ae();yi();sr();Di();_i();zs();de();$s();Zs();Tt();ir();qi();Gs();Hi();Q();Lt();Ki();Ji();Yi();en();nn();sr()});var Cr={};E(Cr,{PluginRegistry:()=>ts});var xr,ts,ss=y(()=>{"use strict";R();xr=class{plugins=new Map;enabledPlugins=new Set;async discover(){let{join:e}=await import("path"),{existsSync:t,readdirSync:s}=await import("fs"),{readFile:r}=await import("fs/promises"),i=[],n=e(process.cwd(),"node_modules");if(!t(n))return i;try{let a=s(n,{withFileTypes:!0});for(let o of a){if(!o.isDirectory())continue;let c=o.name;if(c.startsWith("."))continue;let u=c.startsWith("svelar-");if(!u)continue;let d=e(n,c,"package.json");if(t(d))try{let p=await r(d,"utf-8"),f=JSON.parse(p),b=f.keywords?.includes("svelar-plugin");if(!u&&!b)continue;let C={name:f.name||c,version:f.version||"0.0.0",description:f.description||"",packageName:c,installed:!0,enabled:!1,hasConfig:!!f.svelar?.config,hasMigrations:!!f.svelar?.migrations};i.push(C),this.plugins.set(C.name,C)}catch{}}}catch{}return i}enable(e){let t=this.plugins.get(e);if(!t)throw new Error(`Plugin "${e}" not found in registry.`);this.enabledPlugins.add(e),t.enabled=!0}disable(e){this.enabledPlugins.delete(e);let t=this.plugins.get(e);t&&(t.enabled=!1)}isEnabled(e){return this.enabledPlugins.has(e)}list(){return[...this.plugins.values()]}listEnabled(){return[...this.plugins.values()].filter(e=>this.enabledPlugins.has(e.name))}get(e){return this.plugins.get(e)}register(e){this.plugins.set(e.name,e)}},ts=w("svelar.pluginRegistry",()=>new xr)});var ln={};E(ln,{PluginPublisher:()=>Sr});var Pr,Sr,Rr=y(()=>{"use strict";R();Pr=class{async publish(e,t){let{mkdir:s,copyFile:r}=await import("fs/promises"),{join:i,dirname:n}=await import("path"),{existsSync:a}=await import("fs"),o={configs:[],migrations:[],assets:[]},c=e.publishables?.()||{};for(let[u,d]of Object.entries(c))for(let p of d){if(t?.only&&p.type!==t.only)continue;let f=i(process.cwd(),p.dest),b=n(f);if(!(a(f)&&!t?.force))try{await s(b,{recursive:!0}),await r(p.source,f),p.type==="config"?o.configs.push(f):p.type==="migration"?o.migrations.push(f):p.type==="asset"&&o.assets.push(f)}catch(C){console.warn(`Failed to publish ${p.source} to ${f}:`,C)}}return o}async preview(e){let t={configs:[],migrations:[],assets:[]},s=e.publishables?.()||{};for(let[r,i]of Object.entries(s))for(let n of i){let a=Fe("path").join(process.cwd(),n.dest);n.type==="config"?t.configs.push(a):n.type==="migration"?t.migrations.push(a):n.type==="asset"&&t.assets.push(a)}return t}},Sr=w("svelar.pluginPublisher",()=>new Pr)});var cn={};E(cn,{PluginInstaller:()=>mo});var Tr,mo,dn=y(()=>{"use strict";R();ss();Rr();Tr=class{async install(e,t){let{spawn:s}=await import("child_process"),{promisify:r}=await import("util"),{join:i}=await import("path"),{readFile:n}=await import("fs/promises"),{existsSync:a}=await import("fs");try{await this.runNpmInstall(e);let o=ts,c=await o.discover(),u;for(let p of c)if(p.packageName===e||p.name===e){u=p;break}if(!u)return{success:!1,pluginName:e,version:"0.0.0",published:null,error:`Plugin not found after installation. Make sure ${e} is a valid Svelar plugin.`};o.enable(u.name);let d=null;if(t?.publish!==!1)try{let p=await this.loadPluginClass(u.packageName);p&&(d=await Sr.publish(new p))}catch(p){console.warn("Failed to publish plugin assets:",p)}return{success:!0,pluginName:u.name,version:u.version,published:d}}catch(o){return{success:!1,pluginName:e,version:"0.0.0",published:null,error:o?.message??String(o)}}}async uninstall(e){try{let t=ts,s=t.get(e);return s?(t.disable(e),await this.runNpmUninstall(s.packageName),!0):!1}catch(t){return console.error("Failed to uninstall plugin:",t),!1}}async runNpmInstall(e){return new Promise((t,s)=>{let{spawn:r}=Fe("child_process"),i=r("npm",["install",e],{cwd:process.cwd(),stdio:"inherit"});i.on("close",n=>{n===0?t():s(new Error(`npm install exited with code ${n}`))}),i.on("error",s)})}async runNpmUninstall(e){return new Promise((t,s)=>{let{spawn:r}=Fe("child_process"),i=r("npm",["uninstall",e],{cwd:process.cwd(),stdio:"inherit"});i.on("close",n=>{n===0?t():s(new Error(`npm uninstall exited with code ${n}`))}),i.on("error",s)})}async loadPluginClass(e){try{let t=await import(e);return t.default||Object.values(t)[0]}catch{return null}}},mo=w("svelar.pluginInstaller",()=>new Tr)});import{dirname as mn,join as Er}from"path";import{fileURLToPath as pn,pathToFileURL as po}from"url";import{register as ho}from"module";import{readFileSync as hn,existsSync as go}from"fs";var He=class{commands=new Map;version;constructor(e="0.1.0"){this.version=e}register(e){let t=new e;return this.commands.set(t.name,t),this}add(e){return this.commands.set(e.name,e),this}async run(e=process.argv.slice(2)){let[t,...s]=e;if(!t||t==="--help"||t==="-h"){this.showHelp();return}if(t==="--version"||t==="-v"){console.log(`Svelar v${this.version}`);return}let r=this.commands.get(t);if(r||(console.error(`\x1B[31mUnknown command:\x1B[0m ${t}`),console.log("Run \x1B[36msvelar --help\x1B[0m for available commands."),process.exit(1)),s.includes("--help")||s.includes("-h")){this.showCommandHelp(r);return}let{args:i,flags:n}=this.parseArgs(s,r);try{await r.handle(i,n)}catch(a){let o=a instanceof Error?a.message:String(a);console.error(`\x1B[31mError:\x1B[0m ${o}`),a?.stack&&console.error(a.stack),process.exit(1)}}parseArgs(e,t){let s=[],r={};for(let i of t.flags)i.default!==void 0&&(r[i.name]=i.default);for(let i=0;i<e.length;i++){let n=e[i];if(n.startsWith("--")){let a=n.slice(2),o=a.indexOf("=");if(o!==-1){let d=a.slice(0,o);r[d]=a.slice(o+1);continue}let c=a;t.flags.find(d=>d.name===c)?.type==="boolean"?r[c]=!0:i+1<e.length&&!e[i+1].startsWith("-")?r[c]=e[++i]:r[c]=!0}else if(n.startsWith("-")&&n.length===2){let a=n.slice(1),o=t.flags.find(c=>c.alias===a);o&&(o.type==="boolean"?r[o.name]=!0:i+1<e.length&&(r[o.name]=e[++i]))}else s.push(n)}return{args:s,flags:r}}showCommandHelp(e){if(console.log(`
|
|
162
|
+
\x1B[33mDescription:\x1B[0m`),console.log(` ${e.description}
|
|
163
|
+
`),console.log("\x1B[33mUsage:\x1B[0m"),console.log(` svelar ${e.name} [options]
|
|
164
|
+
`),e.flags.length>0){console.log("\x1B[33mOptions:\x1B[0m");for(let t of e.flags){let s=t.alias?`-${t.alias}, `:" ",r=`--${t.name}`,i=24-s.length-r.length;console.log(` ${s}${r}${" ".repeat(Math.max(1,i))}${t.description}`)}console.log()}}showHelp(){console.log(`
|
|
162
165
|
\x1B[36m ____ _
|
|
163
166
|
/ ___|_ _____| | __ _ _ __
|
|
164
167
|
\\___ \\ \\ / / _ \\ |/ _\` | '__|
|
|
@@ -169,7 +172,7 @@ id: ${Date.now()}
|
|
|
169
172
|
svelar <command> [arguments] [options]
|
|
170
173
|
|
|
171
174
|
\x1B[33mAvailable Commands:\x1B[0m`);let e=new Map;for(let[,t]of this.commands){let s=t.name.includes(":")?t.name.split(":")[0]:"general";e.has(s)||e.set(s,[]),e.get(s).push(t)}for(let[t,s]of e){console.log(`
|
|
172
|
-
\x1B[32m${t}\x1B[0m`);for(let r of s){let
|
|
175
|
+
\x1B[32m${t}\x1B[0m`);for(let r of s){let i=30-r.name.length;console.log(` \x1B[36m${r.name}\x1B[0m${" ".repeat(Math.max(1,i))}${r.description}`)}}console.log()}};var h=class{arguments=[];flags=[];async bootstrap(){let{join:e}=await import("path"),{existsSync:t,readFileSync:s}=await import("fs"),{Connection:r}=await Promise.resolve().then(()=>(x(),P)),i=process.cwd();try{r.getDriver();return}catch{}let n=e(i,"svelar.database.json");if(t(n))try{let c=s(n,"utf-8"),u=JSON.parse(c);r.configure(u),this.info("Database configured from svelar.database.json");return}catch(c){this.warn(`Failed to parse svelar.database.json: ${String(c?.message??c)}`)}let a=process.env.DB_DRIVER??"sqlite",o=process.env.DB_PATH??"database.db";r.configure({default:a,connections:{[a]:{driver:a,filename:o,host:process.env.DB_HOST,port:process.env.DB_PORT?parseInt(process.env.DB_PORT):void 0,database:process.env.DB_NAME,user:process.env.DB_USER,password:process.env.DB_PASSWORD}}}),this.info(`Using ${a} database${a==="sqlite"?`: ${o}`:""}`)}log(e){console.log(e)}info(e){console.log(`\x1B[34mINFO\x1B[0m ${e}`)}success(e){console.log(`\x1B[32m\u2713\x1B[0m ${e}`)}warn(e){console.log(`\x1B[33mWARN\x1B[0m ${e}`)}error(e){console.error(`\x1B[31mERROR\x1B[0m ${e}`)}table(e,t){let s=e.map((n,a)=>Math.max(n.length,...t.map(o=>(o[a]??"").length))),r=s.map(n=>"\u2500".repeat(n+2)).join("\u253C"),i=n=>n.map((a,o)=>` ${(a??"").padEnd(s[o])} `).join("\u2502");console.log(i(e)),console.log(r);for(let n of t)console.log(i(n))}newLine(){console.log()}};import{writeFileSync as ls,mkdirSync as cs,existsSync as bn}from"fs";import{join as K}from"path";var ze=class extends h{name="make:model";description="Create a new model class";arguments=["name"];flags=[{name:"migration",alias:"m",description:"Also create a migration",type:"boolean"},{name:"controller",alias:"c",description:"Also create a controller",type:"boolean"},{name:"resource",alias:"r",description:"Create a resource controller",type:"boolean"},{name:"all",alias:"a",description:"Create model, migration, and controller",type:"boolean"},{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a model name.");return}let r=this.toSnakeCase(this.pluralize(s)),i=t.module||this.toSnakeCase(this.pluralize(s));t.module||this.warn(`No --module specified. Using "${i}" as module. Consider: --module ${i}`);let n=K(process.cwd(),"src","lib","modules",i);cs(n,{recursive:!0});let a=K(n,`${s}.ts`);if(bn(a)){this.warn(`Model ${s} already exists at ${a}`);return}let o=`import { Model } from '@beeblock/svelar/orm';
|
|
173
176
|
|
|
174
177
|
export class ${s} extends Model {
|
|
175
178
|
static table = '${r}';
|
|
@@ -186,7 +189,7 @@ export class ${s} extends Model {
|
|
|
186
189
|
// return this.hasMany(Post, 'user_id');
|
|
187
190
|
// }
|
|
188
191
|
}
|
|
189
|
-
`;if(
|
|
192
|
+
`;if(ls(a,o),this.success(`Model created: src/lib/modules/${i}/${s}.ts`),t.migration||t.all){let u=`${new Date().toISOString().replace(/[^0-9]/g,"").slice(0,14)}_create_${r}_table`,d=K(process.cwd(),"src","lib","database","migrations");cs(d,{recursive:!0});let p=`import { Migration } from '@beeblock/svelar/database';
|
|
190
193
|
|
|
191
194
|
export default class Create${s}sTable extends Migration {
|
|
192
195
|
async up() {
|
|
@@ -203,7 +206,7 @@ export default class Create${s}sTable extends Migration {
|
|
|
203
206
|
await this.schema.dropTable('${r}');
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
|
-
`;
|
|
209
|
+
`;ls(K(d,`${u}.ts`),p),this.success(`Migration created: src/lib/database/migrations/${u}.ts`)}if(t.controller||t.resource||t.all){let c=`${s}Controller`,u=K(process.cwd(),"src","lib","modules",i);cs(u,{recursive:!0});let p=t.resource||t.all?this.generateResourceController(s,c):this.generateBasicController(s,c);ls(K(u,`${c}.ts`),p),this.success(`Controller created: src/lib/modules/${i}/${c}.ts`)}}generateResourceController(e,t){return`import { Controller, type RequestEvent } from '@beeblock/svelar/routing';
|
|
207
210
|
import { z } from '@beeblock/svelar/validation';
|
|
208
211
|
import { ${e} } from './${e}.js';
|
|
209
212
|
|
|
@@ -255,7 +258,7 @@ export class ${t} extends Controller {
|
|
|
255
258
|
return this.json(items);
|
|
256
259
|
}
|
|
257
260
|
}
|
|
258
|
-
`}toSnakeCase(e){return e.replace(/([A-Z])/g,"_$1").toLowerCase().replace(/^_/,"")}pluralize(e){return e.endsWith("y")?e.slice(0,-1)+"ies":e.endsWith("s")||e.endsWith("x")||e.endsWith("z")||e.endsWith("ch")||e.endsWith("sh")?e+"es":e+"s"}};import{writeFileSync as
|
|
261
|
+
`}toSnakeCase(e){return e.replace(/([A-Z])/g,"_$1").toLowerCase().replace(/^_/,"")}pluralize(e){return e.endsWith("y")?e.slice(0,-1)+"ies":e.endsWith("s")||e.endsWith("x")||e.endsWith("z")||e.endsWith("ch")||e.endsWith("sh")?e+"es":e+"s"}};import{writeFileSync as wn,mkdirSync as xn}from"fs";import{join as Ar}from"path";var Ke=class extends h{name="make:migration";description="Create a new migration file";arguments=["name"];flags=[{name:"create",description:"Table to create",type:"string"},{name:"table",description:"Table to modify",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a migration name (e.g. create_users_table).");return}let r=Ar(process.cwd(),"src","lib","database","migrations");xn(r,{recursive:!0});let n=`${new Date().toISOString().replace(/[^0-9]/g,"").slice(0,14)}_${s}`,a=this.toPascalCase(s),o=t.create??this.detectTableName(s,"create"),c=t.table??this.detectTableName(s,"add"),u;o?u=`import { Migration } from '@beeblock/svelar/database';
|
|
259
262
|
|
|
260
263
|
export default class ${a} extends Migration {
|
|
261
264
|
async up() {
|
|
@@ -295,7 +298,7 @@ export default class ${a} extends Migration {
|
|
|
295
298
|
// Reverse the migration
|
|
296
299
|
}
|
|
297
300
|
}
|
|
298
|
-
`,
|
|
301
|
+
`,wn(Ar(r,`${n}.ts`),u),this.success(`Migration created: src/lib/database/migrations/${n}.ts`)}detectTableName(e,t){let s=e.match(new RegExp(`${t}_(.+?)_table`));return s?s[1]:null}toPascalCase(e){return e.split("_").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join("")}};import{writeFileSync as Cn,mkdirSync as Pn,existsSync as Sn}from"fs";import{join as Dr}from"path";var We=class extends h{name="make:controller";description="Create a new controller class";arguments=["name"];flags=[{name:"resource",alias:"r",description:"Create a resource controller with CRUD methods",type:"boolean"},{name:"model",alias:"m",description:"Model name for resource controller",type:"string"},{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a controller name.");return}let r=s.endsWith("Controller")?s:`${s}Controller`,i=r.replace(/Controller$/,""),n=t.module||i.toLowerCase();t.module||this.warn(`No --module specified. Using "${n}" as module. Consider: --module ${n}`);let a=Dr(process.cwd(),"src","lib","modules",n);Pn(a,{recursive:!0});let o=Dr(a,`${r}.ts`);if(Sn(o)){this.warn(`Controller ${r} already exists.`);return}let c=t.resource?this.generateResourceController(r,t.model):this.generateBasicController(r);Cn(o,c),this.success(`Controller created: src/lib/modules/${n}/${r}.ts`)}generateResourceController(e,t){return`import { Controller, type RequestEvent } from '@beeblock/svelar/routing';
|
|
299
302
|
import { z } from '@beeblock/svelar/validation';
|
|
300
303
|
${t?`import { ${t} } from './${t}.js';
|
|
301
304
|
`:""}
|
|
@@ -341,7 +344,7 @@ export class ${e} extends Controller {
|
|
|
341
344
|
return this.json({ message: 'Hello from ${e}' });
|
|
342
345
|
}
|
|
343
346
|
}
|
|
344
|
-
`}};import{writeFileSync as
|
|
347
|
+
`}};import{writeFileSync as Rn,mkdirSync as Tn,existsSync as En}from"fs";import{join as Mr}from"path";var Je=class extends h{name="make:middleware";description="Create a new middleware class";arguments=["name"];flags=[];async handle(e){let t=e[0];if(!t){this.error("Please provide a middleware name.");return}let s=t.endsWith("Middleware")?t:`${t}Middleware`,r=Mr(process.cwd(),"src","lib","shared","middleware");Tn(r,{recursive:!0});let i=Mr(r,`${s}.ts`);if(En(i)){this.warn(`Middleware ${s} already exists.`);return}let n=`import { Middleware, type MiddlewareContext, type NextFunction } from '@beeblock/svelar/middleware';
|
|
345
348
|
|
|
346
349
|
export class ${s} extends Middleware {
|
|
347
350
|
async handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void> {
|
|
@@ -354,7 +357,7 @@ export class ${s} extends Middleware {
|
|
|
354
357
|
return response;
|
|
355
358
|
}
|
|
356
359
|
}
|
|
357
|
-
`;
|
|
360
|
+
`;Rn(i,n),this.success(`Middleware created: src/lib/shared/middleware/${s}.ts`)}};import{writeFileSync as kn,mkdirSync as $n,existsSync as An}from"fs";import{join as Nr}from"path";var Ve=class extends h{name="make:provider";description="Create a new service provider class";arguments=["name"];flags=[];async handle(e){let t=e[0];if(!t){this.error("Please provide a provider name.");return}let s=t.endsWith("ServiceProvider")?t:`${t}ServiceProvider`,r=Nr(process.cwd(),"src","lib","shared","providers");$n(r,{recursive:!0});let i=Nr(r,`${s}.ts`);if(An(i)){this.warn(`Provider ${s} already exists.`);return}let n=`import { ServiceProvider } from '@beeblock/svelar/container';
|
|
358
361
|
import type { Container } from '@beeblock/svelar/container';
|
|
359
362
|
|
|
360
363
|
export class ${s} extends ServiceProvider {
|
|
@@ -375,7 +378,7 @@ export class ${s} extends ServiceProvider {
|
|
|
375
378
|
// Initialization logic here
|
|
376
379
|
}
|
|
377
380
|
}
|
|
378
|
-
`;
|
|
381
|
+
`;kn(i,n),this.success(`Provider created: src/lib/shared/providers/${s}.ts`)}};import{writeFileSync as Dn,mkdirSync as Mn,existsSync as Nn}from"fs";import{join as _r}from"path";var Qe=class extends h{name="make:seeder";description="Create a new database seeder class";arguments=["name"];flags=[];async handle(e){let t=e[0];if(!t){this.error("Please provide a seeder name.");return}let s=t.endsWith("Seeder")?t:`${t}Seeder`,r=_r(process.cwd(),"src","lib","database","seeders");Mn(r,{recursive:!0});let i=_r(r,`${s}.ts`);if(Nn(i)){this.warn(`Seeder ${s} already exists.`);return}let n=`import { Seeder } from '@beeblock/svelar/database';
|
|
379
382
|
|
|
380
383
|
export class ${s} extends Seeder {
|
|
381
384
|
async run(): Promise<void> {
|
|
@@ -384,7 +387,7 @@ export class ${s} extends Seeder {
|
|
|
384
387
|
// await User.create({ name: 'Admin', email: 'admin@example.com' });
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
|
-
`;
|
|
390
|
+
`;Dn(i,n),this.success(`Seeder created: src/lib/database/seeders/${s}.ts`)}};import{writeFileSync as _n,mkdirSync as In,existsSync as jn}from"fs";import{join as Ir}from"path";var Ge=class extends h{name="make:service";description="Create a new service class";arguments=["name"];flags=[{name:"crud",description:"Create a CRUD service with model",type:"boolean"},{name:"model",alias:"m",description:"Model name for CRUD service",type:"string"},{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a service name.");return}let r=s.endsWith("Service")?s:`${s}Service`,i=r.replace(/Service$/,""),n=t.module||i.toLowerCase();t.module||this.warn(`No --module specified. Using "${n}" as module. Consider: --module ${n}`);let a=Ir(process.cwd(),"src","lib","modules",n);In(a,{recursive:!0});let o=Ir(a,`${r}.ts`);if(jn(o)){this.warn(`Service ${r} already exists.`);return}let c=t.crud?this.generateCrudService(r,t.model):this.generateBasicService(r);_n(o,c),this.success(`Service created: src/lib/modules/${n}/${r}.ts`)}generateCrudService(e,t){let s=t||"Model";return`import { CrudService, type ServiceResult } from '@beeblock/svelar/services';
|
|
388
391
|
import { ${s} } from './${s}.js';
|
|
389
392
|
|
|
390
393
|
export class ${e} extends CrudService<${s}> {
|
|
@@ -409,7 +412,7 @@ export class ${e} extends Service {
|
|
|
409
412
|
}
|
|
410
413
|
}
|
|
411
414
|
}
|
|
412
|
-
`}};import{writeFileSync as
|
|
415
|
+
`}};import{writeFileSync as On,mkdirSync as Ln,existsSync as qn}from"fs";import{join as jr}from"path";var Ye=class extends h{name="make:repository";description="Create a new repository class";arguments=["name"];flags=[{name:"model",alias:"m",description:"Model name for the repository",type:"string"},{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a repository name.");return}let r=s.endsWith("Repository")?s:`${s}Repository`,i=r.replace(/Repository$/,""),n=t.module||i.toLowerCase();t.module||this.warn(`No --module specified. Using "${n}" as module. Consider: --module ${n}`);let a=jr(process.cwd(),"src","lib","modules",n);Ln(a,{recursive:!0});let o=jr(a,`${r}.ts`);if(qn(o)){this.warn(`Repository ${r} already exists.`);return}let c=t.model||this.inferModelName(r),u=`import { Repository } from '@beeblock/svelar/repositories';
|
|
413
416
|
import { ${c} } from './${c}.js';
|
|
414
417
|
|
|
415
418
|
export class ${r} extends Repository<${c}> {
|
|
@@ -426,7 +429,7 @@ export class ${r} extends Repository<${c}> {
|
|
|
426
429
|
// return ${c}.where('active', true).orderBy('name').get();
|
|
427
430
|
// }
|
|
428
431
|
}
|
|
429
|
-
`;
|
|
432
|
+
`;On(o,u),this.success(`Repository created: src/lib/modules/${n}/${r}.ts`)}inferModelName(e){return e.replace("Repository","")||"Model"}};import{writeFileSync as Un,mkdirSync as Bn,existsSync as Fn}from"fs";import{join as Or}from"path";var Xe=class extends h{name="make:action";description="Create a new action class";arguments=["name"];flags=[{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide an action name.");return}let r=s.endsWith("Action")?s:`${s}Action`,i=r.replace(/Action$/,""),n=t.module||i.toLowerCase();t.module||this.warn(`No --module specified. Using "${n}" as module. Consider: --module ${n}`);let a=Or(process.cwd(),"src","lib","modules",n);Bn(a,{recursive:!0});let o=Or(a,`${r}.ts`);if(Fn(o)){this.warn(`Action ${r} already exists.`);return}let c=`import { Action } from '@beeblock/svelar/actions';
|
|
430
433
|
|
|
431
434
|
interface ${r}Input {
|
|
432
435
|
// Define input type
|
|
@@ -442,7 +445,7 @@ export class ${r} extends Action<${r}Input, ${r}Output> {
|
|
|
442
445
|
throw new Error('Not implemented');
|
|
443
446
|
}
|
|
444
447
|
}
|
|
445
|
-
`;
|
|
448
|
+
`;Un(o,c),this.success(`Action created: src/lib/modules/${n}/${r}.ts`)}};import{writeFileSync as Hn,mkdirSync as zn,existsSync as Kn}from"fs";import{join as Lr}from"path";var Ze=class extends h{name="make:request";description="Create a new FormRequest validation class";arguments=["name"];flags=[{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a request name.");return}let r=s.endsWith("Request")?s:`${s}Request`,i=r.replace(/Request$/,""),n=t.module||i.toLowerCase();t.module||this.warn(`No --module specified. Using "${n}" as module. Consider: --module ${n}`);let a=Lr(process.cwd(),"src","lib","modules",n);zn(a,{recursive:!0});let o=Lr(a,`${r}.ts`);if(Kn(o)){this.warn(`Request ${r} already exists.`);return}let c=`import { FormRequest } from '@beeblock/svelar/routing';
|
|
446
449
|
import { z } from '@beeblock/svelar/validation';
|
|
447
450
|
|
|
448
451
|
export class ${r} extends FormRequest {
|
|
@@ -471,11 +474,11 @@ export class ${r} extends FormRequest {
|
|
|
471
474
|
return data;
|
|
472
475
|
}
|
|
473
476
|
}
|
|
474
|
-
`;
|
|
477
|
+
`;Hn(o,c),this.success(`Request created: src/lib/modules/${n}/${r}.ts`)}};import{writeFileSync as Wn,mkdirSync as Jn,existsSync as Vn}from"fs";import{join as qr}from"path";var et=class extends h{name="make:plugin";description="Create a new plugin class";arguments=["name"];flags=[];async handle(e){let t=e[0];if(!t){this.error("Please provide a plugin name.");return}let s=t.endsWith("Plugin")?t:`${t}Plugin`,r=qr(process.cwd(),"src","lib","shared","plugins");Jn(r,{recursive:!0});let i=qr(r,`${s}.ts`);if(Vn(i)){this.warn(`Plugin ${s} already exists.`);return}let n=s.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,""),a=`import { Plugin } from '@beeblock/svelar/plugins';
|
|
475
478
|
import type { Container } from '@beeblock/svelar/container';
|
|
476
479
|
|
|
477
480
|
export class ${s} extends Plugin {
|
|
478
|
-
readonly name = '${
|
|
481
|
+
readonly name = '${n}';
|
|
479
482
|
readonly version = '1.0.0';
|
|
480
483
|
description = '${s} for Svelar';
|
|
481
484
|
|
|
@@ -493,7 +496,7 @@ export class ${s} extends Plugin {
|
|
|
493
496
|
// Clean up resources
|
|
494
497
|
}
|
|
495
498
|
}
|
|
496
|
-
`;
|
|
499
|
+
`;Wn(i,a),this.success(`Plugin created: src/lib/shared/plugins/${s}.ts`)}};import{writeFileSync as Qn,mkdirSync as Gn,existsSync as Yn}from"fs";import{join as Ur}from"path";var tt=class extends h{name="make:task";description="Create a new scheduled task class";arguments=["name"];flags=[];async handle(e){let t=e[0];if(!t){this.error("Please provide a task name.");return}let s=(t.endsWith("Task"),t),r=Ur(process.cwd(),"src","lib","shared","scheduler");Gn(r,{recursive:!0});let i=Ur(r,`${s}.ts`);if(Yn(i)){this.warn(`Task ${s} already exists.`);return}let n=`import { ScheduledTask } from '@beeblock/svelar/scheduler';
|
|
497
500
|
|
|
498
501
|
export class ${s} extends ScheduledTask {
|
|
499
502
|
name = '${this.toKebabCase(s)}';
|
|
@@ -526,7 +529,7 @@ export class ${s} extends ScheduledTask {
|
|
|
526
529
|
console.error('${s} failed:', error.message);
|
|
527
530
|
}
|
|
528
531
|
}
|
|
529
|
-
`;
|
|
532
|
+
`;Qn(i,n),this.success(`Scheduled task created: src/lib/shared/scheduler/${s}.ts`)}toKebabCase(e){return e.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,"")}};import{writeFileSync as Xn,mkdirSync as Zn,existsSync as ea}from"fs";import{join as Br}from"path";var st=class extends h{name="make:job";description="Create a new queue job class";arguments=["name"];flags=[];async handle(e){let t=e[0];if(!t){this.error("Please provide a job name.");return}let s=(t.endsWith("Job"),t),r=Br(process.cwd(),"src","lib","shared","jobs");Zn(r,{recursive:!0});let i=Br(r,`${s}.ts`);if(ea(i)){this.warn(`Job ${s} already exists.`);return}let n=`import { Job } from '@beeblock/svelar/queue';
|
|
530
533
|
|
|
531
534
|
export class ${s} extends Job {
|
|
532
535
|
maxAttempts = 3; // Retry up to 3 times
|
|
@@ -550,7 +553,7 @@ export class ${s} extends Job {
|
|
|
550
553
|
console.log('${s} retrying, attempt', attempt);
|
|
551
554
|
}
|
|
552
555
|
}
|
|
553
|
-
`;
|
|
556
|
+
`;Xn(i,n),this.success(`Job created: src/lib/shared/jobs/${s}.ts`)}};import{writeFileSync as ta,mkdirSync as sa,existsSync as ra}from"fs";import{join as Fr}from"path";var rt=class extends h{name="make:command";description="Create a new custom CLI command";arguments=["name"];flags=[{name:"command",description:'The terminal command name (e.g. "app:sync")',type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a command class name. Example: npx svelar make:command SyncUsers");return}let r=s.endsWith("Command")?s:`${s}Command`,i=Fr(process.cwd(),"src","lib","shared","commands");sa(i,{recursive:!0});let n=Fr(i,`${r}.ts`);if(ra(n)){this.warn(`Command ${r} already exists.`);return}let a=t.command??this.deriveCommandName(r),o=`import { Command } from '@beeblock/svelar/cli';
|
|
554
557
|
|
|
555
558
|
export class ${r} extends Command {
|
|
556
559
|
name = '${a}';
|
|
@@ -573,7 +576,7 @@ export class ${r} extends Command {
|
|
|
573
576
|
this.success('Done!');
|
|
574
577
|
}
|
|
575
578
|
}
|
|
576
|
-
`;
|
|
579
|
+
`;ta(n,o),this.success(`Command created: src/lib/shared/commands/${r}.ts`),this.info(`Command name: ${a}`),this.newLine(),this.info("Your command will be auto-discovered. Run it with:"),this.log(` npx svelar ${a}`)}deriveCommandName(e){return`app:${e.replace(/Command$/,"").replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1-$2").toLowerCase()}`}};import{writeFileSync as ia,mkdirSync as na,existsSync as aa}from"fs";import{join as Hr}from"path";var it=class extends h{name="make:config";description="Create a new config file";arguments=["name"];flags=[];templates={app:`import { env } from 'svelar/config';
|
|
577
580
|
|
|
578
581
|
export default {
|
|
579
582
|
name: env('APP_NAME', 'Svelar'),
|
|
@@ -782,7 +785,7 @@ export default {
|
|
|
782
785
|
},
|
|
783
786
|
},
|
|
784
787
|
};
|
|
785
|
-
`};async handle(e){let t=e[0];if(!t){this.error("Please provide a config file name. Example: npx svelar make:config database"),this.newLine(),this.info("Available presets: app, database, auth, mail, cache, queue, storage, broadcasting, logging");return}let s=
|
|
788
|
+
`};async handle(e){let t=e[0];if(!t){this.error("Please provide a config file name. Example: npx svelar make:config database"),this.newLine(),this.info("Available presets: app, database, auth, mail, cache, queue, storage, broadcasting, logging");return}let s=Hr(process.cwd(),"config");na(s,{recursive:!0});let r=t.toLowerCase().replace(/\s+/g,"-"),i=Hr(s,`${r}.ts`);if(aa(i)){this.warn(`Config file config/${r}.ts already exists.`);return}let n=this.templates[r]??this.blankTemplate(r);ia(i,n),this.success(`Config created: config/${r}.ts`),this.newLine(),this.info("Access it with:"),this.log(` config.get('${r}.key')`)}blankTemplate(e){return`import { env } from 'svelar/config';
|
|
786
789
|
|
|
787
790
|
export default {
|
|
788
791
|
// Add your ${e} configuration here
|
|
@@ -791,7 +794,7 @@ export default {
|
|
|
791
794
|
// env<number>('MY_PORT', 3000)
|
|
792
795
|
// env<boolean>('MY_FLAG', false)
|
|
793
796
|
};
|
|
794
|
-
`}};import{writeFileSync as
|
|
797
|
+
`}};import{writeFileSync as oa,mkdirSync as la,existsSync as ca}from"fs";import{join as zr}from"path";var nt=class extends h{name="make:channel";description="Create a new broadcast channel authorization";arguments=["name"];flags=[{name:"presence",alias:"p",description:"Create a presence channel",type:"boolean"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a channel name. Example: npx svelar make:channel OrderChannel");return}let r=s.endsWith("Channel")?s:`${s}Channel`,i=zr(process.cwd(),"src","lib","shared","channels");la(i,{recursive:!0});let n=zr(i,`${r}.ts`);if(ca(n)){this.warn(`Channel ${r} already exists.`);return}let a=r.replace(/Channel$/,""),o=a.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),c=a.charAt(0).toLowerCase()+a.slice(1)+"Id",u=t.presence,p=`${u?"presence":"private"}-${o}.{${c}}`,f=u?this.presenceTemplate(r,p,c):this.privateTemplate(r,p,c);oa(n,f),this.success(`Channel created: src/lib/shared/channels/${r}.ts`),this.info(`Channel pattern: ${p}`),this.newLine(),this.info("Register it in src/app.ts or a service provider:"),this.log(` import { register${r} } from './lib/shared/channels/${r}.js';`),this.log(` register${r}();`)}privateTemplate(e,t,s){let r=e.replace(/Channel$/,"");return`import { Broadcast } from '@beeblock/svelar/broadcasting';
|
|
795
798
|
|
|
796
799
|
/**
|
|
797
800
|
* ${e}
|
|
@@ -834,7 +837,7 @@ export function register${e}(): void {
|
|
|
834
837
|
};
|
|
835
838
|
});
|
|
836
839
|
}
|
|
837
|
-
`}};import{writeFileSync as
|
|
840
|
+
`}};import{writeFileSync as da,mkdirSync as ua,existsSync as ma}from"fs";import{join as we}from"path";var at=class extends h{name="make:docker";description="Scaffold Docker deployment files (Dockerfile, docker-compose.yml, PM2 config)";arguments=[];flags=[{name:"db",alias:"d",description:"Database driver: postgres, mysql, sqlite (default: postgres)",type:"string"},{name:"soketi",alias:"s",description:"Include Soketi WebSocket server",type:"boolean"},{name:"redis",alias:"r",description:"Include Redis service",type:"boolean"},{name:"gotenberg",alias:"g",description:"Include Gotenberg PDF service (default: true)",type:"boolean"},{name:"rustfs",description:"Include RustFS S3-compatible object storage (default: true)",type:"boolean"},{name:"meilisearch",alias:"m",description:"Include Meilisearch full-text search engine",type:"boolean"},{name:"force",alias:"f",description:"Overwrite existing files",type:"boolean"}];async handle(e,t){let s=process.cwd(),r=t.db??"postgres",i=t.soketi??!0,n=t.redis??!0,a=t.gotenberg??!0,o=t.rustfs??!0,c=t.meilisearch??!1,u=t.force??!1,d=["postgres","mysql","sqlite"];if(!d.includes(r)){this.error(`Unknown database driver: ${r}. Use one of: ${d.join(", ")}`);return}let p=[{path:we(s,"Dockerfile"),content:this.dockerfileTemplate(),label:"Dockerfile"},{path:we(s,"docker-compose.yml"),content:this.composeTemplate(r,i,n,a,o,c),label:"docker-compose.yml"},{path:we(s,".dockerignore"),content:this.dockerignoreTemplate(),label:".dockerignore"},{path:we(s,"ecosystem.config.cjs"),content:this.pm2Template(),label:"ecosystem.config.cjs"}],f=0,b=0;for(let C of p){if(ma(C.path)&&!u){this.warn(`${C.label} already exists (use --force to overwrite)`),b++;continue}da(C.path,C.content),this.success(`Created ${C.label}`),f++}if(r!=="sqlite"){let C=we(s,"docker");ua(C,{recursive:!0})}this.newLine(),f>0?this.info(`${f} file(s) created${b>0?`, ${b} skipped`:""}`):this.info("No files created (all exist already)"),this.newLine(),this.info("Quick start:"),this.log(" # Build and start all services"),this.log(" docker compose up -d --build"),this.newLine(),this.log(" # Run migrations inside the container"),this.log(" docker compose exec app npx svelar migrate"),this.newLine(),this.log(" # View logs"),this.log(" docker compose logs -f app"),this.newLine(),this.log(" # Stop services"),this.log(" docker compose down"),this.newLine(),this.info("Make sure to update .env with production values before deploying.")}dockerfileTemplate(){return`# \u2500\u2500 Svelar Production Dockerfile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
838
841
|
# Multi-stage build for a lean production image.
|
|
839
842
|
|
|
840
843
|
# Stage 1: Install dependencies & build
|
|
@@ -887,7 +890,7 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\
|
|
|
887
890
|
|
|
888
891
|
# Start with PM2
|
|
889
892
|
CMD ["pm2-runtime", "ecosystem.config.cjs"]
|
|
890
|
-
`}composeTemplate(e,t,s,r,n=!
|
|
893
|
+
`}composeTemplate(e,t,s,r,i=!0,n=!1){let a=[];a.push("# \u2500\u2500 Svelar Docker Compose \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),a.push("# Generated by: npx svelar make:docker"),a.push("#"),a.push("# Usage:"),a.push("# docker compose up -d --build # Start all services"),a.push("# docker compose exec app npx svelar migrate # Run migrations"),a.push("# docker compose logs -f app # View app logs"),a.push("# docker compose down # Stop all services"),a.push(""),a.push("services:"),a.push(" app:"),a.push(" build: ."),a.push(" restart: unless-stopped"),a.push(" ports:"),a.push(' - "${APP_PORT:-3000}:3000"'),a.push(" env_file: .env"),a.push(" environment:"),a.push(" - NODE_ENV=production"),e==="postgres"?(a.push(" - DB_HOST=postgres"),a.push(" - DB_PORT=5432")):e==="mysql"&&(a.push(" - DB_HOST=mysql"),a.push(" - DB_PORT=3306")),s&&(a.push(" - REDIS_HOST=redis"),a.push(" - REDIS_PORT=6379"),a.push(" - REDIS_PASSWORD=${REDIS_PASSWORD:-svelarsecret}"),a.push(" - QUEUE_DRIVER=redis")),t&&(a.push(" - PUSHER_HOST=soketi"),a.push(" - PUSHER_PORT=6001")),r&&a.push(" - GOTENBERG_URL=http://gotenberg:3000"),i&&(a.push(" - S3_ENDPOINT=http://rustfs:9000"),a.push(" - S3_ACCESS_KEY=${RUSTFS_ROOT_USER:-svelar}"),a.push(" - S3_SECRET_KEY=${RUSTFS_ROOT_PASSWORD:-svelarsecret}"),a.push(" - S3_BUCKET=${S3_BUCKET:-svelar}"),a.push(" - S3_REGION=us-east-1"),a.push(" - STORAGE_DISK=s3")),n&&(a.push(" - MEILISEARCH_HOST=http://meilisearch:7700"),a.push(" - MEILISEARCH_KEY=${MEILI_MASTER_KEY:-svelar-meili-master-key}"));let o=[];if(e==="postgres"&&o.push("postgres"),e==="mysql"&&o.push("mysql"),s&&o.push("redis"),t&&o.push("soketi"),r&&o.push("gotenberg"),i&&o.push("rustfs"),n&&o.push("meilisearch"),o.length>0){a.push(" depends_on:");for(let c of o)a.push(` ${c}:`),a.push(" condition: service_healthy")}return a.push(" volumes:"),a.push(" - app_storage:/app/storage"),e==="postgres"&&(a.push(""),a.push(" postgres:"),a.push(" image: postgres:16-alpine"),a.push(" restart: unless-stopped"),a.push(" # No ports exposed \u2014 only reachable by app via Docker network"),a.push(" environment:"),a.push(" POSTGRES_DB: ${DB_NAME:-svelar}"),a.push(" POSTGRES_USER: ${DB_USER:-svelar}"),a.push(" POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}"),a.push(" volumes:"),a.push(" - pgdata:/var/lib/postgresql/data"),a.push(" healthcheck:"),a.push(' test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-svelar}"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 5")),e==="mysql"&&(a.push(""),a.push(" mysql:"),a.push(" image: mysql:8.0"),a.push(" restart: unless-stopped"),a.push(" # No ports exposed \u2014 only reachable by app via Docker network"),a.push(" environment:"),a.push(" MYSQL_DATABASE: ${DB_NAME:-svelar}"),a.push(" MYSQL_USER: ${DB_USER:-svelar}"),a.push(" MYSQL_PASSWORD: ${DB_PASSWORD:-secret}"),a.push(" MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret}"),a.push(" volumes:"),a.push(" - mysqldata:/var/lib/mysql"),a.push(" healthcheck:"),a.push(' test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 5")),s&&(a.push(""),a.push(" redis:"),a.push(" image: redis:7-alpine"),a.push(" restart: unless-stopped"),a.push(" # No ports exposed \u2014 only reachable by app via Docker network"),a.push(" command: redis-server --requirepass ${REDIS_PASSWORD:-svelarsecret}"),a.push(" volumes:"),a.push(" - redisdata:/data"),a.push(" healthcheck:"),a.push(' test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-svelarsecret}", "ping"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 5")),t&&(a.push(""),a.push(" soketi:"),a.push(" image: quay.io/soketi/soketi:1.6-16-debian"),a.push(" restart: unless-stopped"),a.push(" # No ports exposed \u2014 only reachable by app via Docker network"),a.push(" # Expose 6001 to host only if clients connect directly (uncomment below)"),a.push(" # ports:"),a.push(' # - "${SOKETI_PORT:-6001}:6001"'),a.push(" environment:"),a.push(' SOKETI_DEBUG: "${SOKETI_DEBUG:-0}"'),a.push(" SOKETI_DEFAULT_APP_ID: ${PUSHER_APP_ID}"),a.push(" SOKETI_DEFAULT_APP_KEY: ${PUSHER_KEY}"),a.push(" SOKETI_DEFAULT_APP_SECRET: ${PUSHER_SECRET}"),a.push(' SOKETI_DEFAULT_APP_MAX_CONNS: "${SOKETI_MAX_CONNS:-1000}"'),a.push(' SOKETI_DEFAULT_APP_ENABLE_CLIENT_MESSAGES: "true"'),a.push(' SOKETI_DEFAULT_APP_MAX_BACKEND_EVENTS_PER_SEC: "-1"'),a.push(" healthcheck:"),a.push(' test: ["CMD", "wget", "-qO-", "http://localhost:6001"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 3")),r&&(a.push(""),a.push(" gotenberg:"),a.push(" image: gotenberg/gotenberg:8"),a.push(" restart: unless-stopped"),a.push(" # No ports exposed \u2014 only reachable by app via Docker network"),a.push(" environment:"),a.push(' CHROMIUM_DISABLE_JAVASCRIPT: "false"'),a.push(' CHROMIUM_ALLOW_LIST: "file:///tmp/.*"'),a.push(' API_TIMEOUT: "${GOTENBERG_TIMEOUT:-60s}"'),a.push(' LOG_LEVEL: "${GOTENBERG_LOG_LEVEL:-info}"'),a.push(" healthcheck:"),a.push(' test: ["CMD", "curl", "-f", "http://localhost:3000/health"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 5")),i&&(a.push(""),a.push(" rustfs:"),a.push(" image: rustfs/rustfs:latest"),a.push(" restart: unless-stopped"),a.push(" ports:"),a.push(' - "${RUSTFS_CONSOLE_PORT:-9001}:9001" # Admin console (protect with firewall)'),a.push(" environment:"),a.push(" RUSTFS_ROOT_USER: ${RUSTFS_ROOT_USER:-svelar}"),a.push(" RUSTFS_ROOT_PASSWORD: ${RUSTFS_ROOT_PASSWORD:-svelarsecret}"),a.push(' command: server /data --console-address ":9001"'),a.push(" volumes:"),a.push(" - rustfs_data:/data"),a.push(" healthcheck:"),a.push(' test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 5")),n&&(a.push(""),a.push(" meilisearch:"),a.push(" image: getmeili/meilisearch:v1.13"),a.push(" restart: unless-stopped"),a.push(" # No ports exposed \u2014 only reachable by app via Docker network"),a.push(" # Uncomment below to access the dashboard from the host"),a.push(" # ports:"),a.push(' # - "${MEILI_PORT:-7700}:7700"'),a.push(" environment:"),a.push(" MEILI_MASTER_KEY: ${MEILI_MASTER_KEY:-svelar-meili-master-key}"),a.push(" MEILI_ENV: production"),a.push(" MEILI_DB_PATH: /meili_data"),a.push(' MEILI_NO_ANALYTICS: "true"'),a.push(" volumes:"),a.push(" - meili_data:/meili_data"),a.push(" healthcheck:"),a.push(' test: ["CMD", "wget", "--no-verbose", "--spider", "http://localhost:7700/health"]'),a.push(" interval: 10s"),a.push(" timeout: 5s"),a.push(" retries: 5")),a.push(""),a.push("volumes:"),a.push(" app_storage:"),e==="postgres"&&a.push(" pgdata:"),e==="mysql"&&a.push(" mysqldata:"),s&&a.push(" redisdata:"),i&&a.push(" rustfs_data:"),n&&a.push(" meili_data:"),a.push(""),a.join(`
|
|
891
894
|
`)}dockerignoreTemplate(){return`# Dependencies
|
|
892
895
|
node_modules
|
|
893
896
|
|
|
@@ -1010,7 +1013,7 @@ module.exports = {
|
|
|
1010
1013
|
},
|
|
1011
1014
|
],
|
|
1012
1015
|
};
|
|
1013
|
-
`}};import{writeFileSync as
|
|
1016
|
+
`}};import{writeFileSync as pa,mkdirSync as ha,existsSync as Kr}from"fs";import{join as I}from"path";var ot=class extends h{name="make:broadcasting";description="Scaffold broadcasting routes (Pusher auth, SSE stream, client init)";arguments=[];flags=[{name:"sse",description:"Only scaffold SSE routes (no Pusher auth)",type:"boolean"},{name:"pusher",description:"Only scaffold Pusher/Soketi routes",type:"boolean"},{name:"force",alias:"f",description:"Overwrite existing files",type:"boolean"}];async handle(e,t){let s=process.cwd(),r=t.force??!1,i=t.sse??!1,n=t.pusher??!1,a=!i&&!n,o=[];(a||n)&&o.push({path:I(s,"src/routes/api/broadcasting/auth/+server.ts"),content:this.pusherAuthRoute(),label:"src/routes/api/broadcasting/auth/+server.ts",dirs:[I(s,"src/routes/api/broadcasting/auth")]}),(a||i)&&o.push({path:I(s,"src/routes/api/broadcasting/[channel]/+server.ts"),content:this.sseRoute(),label:"src/routes/api/broadcasting/[channel]/+server.ts",dirs:[I(s,"src/routes/api/broadcasting/[channel]")]}),o.push({path:I(s,"src/lib/broadcasting.ts"),content:a||n?this.clientPusher():this.clientSSE(),label:"src/lib/broadcasting.ts",dirs:[I(s,"src/lib")]});let c=I(s,"config/broadcasting.ts");Kr(c)||o.push({path:c,content:this.configTemplate(),label:"config/broadcasting.ts",dirs:[I(s,"config")]});let u=0,d=0;for(let p of o){if(Kr(p.path)&&!r){this.warn(`${p.label} already exists (use --force to overwrite)`),d++;continue}for(let f of p.dirs)ha(f,{recursive:!0});pa(p.path,p.content),this.success(`Created ${p.label}`),u++}this.newLine(),u>0?this.info(`${u} file(s) created${d>0?`, ${d} skipped`:""}`):this.info("No files created (all exist already)"),this.newLine(),this.info("Next steps:"),a||n?(this.log(" 1. Install pusher-js: npm install pusher-js"),this.log(" 2. Add Pusher env vars to .env:"),this.log(" BROADCAST_DRIVER=pusher"),this.log(" PUSHER_KEY=svelar-key"),this.log(" PUSHER_SECRET=svelar-secret"),this.log(" PUSHER_APP_ID=svelar-app"),this.log(' PUSHER_HOST=localhost # or "soketi" in Docker'),this.log(" PUSHER_PORT=6001"),this.log(" 3. Import broadcasting in your root layout:"),this.log(" import '$lib/broadcasting';")):(this.log(" 1. Import broadcasting in your root layout:"),this.log(" import '$lib/broadcasting';")),this.log(" 4. Register channel authorizations in src/app.ts"),this.log(" 5. Run 'npx svelar make:docker' for Soketi in Docker Compose")}pusherAuthRoute(){return`/**
|
|
1014
1017
|
* Pusher/Soketi Channel Authorization Endpoint
|
|
1015
1018
|
*
|
|
1016
1019
|
* This route authenticates channel subscriptions from pusher-js.
|
|
@@ -1164,624 +1167,7 @@ export default {
|
|
|
1164
1167
|
},
|
|
1165
1168
|
},
|
|
1166
1169
|
};
|
|
1167
|
-
`}};import{writeFileSync as
|
|
1168
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1169
|
-
|
|
1170
|
-
/**
|
|
1171
|
-
* GET /api/admin/health
|
|
1172
|
-
* Returns system health status
|
|
1173
|
-
*/
|
|
1174
|
-
export const GET: RequestHandler = async (event) => {
|
|
1175
|
-
// TODO: Add admin middleware check
|
|
1176
|
-
|
|
1177
|
-
const health = {
|
|
1178
|
-
status: 'ok',
|
|
1179
|
-
timestamp: new Date().toISOString(),
|
|
1180
|
-
uptime: process.uptime(),
|
|
1181
|
-
memory: process.memoryUsage(),
|
|
1182
|
-
version: '0.1.0',
|
|
1183
|
-
};
|
|
1184
|
-
|
|
1185
|
-
return json(health);
|
|
1186
|
-
};
|
|
1187
|
-
`}queueServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1188
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
* GET /api/admin/queue
|
|
1192
|
-
* List jobs from the queue
|
|
1193
|
-
*
|
|
1194
|
-
* Query params:
|
|
1195
|
-
* - status: 'completed' | 'failed' | 'delayed' | 'active' | 'waiting'
|
|
1196
|
-
* - queue: queue name (default: 'default')
|
|
1197
|
-
* - limit: number of jobs (default: 50)
|
|
1198
|
-
* - offset: pagination offset (default: 0)
|
|
1199
|
-
*/
|
|
1200
|
-
export const GET: RequestHandler = async (event) => {
|
|
1201
|
-
// TODO: Add admin middleware check
|
|
1202
|
-
|
|
1203
|
-
const { searchParams } = event.url;
|
|
1204
|
-
const status = searchParams.get('status') || 'all';
|
|
1205
|
-
const queueName = searchParams.get('queue') || 'default';
|
|
1206
|
-
const limit = parseInt(searchParams.get('limit') || '50');
|
|
1207
|
-
const offset = parseInt(searchParams.get('offset') || '0');
|
|
1208
|
-
|
|
1209
|
-
try {
|
|
1210
|
-
const { JobMonitor } = await import('svelar/queue/JobMonitor');
|
|
1211
|
-
|
|
1212
|
-
const jobs = await JobMonitor.listJobs({
|
|
1213
|
-
queue: queueName,
|
|
1214
|
-
status: status === 'all' ? undefined : status as any,
|
|
1215
|
-
limit,
|
|
1216
|
-
offset,
|
|
1217
|
-
});
|
|
1218
|
-
|
|
1219
|
-
const counts = await JobMonitor.getCounts(queueName);
|
|
1220
|
-
|
|
1221
|
-
return json({
|
|
1222
|
-
jobs,
|
|
1223
|
-
counts,
|
|
1224
|
-
queueName,
|
|
1225
|
-
});
|
|
1226
|
-
} catch (error: any) {
|
|
1227
|
-
return json(
|
|
1228
|
-
{
|
|
1229
|
-
error: error.message || 'Failed to fetch queue jobs',
|
|
1230
|
-
},
|
|
1231
|
-
{ status: 500 }
|
|
1232
|
-
);
|
|
1233
|
-
}
|
|
1234
|
-
};
|
|
1235
|
-
`}queueRetryServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1236
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1237
|
-
|
|
1238
|
-
/**
|
|
1239
|
-
* POST /api/admin/queue/[id]/retry
|
|
1240
|
-
* Retry a failed job
|
|
1241
|
-
*/
|
|
1242
|
-
export const POST: RequestHandler = async (event) => {
|
|
1243
|
-
// TODO: Add admin middleware check
|
|
1244
|
-
|
|
1245
|
-
const { id } = event.params;
|
|
1246
|
-
|
|
1247
|
-
try {
|
|
1248
|
-
const { JobMonitor } = await import('svelar/queue/JobMonitor');
|
|
1249
|
-
await JobMonitor.retryJob(id);
|
|
1250
|
-
|
|
1251
|
-
return json({ success: true, message: 'Job queued for retry' });
|
|
1252
|
-
} catch (error: any) {
|
|
1253
|
-
return json(
|
|
1254
|
-
{
|
|
1255
|
-
error: error.message || 'Failed to retry job',
|
|
1256
|
-
},
|
|
1257
|
-
{ status: 500 }
|
|
1258
|
-
);
|
|
1259
|
-
}
|
|
1260
|
-
};
|
|
1261
|
-
`}queueDeleteServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1262
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1263
|
-
|
|
1264
|
-
/**
|
|
1265
|
-
* DELETE /api/admin/queue/[id]
|
|
1266
|
-
* Remove a job from the queue
|
|
1267
|
-
*/
|
|
1268
|
-
export const DELETE: RequestHandler = async (event) => {
|
|
1269
|
-
// TODO: Add admin middleware check
|
|
1270
|
-
|
|
1271
|
-
const { id } = event.params;
|
|
1272
|
-
|
|
1273
|
-
try {
|
|
1274
|
-
const { JobMonitor } = await import('svelar/queue/JobMonitor');
|
|
1275
|
-
await JobMonitor.deleteJob(id);
|
|
1276
|
-
|
|
1277
|
-
return json({ success: true, message: 'Job removed' });
|
|
1278
|
-
} catch (error: any) {
|
|
1279
|
-
return json(
|
|
1280
|
-
{
|
|
1281
|
-
error: error.message || 'Failed to remove job',
|
|
1282
|
-
},
|
|
1283
|
-
{ status: 500 }
|
|
1284
|
-
);
|
|
1285
|
-
}
|
|
1286
|
-
};
|
|
1287
|
-
`}schedulerServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1288
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1289
|
-
|
|
1290
|
-
/**
|
|
1291
|
-
* GET /api/admin/scheduler
|
|
1292
|
-
* List all scheduled tasks
|
|
1293
|
-
*/
|
|
1294
|
-
export const GET: RequestHandler = async (event) => {
|
|
1295
|
-
// TODO: Add admin middleware check
|
|
1296
|
-
|
|
1297
|
-
try {
|
|
1298
|
-
const { ScheduleMonitor } = await import('svelar/scheduler/ScheduleMonitor');
|
|
1299
|
-
|
|
1300
|
-
const tasks = await ScheduleMonitor.listTasks();
|
|
1301
|
-
const health = await ScheduleMonitor.getHealth();
|
|
1302
|
-
|
|
1303
|
-
return json({
|
|
1304
|
-
tasks,
|
|
1305
|
-
health,
|
|
1306
|
-
});
|
|
1307
|
-
} catch (error: any) {
|
|
1308
|
-
return json(
|
|
1309
|
-
{
|
|
1310
|
-
error: error.message || 'Failed to fetch scheduled tasks',
|
|
1311
|
-
},
|
|
1312
|
-
{ status: 500 }
|
|
1313
|
-
);
|
|
1314
|
-
}
|
|
1315
|
-
};
|
|
1316
|
-
`}schedulerRunServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1317
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
* POST /api/admin/scheduler/[name]/run
|
|
1321
|
-
* Manually trigger a scheduled task
|
|
1322
|
-
*/
|
|
1323
|
-
export const POST: RequestHandler = async (event) => {
|
|
1324
|
-
// TODO: Add admin middleware check
|
|
1325
|
-
|
|
1326
|
-
const { name } = event.params;
|
|
1327
|
-
|
|
1328
|
-
try {
|
|
1329
|
-
const { ScheduleMonitor } = await import('svelar/scheduler/ScheduleMonitor');
|
|
1330
|
-
await ScheduleMonitor.runTask(name);
|
|
1331
|
-
|
|
1332
|
-
return json({ success: true, message: \`Task '\${name}' triggered\` });
|
|
1333
|
-
} catch (error: any) {
|
|
1334
|
-
return json(
|
|
1335
|
-
{
|
|
1336
|
-
error: error.message || 'Failed to run task',
|
|
1337
|
-
},
|
|
1338
|
-
{ status: 500 }
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
};
|
|
1342
|
-
`}schedulerToggleServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1343
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1344
|
-
|
|
1345
|
-
/**
|
|
1346
|
-
* POST /api/admin/scheduler/[name]/toggle
|
|
1347
|
-
* Enable or disable a scheduled task
|
|
1348
|
-
*/
|
|
1349
|
-
export const POST: RequestHandler = async (event) => {
|
|
1350
|
-
// TODO: Add admin middleware check
|
|
1351
|
-
|
|
1352
|
-
const { name } = event.params;
|
|
1353
|
-
const body = await event.request.json();
|
|
1354
|
-
const enabled = body.enabled ?? true;
|
|
1355
|
-
|
|
1356
|
-
try {
|
|
1357
|
-
const { ScheduleMonitor } = await import('svelar/scheduler/ScheduleMonitor');
|
|
1358
|
-
if (enabled) {
|
|
1359
|
-
ScheduleMonitor.enableTask(name);
|
|
1360
|
-
} else {
|
|
1361
|
-
ScheduleMonitor.disableTask(name);
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
return json({ success: true, message: \`Task '\${name}' \${enabled ? 'enabled' : 'disabled'}\` });
|
|
1365
|
-
} catch (error: any) {
|
|
1366
|
-
return json(
|
|
1367
|
-
{
|
|
1368
|
-
error: error.message || 'Failed to toggle task',
|
|
1369
|
-
},
|
|
1370
|
-
{ status: 500 }
|
|
1371
|
-
);
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1374
|
-
`}logsServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1375
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1376
|
-
|
|
1377
|
-
/**
|
|
1378
|
-
* GET /api/admin/logs
|
|
1379
|
-
* Retrieve logs with filtering
|
|
1380
|
-
*
|
|
1381
|
-
* Query params:
|
|
1382
|
-
* - level: 'debug' | 'info' | 'warn' | 'error'
|
|
1383
|
-
* - channel: log channel name
|
|
1384
|
-
* - search: search term
|
|
1385
|
-
* - limit: number of logs (default: 100)
|
|
1386
|
-
* - offset: pagination offset (default: 0)
|
|
1387
|
-
*/
|
|
1388
|
-
export const GET: RequestHandler = async (event) => {
|
|
1389
|
-
// TODO: Add admin middleware check
|
|
1390
|
-
|
|
1391
|
-
const { searchParams } = event.url;
|
|
1392
|
-
const level = searchParams.get('level');
|
|
1393
|
-
const channel = searchParams.get('channel');
|
|
1394
|
-
const search = searchParams.get('search');
|
|
1395
|
-
const limit = parseInt(searchParams.get('limit') || '100');
|
|
1396
|
-
const offset = parseInt(searchParams.get('offset') || '0');
|
|
1397
|
-
|
|
1398
|
-
try {
|
|
1399
|
-
const { LogViewer } = await import('svelar/logging/LogViewer');
|
|
1400
|
-
|
|
1401
|
-
const logs = LogViewer.query({
|
|
1402
|
-
level: level as any,
|
|
1403
|
-
channel: channel ?? undefined,
|
|
1404
|
-
search: search ?? undefined,
|
|
1405
|
-
limit,
|
|
1406
|
-
offset,
|
|
1407
|
-
});
|
|
1408
|
-
|
|
1409
|
-
const stats = LogViewer.getStats();
|
|
1410
|
-
|
|
1411
|
-
return json({
|
|
1412
|
-
logs,
|
|
1413
|
-
total: stats.totalEntries,
|
|
1414
|
-
limit,
|
|
1415
|
-
offset,
|
|
1416
|
-
});
|
|
1417
|
-
} catch (error: any) {
|
|
1418
|
-
return json(
|
|
1419
|
-
{
|
|
1420
|
-
error: error.message || 'Failed to fetch logs',
|
|
1421
|
-
},
|
|
1422
|
-
{ status: 500 }
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
};
|
|
1426
|
-
`}logsTailServerTemplate(){return`import type { RequestHandler } from '@sveltejs/kit';
|
|
1427
|
-
|
|
1428
|
-
/**
|
|
1429
|
-
* GET /api/admin/logs/tail
|
|
1430
|
-
* Server-Sent Events stream of live logs
|
|
1431
|
-
*
|
|
1432
|
-
* Returns a text/event-stream response with real-time log entries
|
|
1433
|
-
*/
|
|
1434
|
-
export const GET: RequestHandler = async (event) => {
|
|
1435
|
-
// TODO: Add admin middleware check
|
|
1436
|
-
|
|
1437
|
-
const { LogViewer } = await import('svelar/logging/LogViewer');
|
|
1438
|
-
|
|
1439
|
-
// Set up SSE headers
|
|
1440
|
-
const headers = {
|
|
1441
|
-
'Content-Type': 'text/event-stream',
|
|
1442
|
-
'Cache-Control': 'no-cache',
|
|
1443
|
-
Connection: 'keep-alive',
|
|
1444
|
-
};
|
|
1445
|
-
|
|
1446
|
-
// Create a readable stream using the tail() subscription
|
|
1447
|
-
const stream = new ReadableStream({
|
|
1448
|
-
start(controller) {
|
|
1449
|
-
const encoder = new TextEncoder();
|
|
1450
|
-
const unsubscribe = LogViewer.tail((entry) => {
|
|
1451
|
-
try {
|
|
1452
|
-
controller.enqueue(encoder.encode(\`data: \${JSON.stringify(entry)}
|
|
1453
|
-
|
|
1454
|
-
\`));
|
|
1455
|
-
} catch {
|
|
1456
|
-
unsubscribe();
|
|
1457
|
-
}
|
|
1458
|
-
});
|
|
1459
|
-
|
|
1460
|
-
// Clean up when the client disconnects
|
|
1461
|
-
event.request.signal.addEventListener('abort', () => {
|
|
1462
|
-
unsubscribe();
|
|
1463
|
-
controller.close();
|
|
1464
|
-
});
|
|
1465
|
-
},
|
|
1466
|
-
});
|
|
1467
|
-
|
|
1468
|
-
return new Response(stream, { headers });
|
|
1469
|
-
};
|
|
1470
|
-
`}statsServerTemplate(){return`import { json } from '@sveltejs/kit';
|
|
1471
|
-
import type { RequestHandler } from '@sveltejs/kit';
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* GET /api/admin/stats
|
|
1475
|
-
* Dashboard overview statistics
|
|
1476
|
-
*/
|
|
1477
|
-
export const GET: RequestHandler = async (event) => {
|
|
1478
|
-
// TODO: Add admin middleware check
|
|
1479
|
-
|
|
1480
|
-
try {
|
|
1481
|
-
const { JobMonitor } = await import('svelar/queue/JobMonitor');
|
|
1482
|
-
const { ScheduleMonitor } = await import('svelar/scheduler/ScheduleMonitor');
|
|
1483
|
-
const { LogViewer } = await import('svelar/logging/LogViewer');
|
|
1484
|
-
|
|
1485
|
-
const [queueHealth, recentErrors] = await Promise.all([
|
|
1486
|
-
JobMonitor.getHealth(),
|
|
1487
|
-
Promise.resolve(LogViewer.getRecentErrors(10)),
|
|
1488
|
-
]);
|
|
1489
|
-
|
|
1490
|
-
const schedulerHealth = await ScheduleMonitor.getHealth();
|
|
1491
|
-
const logStats = LogViewer.getStats();
|
|
1492
|
-
|
|
1493
|
-
return json({
|
|
1494
|
-
queue: queueHealth,
|
|
1495
|
-
scheduler: schedulerHealth,
|
|
1496
|
-
logs: logStats,
|
|
1497
|
-
recentErrors,
|
|
1498
|
-
timestamp: new Date().toISOString(),
|
|
1499
|
-
});
|
|
1500
|
-
} catch (error: any) {
|
|
1501
|
-
return json(
|
|
1502
|
-
{
|
|
1503
|
-
error: error.message || 'Failed to fetch stats',
|
|
1504
|
-
},
|
|
1505
|
-
{ status: 500 }
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
};
|
|
1509
|
-
`}dashboardPageServerTemplate(){return`import type { PageServerLoad } from './$types';
|
|
1510
|
-
|
|
1511
|
-
export const load: PageServerLoad = async ({ fetch }) => {
|
|
1512
|
-
try {
|
|
1513
|
-
// Fetch initial dashboard data
|
|
1514
|
-
const statsRes = await fetch('/api/admin/stats');
|
|
1515
|
-
const stats = await statsRes.json();
|
|
1516
|
-
|
|
1517
|
-
const healthRes = await fetch('/api/admin/health');
|
|
1518
|
-
const health = await healthRes.json();
|
|
1519
|
-
|
|
1520
|
-
return {
|
|
1521
|
-
stats,
|
|
1522
|
-
health,
|
|
1523
|
-
};
|
|
1524
|
-
} catch (error) {
|
|
1525
|
-
console.error('Failed to load dashboard:', error);
|
|
1526
|
-
return {
|
|
1527
|
-
stats: null,
|
|
1528
|
-
health: null,
|
|
1529
|
-
error: 'Failed to load dashboard data',
|
|
1530
|
-
};
|
|
1531
|
-
}
|
|
1532
|
-
};
|
|
1533
|
-
`}dashboardPageSvelteTemplate(){return`<script lang="ts">
|
|
1534
|
-
import type { PageData } from './$types';
|
|
1535
|
-
|
|
1536
|
-
export let data: PageData;
|
|
1537
|
-
|
|
1538
|
-
let selectedTab: 'overview' | 'queue' | 'scheduler' | 'logs' = 'overview';
|
|
1539
|
-
let loading = false;
|
|
1540
|
-
|
|
1541
|
-
async function refreshData() {
|
|
1542
|
-
loading = true;
|
|
1543
|
-
try {
|
|
1544
|
-
const response = await fetch('/api/admin/stats');
|
|
1545
|
-
const newStats = await response.json();
|
|
1546
|
-
data.stats = newStats;
|
|
1547
|
-
} catch (error) {
|
|
1548
|
-
console.error('Failed to refresh:', error);
|
|
1549
|
-
} finally {
|
|
1550
|
-
loading = false;
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
</script>
|
|
1554
|
-
|
|
1555
|
-
<div class="dashboard">
|
|
1556
|
-
<header class="dashboard-header">
|
|
1557
|
-
<h1>Admin Dashboard</h1>
|
|
1558
|
-
<button on:click={refreshData} disabled={loading}>
|
|
1559
|
-
{loading ? 'Refreshing...' : 'Refresh'}
|
|
1560
|
-
</button>
|
|
1561
|
-
</header>
|
|
1562
|
-
|
|
1563
|
-
<nav class="tabs">
|
|
1564
|
-
<button
|
|
1565
|
-
class:active={selectedTab === 'overview'}
|
|
1566
|
-
on:click={() => (selectedTab = 'overview')}
|
|
1567
|
-
>
|
|
1568
|
-
Overview
|
|
1569
|
-
</button>
|
|
1570
|
-
<button
|
|
1571
|
-
class:active={selectedTab === 'queue'}
|
|
1572
|
-
on:click={() => (selectedTab = 'queue')}
|
|
1573
|
-
>
|
|
1574
|
-
Queue
|
|
1575
|
-
</button>
|
|
1576
|
-
<button
|
|
1577
|
-
class:active={selectedTab === 'scheduler'}
|
|
1578
|
-
on:click={() => (selectedTab = 'scheduler')}
|
|
1579
|
-
>
|
|
1580
|
-
Scheduler
|
|
1581
|
-
</button>
|
|
1582
|
-
<button
|
|
1583
|
-
class:active={selectedTab === 'logs'}
|
|
1584
|
-
on:click={() => (selectedTab = 'logs')}
|
|
1585
|
-
>
|
|
1586
|
-
Logs
|
|
1587
|
-
</button>
|
|
1588
|
-
</nav>
|
|
1589
|
-
|
|
1590
|
-
<main class="dashboard-content">
|
|
1591
|
-
{#if selectedTab === 'overview'}
|
|
1592
|
-
<section class="overview">
|
|
1593
|
-
<h2>System Overview</h2>
|
|
1594
|
-
|
|
1595
|
-
{#if data.health}
|
|
1596
|
-
<div class="health-card">
|
|
1597
|
-
<h3>System Health</h3>
|
|
1598
|
-
<p>Status: <strong>{data.health.status}</strong></p>
|
|
1599
|
-
<p>Uptime: {(data.health.uptime / 3600).toFixed(2)} hours</p>
|
|
1600
|
-
<p>Memory: {(data.health.memory.heapUsed / 1024 / 1024).toFixed(2)} MB</p>
|
|
1601
|
-
</div>
|
|
1602
|
-
{/if}
|
|
1603
|
-
|
|
1604
|
-
{#if data.stats}
|
|
1605
|
-
<div class="stats-grid">
|
|
1606
|
-
<div class="stat-card">
|
|
1607
|
-
<h3>Queue Jobs</h3>
|
|
1608
|
-
<p class="stat-number">{data.stats.queue?.total || 0}</p>
|
|
1609
|
-
<small>Active: {data.stats.queue?.active || 0}</small>
|
|
1610
|
-
</div>
|
|
1611
|
-
|
|
1612
|
-
<div class="stat-card">
|
|
1613
|
-
<h3>Scheduled Tasks</h3>
|
|
1614
|
-
<p class="stat-number">{data.stats.scheduler?.total || 0}</p>
|
|
1615
|
-
<small>Enabled: {data.stats.scheduler?.enabled || 0}</small>
|
|
1616
|
-
</div>
|
|
1617
|
-
|
|
1618
|
-
<div class="stat-card">
|
|
1619
|
-
<h3>Recent Errors</h3>
|
|
1620
|
-
<p class="stat-number">{data.stats.recentErrors?.length || 0}</p>
|
|
1621
|
-
<small>Last 24 hours</small>
|
|
1622
|
-
</div>
|
|
1623
|
-
</div>
|
|
1624
|
-
{/if}
|
|
1625
|
-
</section>
|
|
1626
|
-
{:else if selectedTab === 'queue'}
|
|
1627
|
-
<section class="queue">
|
|
1628
|
-
<h2>Job Queue</h2>
|
|
1629
|
-
<p>Queue management interface coming soon...</p>
|
|
1630
|
-
</section>
|
|
1631
|
-
{:else if selectedTab === 'scheduler'}
|
|
1632
|
-
<section class="scheduler">
|
|
1633
|
-
<h2>Scheduled Tasks</h2>
|
|
1634
|
-
<p>Task management interface coming soon...</p>
|
|
1635
|
-
</section>
|
|
1636
|
-
{:else if selectedTab === 'logs'}
|
|
1637
|
-
<section class="logs">
|
|
1638
|
-
<h2>Application Logs</h2>
|
|
1639
|
-
<p>Log viewer coming soon...</p>
|
|
1640
|
-
</section>
|
|
1641
|
-
{/if}
|
|
1642
|
-
</main>
|
|
1643
|
-
</div>
|
|
1644
|
-
|
|
1645
|
-
<style>
|
|
1646
|
-
.dashboard {
|
|
1647
|
-
padding: 2rem;
|
|
1648
|
-
max-width: 1200px;
|
|
1649
|
-
margin: 0 auto;
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
.dashboard-header {
|
|
1653
|
-
display: flex;
|
|
1654
|
-
justify-content: space-between;
|
|
1655
|
-
align-items: center;
|
|
1656
|
-
margin-bottom: 2rem;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
.dashboard-header h1 {
|
|
1660
|
-
margin: 0;
|
|
1661
|
-
font-size: 2rem;
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
.dashboard-header button {
|
|
1665
|
-
padding: 0.5rem 1rem;
|
|
1666
|
-
background: #0066cc;
|
|
1667
|
-
color: white;
|
|
1668
|
-
border: none;
|
|
1669
|
-
border-radius: 4px;
|
|
1670
|
-
cursor: pointer;
|
|
1671
|
-
font-size: 1rem;
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
.dashboard-header button:hover:not(:disabled) {
|
|
1675
|
-
background: #0052a3;
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
.dashboard-header button:disabled {
|
|
1679
|
-
opacity: 0.6;
|
|
1680
|
-
cursor: not-allowed;
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
.tabs {
|
|
1684
|
-
display: flex;
|
|
1685
|
-
gap: 1rem;
|
|
1686
|
-
margin-bottom: 2rem;
|
|
1687
|
-
border-bottom: 1px solid #e0e0e0;
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
.tabs button {
|
|
1691
|
-
padding: 0.75rem 1.5rem;
|
|
1692
|
-
background: none;
|
|
1693
|
-
border: none;
|
|
1694
|
-
border-bottom: 3px solid transparent;
|
|
1695
|
-
cursor: pointer;
|
|
1696
|
-
font-size: 1rem;
|
|
1697
|
-
color: #666;
|
|
1698
|
-
transition: all 0.3s ease;
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
.tabs button:hover {
|
|
1702
|
-
color: #0066cc;
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
.tabs button.active {
|
|
1706
|
-
color: #0066cc;
|
|
1707
|
-
border-bottom-color: #0066cc;
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
.dashboard-content {
|
|
1711
|
-
animation: fadeIn 0.3s ease;
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
@keyframes fadeIn {
|
|
1715
|
-
from {
|
|
1716
|
-
opacity: 0;
|
|
1717
|
-
transform: translateY(10px);
|
|
1718
|
-
}
|
|
1719
|
-
to {
|
|
1720
|
-
opacity: 1;
|
|
1721
|
-
transform: translateY(0);
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
.overview {
|
|
1726
|
-
display: flex;
|
|
1727
|
-
flex-direction: column;
|
|
1728
|
-
gap: 2rem;
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
.health-card {
|
|
1732
|
-
padding: 1.5rem;
|
|
1733
|
-
background: #f5f5f5;
|
|
1734
|
-
border-radius: 8px;
|
|
1735
|
-
border-left: 4px solid #00aa00;
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
.health-card h3 {
|
|
1739
|
-
margin-top: 0;
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
.health-card p {
|
|
1743
|
-
margin: 0.5rem 0;
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
.stats-grid {
|
|
1747
|
-
display: grid;
|
|
1748
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
1749
|
-
gap: 1.5rem;
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
.stat-card {
|
|
1753
|
-
padding: 1.5rem;
|
|
1754
|
-
background: white;
|
|
1755
|
-
border: 1px solid #e0e0e0;
|
|
1756
|
-
border-radius: 8px;
|
|
1757
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
.stat-card h3 {
|
|
1761
|
-
margin-top: 0;
|
|
1762
|
-
font-size: 0.9rem;
|
|
1763
|
-
color: #666;
|
|
1764
|
-
text-transform: uppercase;
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
.stat-number {
|
|
1768
|
-
margin: 0.5rem 0;
|
|
1769
|
-
font-size: 2rem;
|
|
1770
|
-
font-weight: bold;
|
|
1771
|
-
color: #0066cc;
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
.stat-card small {
|
|
1775
|
-
display: block;
|
|
1776
|
-
color: #999;
|
|
1777
|
-
font-size: 0.85rem;
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
section h2 {
|
|
1781
|
-
margin-top: 0;
|
|
1782
|
-
}
|
|
1783
|
-
</style>
|
|
1784
|
-
`}};import{writeFileSync as Vr,mkdirSync as wa,existsSync as Qr}from"fs";import{join as ms}from"path";var dt=class extends h{name="make:resource";description="Create a new API resource (response transformer)";arguments=["name"];flags=[{name:"module",description:"Module name (e.g. auth, billing)",type:"string"},{name:"model",alias:"m",description:"Model name to transform",type:"string"},{name:"collection",alias:"c",description:"Also generate a collection resource",type:"boolean"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a resource name.");return}let r=s.endsWith("Resource")?s:`${s}Resource`,n=t.module||this.deriveModuleName(r);t.module||this.warn(`No --module specified, using "${n}". Use --module=<name> to target a specific module.`);let i=ms(process.cwd(),"src","lib","modules",n);wa(i,{recursive:!0});let a=ms(i,`${r}.ts`);if(Qr(a)){this.warn(`Resource ${r} already exists.`);return}let o=t.model||this.inferModelName(r),c=this.generateResource(r,o);if(Vr(a,c),this.success(`Resource created: src/lib/modules/${n}/${r}.ts`),t.collection){let u=r.replace("Resource","CollectionResource"),d=ms(i,`${u}.ts`);if(!Qr(d)){let p=this.generateCollectionResource(u,r,o);Vr(d,p),this.success(`Collection resource created: src/lib/modules/${n}/${u}.ts`)}}}generateResource(e,t){let s=`${t}Data`;return`import { Resource } from '@beeblock/svelar/routing';
|
|
1170
|
+
`}};import{writeFileSync as Wr,mkdirSync as ga,existsSync as Jr}from"fs";import{join as ds}from"path";var lt=class extends h{name="make:resource";description="Create a new API resource (response transformer)";arguments=["name"];flags=[{name:"module",description:"Module name (e.g. auth, billing)",type:"string"},{name:"model",alias:"m",description:"Model name to transform",type:"string"},{name:"collection",alias:"c",description:"Also generate a collection resource",type:"boolean"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a resource name.");return}let r=s.endsWith("Resource")?s:`${s}Resource`,i=t.module||this.deriveModuleName(r);t.module||this.warn(`No --module specified, using "${i}". Use --module=<name> to target a specific module.`);let n=ds(process.cwd(),"src","lib","modules",i);ga(n,{recursive:!0});let a=ds(n,`${r}.ts`);if(Jr(a)){this.warn(`Resource ${r} already exists.`);return}let o=t.model||this.inferModelName(r),c=this.generateResource(r,o);if(Wr(a,c),this.success(`Resource created: src/lib/modules/${i}/${r}.ts`),t.collection){let u=r.replace("Resource","CollectionResource"),d=ds(n,`${u}.ts`);if(!Jr(d)){let p=this.generateCollectionResource(u,r,o);Wr(d,p),this.success(`Collection resource created: src/lib/modules/${i}/${u}.ts`)}}}generateResource(e,t){let s=`${t}Data`;return`import { Resource } from '@beeblock/svelar/routing';
|
|
1785
1171
|
import type { ${t} } from './${t}.js';
|
|
1786
1172
|
|
|
1787
1173
|
// \u2500\u2500 API Contract \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -1859,7 +1245,7 @@ export class ${e} {
|
|
|
1859
1245
|
.toObject();
|
|
1860
1246
|
}
|
|
1861
1247
|
}
|
|
1862
|
-
`}deriveModuleName(e){return e.replace(/Resource$/,"").replace(/Collection$/,"").toLowerCase()}inferModelName(e){return e.replace(/Resource$/,"")||"Model"}};import{writeFileSync as
|
|
1248
|
+
`}deriveModuleName(e){return e.replace(/Resource$/,"").replace(/Collection$/,"").toLowerCase()}inferModelName(e){return e.replace(/Resource$/,"")||"Model"}};import{writeFileSync as fa,mkdirSync as ya,existsSync as va}from"fs";import{join as Vr}from"path";var ct=class extends h{name="make:schema";description="Create a contract schema (Zod schemas + shared types)";arguments=["name"];flags=[{name:"module",description:"Module name (e.g. auth, billing)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a schema name (e.g. User, Post, Invoice).");return}let r=s.charAt(0).toUpperCase()+s.slice(1),i=this.toKebab(s)+".schema",n=t.module||s.toLowerCase(),a=Vr(process.cwd(),"src","lib","modules",n);ya(a,{recursive:!0});let o=Vr(a,`${i}.ts`);if(va(o)){this.warn(`Schema already exists: src/lib/modules/${n}/${i}.ts`);return}let c=this.generateSchema(r);fa(o,c),this.success(`Schema created: src/lib/modules/${n}/${i}.ts`),this.info(""),this.info(" Use this schema across your entire stack:"),this.info(""),this.info(` Resource: extends Resource<${r}, ${r}Data>`),this.info(` FormRequest: uses create${r}Schema / update${r}Schema`),this.info(` Frontend: import type { ${r}Data } from '$lib/modules/${n}/${i}'`)}generateSchema(e){let t=e.charAt(0).toLowerCase()+e.slice(1),s=this.toKebab(e);return`import { z } from 'zod';
|
|
1863
1249
|
|
|
1864
1250
|
// \u2500\u2500 ${e} Contract Schema \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1865
1251
|
//
|
|
@@ -1894,7 +1280,7 @@ export const update${e}Schema = create${e}Schema.partial();
|
|
|
1894
1280
|
export type ${e}Data = z.infer<typeof ${t}Schema>;
|
|
1895
1281
|
export type Create${e}Input = z.infer<typeof create${e}Schema>;
|
|
1896
1282
|
export type Update${e}Input = z.infer<typeof update${e}Schema>;
|
|
1897
|
-
`}toKebab(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1-$2").toLowerCase()}};import{writeFileSync as
|
|
1283
|
+
`}toKebab(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1-$2").toLowerCase()}};import{writeFileSync as ba,mkdirSync as wa,existsSync as xa}from"fs";import{join as Qr}from"path";var dt=class extends h{name="make:observer";description="Create a new model observer class";arguments=["name"];flags=[{name:"model",alias:"m",description:"The model class to observe",type:"string"},{name:"module",description:"Module name (e.g. users, posts)",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide an observer name (e.g. UserObserver).");return}let r=t.model||s.replace(/Observer$/,""),i=t.module||this.toSnakeCase(this.pluralize(r)),n=Qr(process.cwd(),"src","lib","modules",i);wa(n,{recursive:!0});let a=Qr(n,`${s}.ts`);if(xa(a)){this.warn(`Observer ${s} already exists at ${a}`);return}let o=`import { ModelObserver } from '@beeblock/svelar/orm';
|
|
1898
1284
|
import type { ${r} } from './${r}.js';
|
|
1899
1285
|
|
|
1900
1286
|
export class ${s} extends ModelObserver {
|
|
@@ -1932,7 +1318,7 @@ export class ${s} extends ModelObserver {
|
|
|
1932
1318
|
// async deleted(${r.toLowerCase()}: ${r}) {
|
|
1933
1319
|
// }
|
|
1934
1320
|
}
|
|
1935
|
-
`;
|
|
1321
|
+
`;ba(a,o),this.success(`Observer created: src/lib/modules/${i}/${s}.ts`),this.info(`Register it in your app: ${r}.observe(new ${s}());`)}toSnakeCase(e){return e.replace(/([A-Z])/g,"_$1").toLowerCase().replace(/^_/,"")}pluralize(e){return e.endsWith("y")?e.slice(0,-1)+"ies":e.endsWith("s")||e.endsWith("x")||e.endsWith("z")||e.endsWith("ch")||e.endsWith("sh")?e+"es":e+"s"}};import{writeFileSync as Ca,mkdirSync as Pa,existsSync as Sa}from"fs";import{join as Gr}from"path";var ut=class extends h{name="make:event";description="Create a new event class";arguments=["name"];flags=[];async handle(e,t){let s=e[0];if(!s){this.error("Please provide an event name (e.g. UserRegistered).");return}let r=Gr(process.cwd(),"src","lib","events");Pa(r,{recursive:!0});let i=Gr(r,`${s}.ts`);if(Sa(i)){this.warn(`Event ${s} already exists at ${i}`);return}let n=`/**
|
|
1936
1322
|
* ${s} Event
|
|
1937
1323
|
*
|
|
1938
1324
|
* Dispatched when ... (describe when this event fires).
|
|
@@ -1950,7 +1336,7 @@ export class ${s} {
|
|
|
1950
1336
|
// public readonly metadata?: Record<string, any>,
|
|
1951
1337
|
) {}
|
|
1952
1338
|
}
|
|
1953
|
-
`;
|
|
1339
|
+
`;Ca(i,n),this.success(`Event created: src/lib/events/${s}.ts`)}};import{writeFileSync as Ra,mkdirSync as Ta,existsSync as Ea}from"fs";import{join as Yr}from"path";var mt=class extends h{name="make:listener";description="Create a new event listener class";arguments=["name"];flags=[{name:"event",alias:"e",description:"The event class this listener handles",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a listener name (e.g. SendWelcomeEmail).");return}let r=Yr(process.cwd(),"src","lib","listeners");Ta(r,{recursive:!0});let i=Yr(r,`${s}.ts`);if(Ea(i)){this.warn(`Listener ${s} already exists at ${i}`);return}let n=t.event||"any",a=t.event?`import type { ${t.event} } from '../events/${t.event}.js';
|
|
1954
1340
|
|
|
1955
1341
|
`:"",o=t.event||"any",c=`import { Listener } from '@beeblock/svelar/events';
|
|
1956
1342
|
${a}export class ${s} extends Listener<${o}> {
|
|
@@ -1964,16 +1350,16 @@ ${a}export class ${s} extends Listener<${o}> {
|
|
|
1964
1350
|
// return true;
|
|
1965
1351
|
// }
|
|
1966
1352
|
}
|
|
1967
|
-
`;
|
|
1353
|
+
`;Ra(i,c),this.success(`Listener created: src/lib/listeners/${s}.ts`),t.event&&(this.info("Don't forget to register it in your EventServiceProvider:"),this.info(` [${t.event}.name]: [${s}]`))}};import{writeFileSync as ka,mkdirSync as $a,existsSync as Aa}from"fs";import{join as Xr}from"path";var pt=class extends h{name="make:route";description="Create route files with controller bindings";arguments=["path"];flags=[{name:"controller",alias:"c",description:"Controller class name",type:"string"},{name:"resource",alias:"r",description:"Generate full CRUD resource routes",type:"boolean"},{name:"api",description:"Prefix path with /api",type:"boolean"},{name:"methods",alias:"m",description:"HTTP methods (comma-separated: GET,POST,PUT,DELETE)",type:"string"},{name:"module",description:"Module name for controller import path",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a route path (e.g. posts, users/[id], admin/settings).");return}let r=s.replace(/^\//,"");t.api&&!r.startsWith("api/")&&(r="api/"+r);let i=t.controller||this.inferControllerName(r),n=t.module||this.inferModuleName(r);t.resource?this.generateResourceRoutes(r,i,n):this.generateRoute(r,i,n,t.methods)}generateResourceRoutes(e,t,s){this.generateRouteFile(e,t,s,[{method:"GET",handler:"index"},{method:"POST",handler:"store"}]);let r=this.inferParamName(e);this.generateRouteFile(`${e}/[${r}]`,t,s,[{method:"GET",handler:"show"},{method:"PUT",handler:"update"},{method:"DELETE",handler:"destroy"}])}generateRoute(e,t,s,r){let n=(r?r.split(",").map(a=>a.trim().toUpperCase()):["GET"]).map(a=>({method:a,handler:this.defaultHandler(a)}));this.generateRouteFile(e,t,s,n)}generateRouteFile(e,t,s,r){let i=Xr(process.cwd(),"src","routes",...e.split("/"));$a(i,{recursive:!0});let n=Xr(i,"+server.ts");if(Aa(n)){this.warn(`Route already exists: src/routes/${e}/+server.ts (skipped)`);return}let a=`$lib/modules/${s}/${t}.js`,o=r.map(u=>`export const ${u.method} = ctrl.handle('${u.handler}');`).join(`
|
|
1968
1354
|
`),c=`import { ${t} } from '${a}';
|
|
1969
1355
|
|
|
1970
1356
|
const ctrl = new ${t}();
|
|
1971
1357
|
${o}
|
|
1972
|
-
`;
|
|
1973
|
-
`);let i=Math.max(6,...n.map(u=>u.method.length)),a=Math.max(4,...n.map(u=>u.path.length)),o=Math.max(7,...n.map(u=>u.handler.length)),c=` ${"METHOD".padEnd(i)} ${"PATH".padEnd(a)} ${"HANDLER".padEnd(o)} FILE`;this.log(`\x1B[2m${c}\x1B[0m`),this.log(`\x1B[2m ${"\u2500".repeat(i)} ${"\u2500".repeat(a)} ${"\u2500".repeat(o)} ${"\u2500".repeat(20)}\x1B[0m`);for(let u of n){let p=`${this.getMethodColor(u.method)}${u.method.padEnd(i)}\x1B[0m`,f=u.path.padEnd(a),y=`\x1B[2m${u.handler.padEnd(o)}\x1B[0m`,S=`\x1B[2m${u.file}\x1B[0m`;this.log(` ${p} ${f} ${y} ${S}`)}this.log("")}scanRoutes(e,t){let s=[],r=Ia(e);for(let n of r){let i=ti(e,n);if(La(i).isDirectory()){s.push(...this.scanRoutes(i,t));continue}n==="+server.ts"||n==="+server.js"?s.push(...this.parseServerFile(i,t)):(n==="+page.server.ts"||n==="+page.server.js")&&s.push(...this.parsePageServerFile(i,t))}return s}parseServerFile(e,t){let s=[],r=si(e,"utf-8"),n=this.filePathToUrl(e,t),i=ps(process.cwd(),e).split(hs).join("/"),a=/export\s+(?:const|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g,o;for(;(o=a.exec(r))!==null;){let c=o[1],u=this.extractHandler(r,c);s.push({method:c,path:n,handler:u,file:i})}return s}parsePageServerFile(e,t){let s=[],r=si(e,"utf-8"),n=this.filePathToUrl(e,t),i=ps(process.cwd(),e).split(hs).join("/");/export\s+(const|async\s+function)\s+load\b/.test(r)&&s.push({method:"GET",path:n,handler:"load()",file:i});let a=r.match(/export\s+const\s+actions\s*=\s*\{([^}]+)\}/);if(a){let o=a[1].split(",").map(c=>c.trim().split(":")[0].split("(")[0].trim()).filter(Boolean);for(let c of o)s.push({method:"POST",path:c==="default"?n:`${n}?/${c}`,handler:`actions.${c}()`,file:i})}return s}filePathToUrl(e,t){let s=ps(t,e).split(hs).join("/");return s=s.replace(/\/?\+(?:server|page\.server)\.[tj]s$/,""),s=s.replace(/\[\.\.\.(\w+)\]/g,"*$1").replace(/\[\[(\w+)\]\]/g,":$1?").replace(/\[(\w+)\]/g,":$1"),s=s.replace(/\([^)]+\)\//g,""),"/"+s||"/"}extractHandler(e,t){let s=new RegExp(`export\\s+const\\s+${t}\\s*=\\s*(\\w+)\\.handle\\(['"]([^'"]+)['"]\\)`),r=e.match(s);if(r)return`${r[1]}.${r[2]}()`;let n=new RegExp(`const\\s*\\{[^}]*${t}[^}]*\\}\\s*=\\s*resource\\(\\s*(\\w+)`),i=e.match(n);return i?`${i[1]}.${{GET:"index/show",POST:"store",PUT:"update",PATCH:"update",DELETE:"destroy"}[t]??t.toLowerCase()}()`:new RegExp(`export\\s+(?:const|async\\s+function)\\s+${t}\\s*(?:=\\s*async)?`).test(e)?"inline handler":"handler"}getMethodColor(e){return{GET:"\x1B[32m",POST:"\x1B[33m",PUT:"\x1B[34m",PATCH:"\x1B[34m",DELETE:"\x1B[31m",HEAD:"\x1B[2m",OPTIONS:"\x1B[2m"}[e]||""}};import{readdirSync as qa}from"fs";import{join as ni}from"path";import{pathToFileURL as Ua}from"url";var yt=class extends h{name="migrate";description="Run pending database migrations";flags=[{name:"rollback",description:"Rollback the last batch of migrations",type:"boolean"},{name:"reset",description:"Reset all migrations",type:"boolean"},{name:"refresh",description:"Reset and re-run all migrations",type:"boolean"},{name:"fresh",description:"Drop all tables and re-run all migrations",type:"boolean"},{name:"status",description:"Show migration status",type:"boolean"},{name:"seed",description:"Run seeders after migrating",type:"boolean"},{name:"force",description:"Force destructive operations in production",type:"boolean"}];destructiveFlags=["reset","refresh","fresh"];async handle(e,t){await this.bootstrap();let s=this.destructiveFlags.find(c=>t[c]);s&&this.isProduction()&&!t.force&&(this.error(`The --${s} flag is destructive and cannot be run in production.`),this.error(`Use --force to run this command in production: npx svelar migrate --${s} --force`),process.exit(1));let{Migrator:r}=await Promise.resolve().then(()=>(ys(),ii)),n=new r,i=ni(process.cwd(),"src","lib","database","migrations"),a=await this.loadMigrations(i);if(t.status){let c=await n.status(a);this.table(["Migration","Status","Batch"],c.map(u=>[u.name,u.ran?"\x1B[32mRan\x1B[0m":"\x1B[33mPending\x1B[0m",u.batch?.toString()??"-"]));return}if(t.rollback){this.info("Rolling back last batch...");let c=await n.rollback(a);if(c.length===0)this.info("Nothing to rollback.");else for(let u of c)this.success(`Rolled back: ${u}`);return}if(t.reset){this.warnDestructive("reset"),this.info("Resetting all migrations...");let c=await n.reset(a);if(c.length===0)this.info("Nothing to reset.");else for(let u of c)this.success(`Rolled back: ${u}`);return}if(t.refresh){this.warnDestructive("refresh"),this.info("Refreshing migrations...");let c=await n.refresh(a);for(let u of c.reset)this.success(`Rolled back: ${u}`);for(let u of c.migrated)this.success(`Migrated: ${u}`);return}if(t.fresh){this.warnDestructive("fresh"),this.info("Dropping all tables...");let c=await n.fresh(a);if(c.dropped.length>0)for(let u of c.dropped)this.success(`Dropped table: ${u}`);this.newLine(),this.info("Re-running all migrations...");for(let u of c.migrated)this.success(`Migrated: ${u}`);return}this.info("Running migrations...");let o=await n.run(a);if(o.length===0)this.info("Nothing to migrate.");else for(let c of o)this.success(`Migrated: ${c}`)}isProduction(){return(process.env.NODE_ENV||process.env.APP_ENV||"development")==="production"}warnDestructive(e){this.isProduction()&&this.warn(`Running --${e} in PRODUCTION with --force.`)}async loadMigrations(e){let t;try{t=qa(e).filter(r=>r.endsWith(".ts")||r.endsWith(".js")).sort()}catch{return this.warn(`Migrations directory not found: ${e}`),[]}let s=[];for(let r of t){let n=ni(e,r);try{let i=await import(Ua(n).href),a=i.default??Object.values(i).find(o=>typeof o=="function"&&o.prototype&&typeof o.prototype.up=="function");a?s.push({name:r.replace(/\.(ts|js)$/,""),timestamp:r.split("_")[0],path:n,migration:new a}):this.warn(`No migration class found in: ${r}`)}catch(i){let a;try{a=i instanceof Error?i.message:String(i)}catch{a=JSON.stringify(i)??"Unknown error (non-stringifiable object)"}this.error(`Failed to load migration ${r}: ${a}`)}}return s}};import{join as vs}from"path";import{pathToFileURL as Fa}from"url";import{existsSync as ai}from"fs";var vt=class extends h{name="seed:run";description="Run database seeders";flags=[{name:"class",description:"Specific seeder class to run",type:"string"}];async handle(e,t){await this.bootstrap();let s=vs(process.cwd(),"src","lib","database","seeders"),r=t.class?vs(s,`${t.class}.ts`):vs(s,"DatabaseSeeder.ts"),n=r;ai(n)||(n=n.replace(/\.ts$/,".js")),ai(n)||(this.error(`Seeder not found: ${r}`),process.exit(1)),this.info("Running seeders...");try{let i=await import(Fa(n).href),a=i.default??i.DatabaseSeeder??Object.values(i).find(c=>typeof c=="function"&&c.prototype&&typeof c.prototype.run=="function");(!a||typeof a!="function")&&(this.error("No seeder class found in file."),process.exit(1)),await new a().run(),this.success("Database seeded successfully.")}catch(i){let a=i instanceof Error?i.message:String(i);this.error(`Seeding failed: ${a}`),i?.stack&&console.error(i.stack),process.exit(1)}}};import{readdirSync as Ka}from"fs";import{join as ui}from"path";import{pathToFileURL as Wa}from"url";var xt=class extends h{name="schedule:run";description="Run the task scheduler";flags=[{name:"once",description:"Run due tasks once and exit",type:"boolean"}];async handle(e,t){await this.bootstrap();let{Scheduler:s}=await Promise.resolve().then(()=>(di(),ci)),r=new s;r.persistToDatabase();let n=ui(process.cwd(),"src","lib","scheduler"),i=await this.loadTasks(n);if(i.length===0){this.warn("No scheduled tasks found in src/lib/scheduler/");return}for(let c of i)r.register(c),this.info(`Registered task: ${c.name}`);if(this.newLine(),t.once){this.info("Running due tasks (once)...");let c=await r.run();if(c.length===0)this.info("No tasks were due.");else for(let u of c)u.success?this.success(`${u.task}: completed in ${u.duration}ms`):this.error(`${u.task}: failed \u2014 ${u.error}`);return}this.info("Scheduler running. Press Ctrl+C to stop."),this.newLine();let a=async()=>{let c=await r.run();for(let u of c){let d=new Date().toISOString().replace("T"," ").slice(0,19);u.success?this.success(`[${d}] ${u.task}: completed in ${u.duration}ms`):this.error(`[${d}] ${u.task}: failed \u2014 ${u.error}`)}};await a();let o=6e4-Date.now()%6e4;this.info(`Next tick aligned to minute boundary in ${Math.round(o/1e3)}s.`),await new Promise(c=>setTimeout(c,o)),await a(),setInterval(a,6e4),await new Promise(()=>{})}async loadTasks(e){let t;try{t=Ka(e).filter(r=>(r.endsWith(".ts")||r.endsWith(".js"))&&!r.startsWith("index")).sort()}catch{return[]}let s=[];for(let r of t){let n=ui(e,r);try{let i=await import(Wa(n).href),a=i.default??Object.values(i).find(o=>typeof o=="function"&&o.prototype&&typeof o.prototype.handle=="function");if(a){let o=new a;o.schedule(),s.push(o)}}catch(i){this.error(`Failed to load task ${r}: ${i.message??i}`)}}return s}};var St=class extends h{name="queue:work";description="Process queued jobs";flags=[{name:"queue",description:'Queue name to process (default: "default")',type:"string",default:"default"},{name:"max-jobs",description:"Stop after processing N jobs",type:"string"},{name:"max-time",description:"Stop after N seconds",type:"string"},{name:"sleep",description:"Sleep N milliseconds between polls (default: 1000)",type:"string",default:"1000"},{name:"once",description:"Process a single job and exit",type:"boolean"}];async handle(e,t){await this.bootstrap();let{Queue:s}=await Promise.resolve().then(()=>(G(),ke)),r=t.queue??"default",n=t["max-jobs"]?parseInt(t["max-jobs"]):void 0,i=t["max-time"]?parseInt(t["max-time"]):void 0,a=t.sleep?parseInt(t.sleep):1e3;if(this.info(`Processing queue "${r}"...`),n&&this.info(`Will stop after ${n} jobs.`),i&&this.info(`Will stop after ${i} seconds.`),this.newLine(),t.once){let u=await s.work({queue:r,maxJobs:1,sleep:0});u===0?this.info("No jobs to process."):this.success(`Processed ${u} job(s).`);return}this.info(`Worker running on "${r}". Press Ctrl+C to stop.`),this.newLine();let o=Date.now(),c=0;for(;;){if(i&&(Date.now()-o)/1e3>=i){this.info(`Max time (${i}s) reached. Stopping.`);break}if(n&&c>=n){this.info(`Max jobs (${n}) reached. Stopping.`);break}let u=await s.work({queue:r,maxJobs:1,sleep:0});if(u>0){c+=u;let d=new Date().toISOString().replace("T"," ").slice(0,19);this.success(`[${d}] Processed ${u} job(s) (total: ${c})`)}else await new Promise(d=>setTimeout(d,a))}this.newLine(),this.info(`Worker stopped. Total jobs processed: ${c}`)}};var Tt=class extends h{name="queue:failed";description="List all failed jobs";async handle(e,t){await this.bootstrap();let{Queue:s}=await Promise.resolve().then(()=>(G(),ke)),r=await s.failed();if(r.length===0){this.info("No failed jobs.");return}this.info(`Found ${r.length} failed job(s):
|
|
1974
|
-
`);for(let
|
|
1975
|
-
`)[0]}`),this.log("")}}};var
|
|
1976
|
-
`);let t=(await import("repl")).start({prompt:"\x1B[36msvelar>\x1B[0m ",useGlobal:!0});try{let s=await Promise.resolve().then(()=>(
|
|
1358
|
+
`;ka(n,c),this.success(`Route created: src/routes/${e}/+server.ts`);for(let u of r)this.info(` ${u.method} /${e} \u2192 ${t}.${u.handler}()`)}inferControllerName(e){let t=e.replace(/^api\//,"").split("/").filter(n=>!n.startsWith("[")),s=t[t.length-1]||t[0]||"Index",r=this.singularize(s);return`${r.charAt(0).toUpperCase()+r.slice(1)}Controller`}inferModuleName(e){return e.replace(/^api\//,"").split("/").filter(s=>!s.startsWith("["))[0]||"app"}inferParamName(e){return"id"}defaultHandler(e){return{GET:"index",POST:"store",PUT:"update",PATCH:"update",DELETE:"destroy"}[e]||e.toLowerCase()}singularize(e){return e.endsWith("ies")?e.slice(0,-3)+"y":e.endsWith("ses")||e.endsWith("xes")||e.endsWith("zes")||e.endsWith("ches")||e.endsWith("shes")?e.slice(0,-2):e.endsWith("s")&&!e.endsWith("ss")?e.slice(0,-1):e}};import{join as Zr,relative as us,sep as ms}from"path";import{existsSync as Da,readdirSync as Ma,readFileSync as ei,statSync as Na}from"fs";var ht=class extends h{name="routes:list";description="List all registered routes";arguments=[];flags=[{name:"json",description:"Output as JSON",type:"boolean"},{name:"api",description:"Show only API routes",type:"boolean"},{name:"method",alias:"m",description:"Filter by HTTP method (GET, POST, etc.)",type:"string"}];async handle(e,t){let s=Zr(process.cwd(),"src","routes");if(!Da(s)){this.error("No src/routes/ directory found.");return}let i=this.scanRoutes(s,s);if(t.api&&(i=i.filter(u=>u.path.startsWith("/api"))),t.method){let u=t.method.toUpperCase();i=i.filter(d=>d.method===u)}if(i.sort((u,d)=>u.path.localeCompare(d.path)||u.method.localeCompare(d.method)),i.length===0){this.warn("No routes found.");return}if(t.json){this.log(JSON.stringify(i,null,2));return}this.log(""),this.log(` \x1B[1mApplication Routes\x1B[0m (${i.length} routes)
|
|
1359
|
+
`);let n=Math.max(6,...i.map(u=>u.method.length)),a=Math.max(4,...i.map(u=>u.path.length)),o=Math.max(7,...i.map(u=>u.handler.length)),c=` ${"METHOD".padEnd(n)} ${"PATH".padEnd(a)} ${"HANDLER".padEnd(o)} FILE`;this.log(`\x1B[2m${c}\x1B[0m`),this.log(`\x1B[2m ${"\u2500".repeat(n)} ${"\u2500".repeat(a)} ${"\u2500".repeat(o)} ${"\u2500".repeat(20)}\x1B[0m`);for(let u of i){let p=`${this.getMethodColor(u.method)}${u.method.padEnd(n)}\x1B[0m`,f=u.path.padEnd(a),b=`\x1B[2m${u.handler.padEnd(o)}\x1B[0m`,C=`\x1B[2m${u.file}\x1B[0m`;this.log(` ${p} ${f} ${b} ${C}`)}this.log("")}scanRoutes(e,t){let s=[],r=Ma(e);for(let i of r){let n=Zr(e,i);if(Na(n).isDirectory()){s.push(...this.scanRoutes(n,t));continue}i==="+server.ts"||i==="+server.js"?s.push(...this.parseServerFile(n,t)):(i==="+page.server.ts"||i==="+page.server.js")&&s.push(...this.parsePageServerFile(n,t))}return s}parseServerFile(e,t){let s=[],r=ei(e,"utf-8"),i=this.filePathToUrl(e,t),n=us(process.cwd(),e).split(ms).join("/"),a=/export\s+(?:const|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g,o;for(;(o=a.exec(r))!==null;){let c=o[1],u=this.extractHandler(r,c);s.push({method:c,path:i,handler:u,file:n})}return s}parsePageServerFile(e,t){let s=[],r=ei(e,"utf-8"),i=this.filePathToUrl(e,t),n=us(process.cwd(),e).split(ms).join("/");/export\s+(const|async\s+function)\s+load\b/.test(r)&&s.push({method:"GET",path:i,handler:"load()",file:n});let a=r.match(/export\s+const\s+actions\s*=\s*\{([^}]+)\}/);if(a){let o=a[1].split(",").map(c=>c.trim().split(":")[0].split("(")[0].trim()).filter(Boolean);for(let c of o)s.push({method:"POST",path:c==="default"?i:`${i}?/${c}`,handler:`actions.${c}()`,file:n})}return s}filePathToUrl(e,t){let s=us(t,e).split(ms).join("/");return s=s.replace(/\/?\+(?:server|page\.server)\.[tj]s$/,""),s=s.replace(/\[\.\.\.(\w+)\]/g,"*$1").replace(/\[\[(\w+)\]\]/g,":$1?").replace(/\[(\w+)\]/g,":$1"),s=s.replace(/\([^)]+\)\//g,""),"/"+s||"/"}extractHandler(e,t){let s=new RegExp(`export\\s+const\\s+${t}\\s*=\\s*(\\w+)\\.handle\\(['"]([^'"]+)['"]\\)`),r=e.match(s);if(r)return`${r[1]}.${r[2]}()`;let i=new RegExp(`const\\s*\\{[^}]*${t}[^}]*\\}\\s*=\\s*resource\\(\\s*(\\w+)`),n=e.match(i);return n?`${n[1]}.${{GET:"index/show",POST:"store",PUT:"update",PATCH:"update",DELETE:"destroy"}[t]??t.toLowerCase()}()`:new RegExp(`export\\s+(?:const|async\\s+function)\\s+${t}\\s*(?:=\\s*async)?`).test(e)?"inline handler":"handler"}getMethodColor(e){return{GET:"\x1B[32m",POST:"\x1B[33m",PUT:"\x1B[34m",PATCH:"\x1B[34m",DELETE:"\x1B[31m",HEAD:"\x1B[2m",OPTIONS:"\x1B[2m"}[e]||""}};import{readdirSync as _a}from"fs";import{join as ri}from"path";import{pathToFileURL as Ia}from"url";var gt=class extends h{name="migrate";description="Run pending database migrations";flags=[{name:"rollback",description:"Rollback the last batch of migrations",type:"boolean"},{name:"reset",description:"Reset all migrations",type:"boolean"},{name:"refresh",description:"Reset and re-run all migrations",type:"boolean"},{name:"fresh",description:"Drop all tables and re-run all migrations",type:"boolean"},{name:"status",description:"Show migration status",type:"boolean"},{name:"seed",description:"Run seeders after migrating",type:"boolean"},{name:"force",description:"Force destructive operations in production",type:"boolean"}];destructiveFlags=["reset","refresh","fresh"];async handle(e,t){await this.bootstrap();let s=this.destructiveFlags.find(c=>t[c]);s&&this.isProduction()&&!t.force&&(this.error(`The --${s} flag is destructive and cannot be run in production.`),this.error(`Use --force to run this command in production: npx svelar migrate --${s} --force`),process.exit(1));let{Migrator:r}=await Promise.resolve().then(()=>(gs(),si)),i=new r,n=ri(process.cwd(),"src","lib","database","migrations"),a=await this.loadMigrations(n);if(t.status){let c=await i.status(a);this.table(["Migration","Status","Batch"],c.map(u=>[u.name,u.ran?"\x1B[32mRan\x1B[0m":"\x1B[33mPending\x1B[0m",u.batch?.toString()??"-"]));return}if(t.rollback){this.info("Rolling back last batch...");let c=await i.rollback(a);if(c.length===0)this.info("Nothing to rollback.");else for(let u of c)this.success(`Rolled back: ${u}`);return}if(t.reset){this.warnDestructive("reset"),this.info("Resetting all migrations...");let c=await i.reset(a);if(c.length===0)this.info("Nothing to reset.");else for(let u of c)this.success(`Rolled back: ${u}`);return}if(t.refresh){this.warnDestructive("refresh"),this.info("Refreshing migrations...");let c=await i.refresh(a);for(let u of c.reset)this.success(`Rolled back: ${u}`);for(let u of c.migrated)this.success(`Migrated: ${u}`);return}if(t.fresh){this.warnDestructive("fresh"),this.info("Dropping all tables...");let c=await i.fresh(a);if(c.dropped.length>0)for(let u of c.dropped)this.success(`Dropped table: ${u}`);this.newLine(),this.info("Re-running all migrations...");for(let u of c.migrated)this.success(`Migrated: ${u}`);return}this.info("Running migrations...");let o=await i.run(a);if(o.length===0)this.info("Nothing to migrate.");else for(let c of o)this.success(`Migrated: ${c}`)}isProduction(){return(process.env.NODE_ENV||process.env.APP_ENV||"development")==="production"}warnDestructive(e){this.isProduction()&&this.warn(`Running --${e} in PRODUCTION with --force.`)}async loadMigrations(e){let t;try{t=_a(e).filter(r=>r.endsWith(".ts")||r.endsWith(".js")).sort()}catch{return this.warn(`Migrations directory not found: ${e}`),[]}let s=[];for(let r of t){let i=ri(e,r);try{let n=await import(Ia(i).href),a=n.default??Object.values(n).find(o=>typeof o=="function"&&o.prototype&&typeof o.prototype.up=="function");a?s.push({name:r.replace(/\.(ts|js)$/,""),timestamp:r.split("_")[0],path:i,migration:new a}):this.warn(`No migration class found in: ${r}`)}catch(n){let a;try{a=n instanceof Error?n.message:String(n)}catch{a=JSON.stringify(n)??"Unknown error (non-stringifiable object)"}this.error(`Failed to load migration ${r}: ${a}`)}}return s}};import{join as fs}from"path";import{pathToFileURL as ja}from"url";import{existsSync as ii}from"fs";var ft=class extends h{name="seed:run";description="Run database seeders";flags=[{name:"class",description:"Specific seeder class to run",type:"string"}];async handle(e,t){await this.bootstrap();let s=fs(process.cwd(),"src","lib","database","seeders"),r=t.class?fs(s,`${t.class}.ts`):fs(s,"DatabaseSeeder.ts"),i=r;ii(i)||(i=i.replace(/\.ts$/,".js")),ii(i)||(this.error(`Seeder not found: ${r}`),process.exit(1)),this.info("Running seeders...");try{let n=await import(ja(i).href),a=n.default??n.DatabaseSeeder??Object.values(n).find(c=>typeof c=="function"&&c.prototype&&typeof c.prototype.run=="function");(!a||typeof a!="function")&&(this.error("No seeder class found in file."),process.exit(1)),await new a().run(),this.success("Database seeded successfully.")}catch(n){let a=n instanceof Error?n.message:String(n);this.error(`Seeding failed: ${a}`),n?.stack&&console.error(n.stack),process.exit(1)}}};import{readdirSync as Ua}from"fs";import{join as ci}from"path";import{pathToFileURL as Ba}from"url";var bt=class extends h{name="schedule:run";description="Run the task scheduler";flags=[{name:"once",description:"Run due tasks once and exit",type:"boolean"}];async handle(e,t){await this.bootstrap();let{Scheduler:s}=await Promise.resolve().then(()=>(li(),oi)),r=new s;r.persistToDatabase();let i=ci(process.cwd(),"src","lib","scheduler"),n=await this.loadTasks(i);if(n.length===0){this.warn("No scheduled tasks found in src/lib/scheduler/");return}for(let c of n)r.register(c),this.info(`Registered task: ${c.name}`);if(this.newLine(),t.once){this.info("Running due tasks (once)...");let c=await r.run();if(c.length===0)this.info("No tasks were due.");else for(let u of c)u.success?this.success(`${u.task}: completed in ${u.duration}ms`):this.error(`${u.task}: failed \u2014 ${u.error}`);return}this.info("Scheduler running. Press Ctrl+C to stop."),this.newLine();let a=async()=>{let c=await r.run();for(let u of c){let d=new Date().toISOString().replace("T"," ").slice(0,19);u.success?this.success(`[${d}] ${u.task}: completed in ${u.duration}ms`):this.error(`[${d}] ${u.task}: failed \u2014 ${u.error}`)}};await a();let o=6e4-Date.now()%6e4;this.info(`Next tick aligned to minute boundary in ${Math.round(o/1e3)}s.`),await new Promise(c=>setTimeout(c,o)),await a(),setInterval(a,6e4),await new Promise(()=>{})}async loadTasks(e){let t;try{t=Ua(e).filter(r=>(r.endsWith(".ts")||r.endsWith(".js"))&&!r.startsWith("index")).sort()}catch{return[]}let s=[];for(let r of t){let i=ci(e,r);try{let n=await import(Ba(i).href),a=n.default??Object.values(n).find(o=>typeof o=="function"&&o.prototype&&typeof o.prototype.handle=="function");if(a){let o=new a;o.schedule(),s.push(o)}}catch(n){this.error(`Failed to load task ${r}: ${n.message??n}`)}}return s}};var Ct=class extends h{name="queue:work";description="Process queued jobs";flags=[{name:"queue",description:'Queue name to process (default: "default")',type:"string",default:"default"},{name:"max-jobs",description:"Stop after processing N jobs",type:"string"},{name:"max-time",description:"Stop after N seconds",type:"string"},{name:"sleep",description:"Sleep N milliseconds between polls (default: 1000)",type:"string",default:"1000"},{name:"once",description:"Process a single job and exit",type:"boolean"}];async handle(e,t){await this.bootstrap();let{Queue:s}=await Promise.resolve().then(()=>(Q(),Ee)),r=t.queue??"default",i=t["max-jobs"]?parseInt(t["max-jobs"]):void 0,n=t["max-time"]?parseInt(t["max-time"]):void 0,a=t.sleep?parseInt(t.sleep):1e3;if(this.info(`Processing queue "${r}"...`),i&&this.info(`Will stop after ${i} jobs.`),n&&this.info(`Will stop after ${n} seconds.`),this.newLine(),t.once){let u=await s.work({queue:r,maxJobs:1,sleep:0});u===0?this.info("No jobs to process."):this.success(`Processed ${u} job(s).`);return}this.info(`Worker running on "${r}". Press Ctrl+C to stop.`),this.newLine();let o=Date.now(),c=0;for(;;){if(n&&(Date.now()-o)/1e3>=n){this.info(`Max time (${n}s) reached. Stopping.`);break}if(i&&c>=i){this.info(`Max jobs (${i}) reached. Stopping.`);break}let u=await s.work({queue:r,maxJobs:1,sleep:0});if(u>0){c+=u;let d=new Date().toISOString().replace("T"," ").slice(0,19);this.success(`[${d}] Processed ${u} job(s) (total: ${c})`)}else await new Promise(d=>setTimeout(d,a))}this.newLine(),this.info(`Worker stopped. Total jobs processed: ${c}`)}};var Pt=class extends h{name="queue:failed";description="List all failed jobs";async handle(e,t){await this.bootstrap();let{Queue:s}=await Promise.resolve().then(()=>(Q(),Ee)),r=await s.failed();if(r.length===0){this.info("No failed jobs.");return}this.info(`Found ${r.length} failed job(s):
|
|
1360
|
+
`);for(let i of r){let n=new Date(i.failedAt*1e3).toISOString().replace("T"," ").slice(0,19);this.log(` ID: ${i.id}`),this.log(` Job: ${i.jobClass}`),this.log(` Queue: ${i.queue}`),this.log(` Date: ${n}`),this.log(` Error: ${i.exception.split(`
|
|
1361
|
+
`)[0]}`),this.log("")}}};var St=class extends h{name="queue:retry";description="Retry a failed job (or all failed jobs)";arguments=["id"];flags=[{name:"all",description:"Retry all failed jobs",type:"boolean",default:!1}];async handle(e,t){await this.bootstrap();let{Queue:s}=await Promise.resolve().then(()=>(Q(),Ee));if(t.all){let n=await s.retryAll();n===0?this.info("No failed jobs to retry."):this.success(`Retried ${n} job(s).`);return}let r=e[0];r||(this.error("Please provide a failed job ID, or use --all to retry all."),process.exit(1)),await s.retry(r)?this.success(`Job ${r} has been pushed back onto the queue.`):this.error(`Failed job with ID "${r}" not found.`)}};var Rt=class extends h{name="queue:flush";description="Delete all failed job records";async handle(e,t){await this.bootstrap();let{Queue:s}=await Promise.resolve().then(()=>(Q(),Ee)),r=await s.flushFailed();r===0?this.info("No failed jobs to flush."):this.success(`Flushed ${r} failed job record(s).`)}};var Xt=class extends h{name="tinker";description="Start an interactive REPL with Svelar preloaded";flags=[];async handle(){await this.bootstrap(),this.info("Starting Svelar Tinker..."),this.log(`Type .exit to quit. All Svelar modules are available.
|
|
1362
|
+
`);let t=(await import("repl")).start({prompt:"\x1B[36msvelar>\x1B[0m ",useGlobal:!0});try{let s=await Promise.resolve().then(()=>(on(),an));for(let[r,i]of Object.entries(s))t.context[r]=i;t.context.DB=s.Connection,t.context.Schema=s.Schema,this.log("Available: Model, QueryBuilder, Connection, Schema, Hash, Cache, Event, Log, ..."),this.log("")}catch(s){this.warn(`Could not preload all modules: ${s.message}`)}try{let{readdirSync:s}=await import("fs"),{join:r}=await import("path"),{pathToFileURL:i}=await import("url"),n=r(process.cwd(),"src","lib","models"),a=s(n).filter(o=>o.endsWith(".ts")||o.endsWith(".js"));for(let o of a)try{let c=await import(i(r(n,o)).href);for(let[u,d]of Object.entries(c))typeof d=="function"&&(t.context[u]=d)}catch{}a.length>0&&this.log(`Loaded models: ${a.map(o=>o.replace(/\.(ts|js)$/,"")).join(", ")}`)}catch{}await new Promise(s=>{t.on("exit",s)})}};var m=class{static packageJson(e,t="0.4.0"){return JSON.stringify({name:e,version:"0.0.1",private:!0,type:"module",scripts:{dev:"vite dev",build:"vite build",preview:"vite preview",migrate:"npx svelar migrate","migrate:rollback":"npx svelar migrate --rollback","migrate:refresh":"npx svelar migrate --refresh",seed:"npx svelar seed:run"},devDependencies:{"@sveltejs/adapter-auto":"^3.0.0","@sveltejs/kit":"^2.55.0","@sveltejs/vite-plugin-svelte":"^5.0.0","@tailwindcss/vite":"^4.2.2","lucide-svelte":"^0.468.0",svelte:"^5.0.0","svelte-check":"^4.0.0",tailwindcss:"^4.2.2",typescript:"^5.7.0",vite:"^6.0.0"},dependencies:{"better-sqlite3":"^11.0.0","drizzle-orm":"^0.38.0","@beeblock/svelar":`^${t}`,exceljs:"^4.4.0",pdfkit:"^0.18.0","sveltekit-superforms":"^2.22.0",zod:"^3.23.0"}},null,2)+`
|
|
1977
1363
|
`}static svelteConfig(){return`import adapter from '@sveltejs/adapter-auto';
|
|
1978
1364
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
1979
1365
|
|
|
@@ -2088,6 +1474,7 @@ export default defineConfig({
|
|
|
2088
1474
|
</body>
|
|
2089
1475
|
</html>
|
|
2090
1476
|
`}static appCss(){return`@import "tailwindcss";
|
|
1477
|
+
@source "../node_modules/@beeblock/svelar/src/ui";
|
|
2091
1478
|
|
|
2092
1479
|
@theme {
|
|
2093
1480
|
--color-brand: #ff3e00;
|
|
@@ -2239,14 +1626,14 @@ export { Connection, Hash, Broadcast };
|
|
|
2239
1626
|
|
|
2240
1627
|
import { createSvelarApp } from '@beeblock/svelar/hooks';
|
|
2241
1628
|
import { DatabaseSessionStore } from '@beeblock/svelar/session';
|
|
1629
|
+
import { env } from '$env/dynamic/private';
|
|
2242
1630
|
|
|
2243
1631
|
// Import app.ts to trigger database + hashing + auth configuration
|
|
2244
1632
|
import { auth } from './app.js';
|
|
2245
1633
|
|
|
2246
1634
|
export const { handle, handleError } = createSvelarApp({
|
|
2247
1635
|
auth,
|
|
2248
|
-
|
|
2249
|
-
secret: process.env.APP_KEY || 'change-me-in-production',
|
|
1636
|
+
secret: env.APP_KEY,
|
|
2250
1637
|
sessionStore: new DatabaseSessionStore(),
|
|
2251
1638
|
csrfExcludePaths: ['/api/webhooks', '/api/internal/'],
|
|
2252
1639
|
});
|
|
@@ -2290,11 +1677,17 @@ DB_PATH=database.db
|
|
|
2290
1677
|
# RESEND_API_KEY=re_your-resend-api-key
|
|
2291
1678
|
|
|
2292
1679
|
# Redis (optional \u2014 needed for BullMQ queue and Redis cache/session)
|
|
2293
|
-
#
|
|
1680
|
+
# REDIS_HOST=localhost
|
|
1681
|
+
# REDIS_PORT=6379
|
|
1682
|
+
# REDIS_PASSWORD=
|
|
2294
1683
|
|
|
2295
1684
|
# Queue driver (sync = immediate, redis = background via BullMQ)
|
|
2296
1685
|
# QUEUE_DRIVER=sync
|
|
2297
1686
|
|
|
1687
|
+
# Meilisearch (optional \u2014 full-text search engine)
|
|
1688
|
+
# MEILISEARCH_HOST=http://localhost:7700
|
|
1689
|
+
# MEILISEARCH_KEY=
|
|
1690
|
+
|
|
2298
1691
|
# PDF (default driver is pdfkit \u2014 no config needed)
|
|
2299
1692
|
# Switch to Gotenberg for pixel-perfect HTML rendering:
|
|
2300
1693
|
# PDF_DRIVER=gotenberg
|
|
@@ -4410,6 +3803,66 @@ export const load: PageServerLoad = async ({ url }) => {
|
|
|
4410
3803
|
`}static dashboardLayoutServer(){return`import { guardAuth } from '@beeblock/svelar/auth';
|
|
4411
3804
|
|
|
4412
3805
|
export const load = guardAuth();
|
|
3806
|
+
`}static dashboardLayoutSvelte(){return`<script lang="ts">
|
|
3807
|
+
import { page } from '$app/stores';
|
|
3808
|
+
import type { Snippet } from 'svelte';
|
|
3809
|
+
import { Icon } from '@beeblock/svelar/ui';
|
|
3810
|
+
import LayoutDashboard from 'lucide-svelte/icons/layout-dashboard';
|
|
3811
|
+
import KeyRound from 'lucide-svelte/icons/key-round';
|
|
3812
|
+
import Users from 'lucide-svelte/icons/users';
|
|
3813
|
+
import Settings from 'lucide-svelte/icons/settings';
|
|
3814
|
+
|
|
3815
|
+
interface Props {
|
|
3816
|
+
data: any;
|
|
3817
|
+
children: Snippet;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
let { data, children }: Props = $props();
|
|
3821
|
+
|
|
3822
|
+
const navItems = [
|
|
3823
|
+
{ href: '/dashboard', label: 'Overview', exact: true, icon: LayoutDashboard },
|
|
3824
|
+
{ href: '/dashboard/api-keys', label: 'API Keys', exact: false, icon: KeyRound },
|
|
3825
|
+
{ href: '/dashboard/team', label: 'Team', exact: false, icon: Users },
|
|
3826
|
+
];
|
|
3827
|
+
|
|
3828
|
+
function isActive(href: string, exact: boolean, pathname: string): boolean {
|
|
3829
|
+
return exact ? pathname === href : pathname.startsWith(href);
|
|
3830
|
+
}
|
|
3831
|
+
</script>
|
|
3832
|
+
|
|
3833
|
+
<div class="flex min-h-[calc(100vh-130px)]">
|
|
3834
|
+
<aside class="w-64 border-r border-gray-200 bg-gray-50 hidden md:block">
|
|
3835
|
+
<nav class="p-4 space-y-1">
|
|
3836
|
+
{#each navItems as item}
|
|
3837
|
+
{@const active = isActive(item.href, item.exact, $page.url.pathname)}
|
|
3838
|
+
<a
|
|
3839
|
+
href={item.href}
|
|
3840
|
+
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors {active ? 'bg-brand/10 text-brand' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-900'}"
|
|
3841
|
+
>
|
|
3842
|
+
<Icon icon={item.icon} size={20} class={active ? 'text-brand' : 'text-gray-400'} />
|
|
3843
|
+
{item.label}
|
|
3844
|
+
</a>
|
|
3845
|
+
{/each}
|
|
3846
|
+
</nav>
|
|
3847
|
+
|
|
3848
|
+
{#if data.user?.role === 'admin'}
|
|
3849
|
+
<div class="border-t border-gray-200 mx-4 my-2"></div>
|
|
3850
|
+
<div class="p-4 pt-0">
|
|
3851
|
+
<a
|
|
3852
|
+
href="/admin"
|
|
3853
|
+
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900 transition-colors"
|
|
3854
|
+
>
|
|
3855
|
+
<Icon icon={Settings} size={20} class="text-gray-400" />
|
|
3856
|
+
Admin Panel
|
|
3857
|
+
</a>
|
|
3858
|
+
</div>
|
|
3859
|
+
{/if}
|
|
3860
|
+
</aside>
|
|
3861
|
+
|
|
3862
|
+
<div class="flex-1 p-6 md:p-8">
|
|
3863
|
+
{@render children()}
|
|
3864
|
+
</div>
|
|
3865
|
+
</div>
|
|
4413
3866
|
`}static dashboardPageServer(){return`import type { PageServerLoad } from './$types';
|
|
4414
3867
|
import { ApiKeys } from '@beeblock/svelar/api-keys';
|
|
4415
3868
|
import { Teams } from '@beeblock/svelar/teams';
|
|
@@ -4982,15 +4435,91 @@ export const actions: Actions = {
|
|
|
4982
4435
|
`}static adminLayoutServer(){return`import { guardAuth } from '@beeblock/svelar/auth';
|
|
4983
4436
|
|
|
4984
4437
|
export const load = guardAuth('/dashboard', { role: 'admin' });
|
|
4438
|
+
`}static adminLayoutSvelte(){return`<script lang="ts">
|
|
4439
|
+
import { page } from '$app/stores';
|
|
4440
|
+
import type { Snippet } from 'svelte';
|
|
4441
|
+
import { Icon } from '@beeblock/svelar/ui';
|
|
4442
|
+
import LayoutDashboard from 'lucide-svelte/icons/layout-dashboard';
|
|
4443
|
+
import Users from 'lucide-svelte/icons/users';
|
|
4444
|
+
import ShieldCheck from 'lucide-svelte/icons/shield-check';
|
|
4445
|
+
import Lock from 'lucide-svelte/icons/lock';
|
|
4446
|
+
import ListTodo from 'lucide-svelte/icons/list-todo';
|
|
4447
|
+
import Clock from 'lucide-svelte/icons/clock';
|
|
4448
|
+
import FileText from 'lucide-svelte/icons/file-text';
|
|
4449
|
+
import ArrowLeft from 'lucide-svelte/icons/arrow-left';
|
|
4450
|
+
|
|
4451
|
+
interface Props {
|
|
4452
|
+
data: any;
|
|
4453
|
+
children: Snippet;
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
let { data, children }: Props = $props();
|
|
4457
|
+
|
|
4458
|
+
const navItems = [
|
|
4459
|
+
{ tab: 'overview', label: 'Overview', icon: LayoutDashboard },
|
|
4460
|
+
{ tab: 'users', label: 'Users', icon: Users },
|
|
4461
|
+
{ tab: 'roles', label: 'Roles', icon: ShieldCheck },
|
|
4462
|
+
{ tab: 'permissions', label: 'Permissions', icon: Lock },
|
|
4463
|
+
{ tab: 'queue', label: 'Queue', icon: ListTodo },
|
|
4464
|
+
{ tab: 'scheduler', label: 'Scheduler', icon: Clock },
|
|
4465
|
+
{ tab: 'logs', label: 'Logs', icon: FileText },
|
|
4466
|
+
];
|
|
4467
|
+
|
|
4468
|
+
function isActive(tab: string, currentUrl: URL): boolean {
|
|
4469
|
+
const activeTab = currentUrl.searchParams.get('tab') ?? 'overview';
|
|
4470
|
+
return activeTab === tab;
|
|
4471
|
+
}
|
|
4472
|
+
</script>
|
|
4473
|
+
|
|
4474
|
+
<div class="flex min-h-[calc(100vh-130px)]">
|
|
4475
|
+
<aside class="w-64 border-r border-gray-200 bg-gray-50 hidden md:block">
|
|
4476
|
+
<div class="p-4 border-b border-gray-200">
|
|
4477
|
+
<p class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Administration</p>
|
|
4478
|
+
</div>
|
|
4479
|
+
<nav class="p-4 space-y-1">
|
|
4480
|
+
{#each navItems as item}
|
|
4481
|
+
{@const active = isActive(item.tab, $page.url)}
|
|
4482
|
+
<a
|
|
4483
|
+
href="/admin?tab={item.tab}"
|
|
4484
|
+
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors {active ? 'bg-brand/10 text-brand' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-900'}"
|
|
4485
|
+
>
|
|
4486
|
+
<Icon icon={item.icon} size={20} class={active ? 'text-brand' : 'text-gray-400'} />
|
|
4487
|
+
{item.label}
|
|
4488
|
+
</a>
|
|
4489
|
+
{/each}
|
|
4490
|
+
</nav>
|
|
4491
|
+
|
|
4492
|
+
<div class="border-t border-gray-200 mx-4 my-2"></div>
|
|
4493
|
+
<div class="p-4 pt-0">
|
|
4494
|
+
<a
|
|
4495
|
+
href="/dashboard"
|
|
4496
|
+
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900 transition-colors"
|
|
4497
|
+
>
|
|
4498
|
+
<Icon icon={ArrowLeft} size={20} class="text-gray-400" />
|
|
4499
|
+
Back to Dashboard
|
|
4500
|
+
</a>
|
|
4501
|
+
</div>
|
|
4502
|
+
</aside>
|
|
4503
|
+
|
|
4504
|
+
<div class="flex-1 p-6 md:p-8">
|
|
4505
|
+
{@render children()}
|
|
4506
|
+
</div>
|
|
4507
|
+
</div>
|
|
4985
4508
|
`}static adminPageServer(){return`import type { ServerLoadEvent } from '@sveltejs/kit';
|
|
4986
4509
|
import { User } from '$lib/models/User.js';
|
|
4987
4510
|
import { Post } from '$lib/models/Post.js';
|
|
4511
|
+
import { JobMonitor } from '@beeblock/svelar/queue/JobMonitor';
|
|
4512
|
+
import { ScheduleMonitor } from '@beeblock/svelar/scheduler/ScheduleMonitor';
|
|
4513
|
+
import { LogViewer } from '@beeblock/svelar/logging/LogViewer';
|
|
4988
4514
|
import { Permissions } from '@beeblock/svelar/permissions';
|
|
4989
4515
|
|
|
4990
4516
|
export async function load(event: ServerLoadEvent) {
|
|
4991
4517
|
const user = event.locals.user;
|
|
4992
4518
|
|
|
4519
|
+
// Fetch all users
|
|
4993
4520
|
const users = await User.query().get();
|
|
4521
|
+
|
|
4522
|
+
// Fetch stats
|
|
4994
4523
|
const userCount = users.length;
|
|
4995
4524
|
const postCount = await Post.count();
|
|
4996
4525
|
|
|
@@ -4999,15 +4528,66 @@ export async function load(event: ServerLoadEvent) {
|
|
|
4999
4528
|
user: users.filter((u: any) => u.role === 'user').length,
|
|
5000
4529
|
};
|
|
5001
4530
|
|
|
4531
|
+
// Queue stats from JobMonitor
|
|
4532
|
+
let queueCounts = { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0, total: 0 };
|
|
4533
|
+
try {
|
|
4534
|
+
queueCounts = await JobMonitor.getCounts('default');
|
|
4535
|
+
} catch { /* sync/memory driver \u2014 no counts available */ }
|
|
4536
|
+
|
|
4537
|
+
// Scheduler tasks from ScheduleMonitor
|
|
4538
|
+
let scheduledTasks: any[] = [];
|
|
4539
|
+
try {
|
|
4540
|
+
scheduledTasks = await ScheduleMonitor.listTasks();
|
|
4541
|
+
} catch { /* scheduler not configured */ }
|
|
4542
|
+
|
|
4543
|
+
// Recent logs from LogViewer
|
|
4544
|
+
let recentLogs: any[] = [];
|
|
4545
|
+
let logStats = { totalEntries: 0, byLevel: {} as Record<string, number>, byChannel: {} };
|
|
4546
|
+
try {
|
|
4547
|
+
recentLogs = LogViewer.query({ limit: 50 });
|
|
4548
|
+
logStats = LogViewer.getStats();
|
|
4549
|
+
} catch { /* no logs yet */ }
|
|
4550
|
+
|
|
4551
|
+
// System health
|
|
4552
|
+
const memUsage = process.memoryUsage();
|
|
4553
|
+
const health = {
|
|
4554
|
+
status: 'ok',
|
|
4555
|
+
uptime: process.uptime(),
|
|
4556
|
+
memoryUsedMB: Math.round(memUsage.heapUsed / 1024 / 1024),
|
|
4557
|
+
memoryTotalMB: Math.round(memUsage.heapTotal / 1024 / 1024),
|
|
4558
|
+
memoryPercent: Math.round((memUsage.heapUsed / memUsage.heapTotal) * 100),
|
|
4559
|
+
};
|
|
4560
|
+
|
|
4561
|
+
// Roles & Permissions
|
|
5002
4562
|
let roles: any[] = [];
|
|
5003
4563
|
let permissions: any[] = [];
|
|
4564
|
+
let rolePermissionsMap: Record<number, number[]> = {};
|
|
4565
|
+
let userRolesMap: Record<number, any[]> = {};
|
|
4566
|
+
let userDirectPermsMap: Record<number, any[]> = {};
|
|
5004
4567
|
try {
|
|
5005
4568
|
roles = await Permissions.allRoles();
|
|
5006
4569
|
permissions = await Permissions.allPermissions();
|
|
5007
|
-
|
|
4570
|
+
|
|
4571
|
+
// Load permissions for each role
|
|
4572
|
+
for (const role of roles) {
|
|
4573
|
+
const rolePerms = await Permissions.getRolePermissions(role.id);
|
|
4574
|
+
rolePermissionsMap[role.id] = rolePerms.map((p: any) => p.id);
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4577
|
+
// Load roles and direct permissions for each user
|
|
4578
|
+
for (const u of users) {
|
|
4579
|
+
userRolesMap[u.id] = await Permissions.getModelRoles('User', u.id);
|
|
4580
|
+
userDirectPermsMap[u.id] = await Permissions.getModelDirectPermissions('User', u.id);
|
|
4581
|
+
}
|
|
4582
|
+
} catch { /* permissions tables may not exist yet */ }
|
|
5008
4583
|
|
|
5009
4584
|
return {
|
|
5010
|
-
user: {
|
|
4585
|
+
user: {
|
|
4586
|
+
id: user.id,
|
|
4587
|
+
name: user.name,
|
|
4588
|
+
email: user.email,
|
|
4589
|
+
role: user.role,
|
|
4590
|
+
},
|
|
5011
4591
|
users: users.map((u: any) => ({
|
|
5012
4592
|
id: u.id,
|
|
5013
4593
|
name: u.name,
|
|
@@ -5015,163 +4595,1009 @@ export async function load(event: ServerLoadEvent) {
|
|
|
5015
4595
|
role: u.role,
|
|
5016
4596
|
created_at: u.created_at,
|
|
5017
4597
|
})),
|
|
5018
|
-
stats: {
|
|
5019
|
-
|
|
5020
|
-
|
|
4598
|
+
stats: {
|
|
4599
|
+
userCount,
|
|
4600
|
+
postCount,
|
|
4601
|
+
roleDistribution,
|
|
4602
|
+
},
|
|
4603
|
+
queueCounts,
|
|
4604
|
+
scheduledTasks: scheduledTasks.map((t: any) => ({
|
|
4605
|
+
name: t.name,
|
|
4606
|
+
expression: t.expression,
|
|
4607
|
+
humanReadable: t.humanReadable,
|
|
4608
|
+
enabled: t.enabled,
|
|
4609
|
+
isRunning: t.isRunning,
|
|
4610
|
+
lastRun: t.lastRun?.toISOString() ?? null,
|
|
4611
|
+
lastStatus: t.lastStatus ?? null,
|
|
4612
|
+
nextRun: t.nextRun?.toISOString() ?? null,
|
|
4613
|
+
})),
|
|
4614
|
+
recentLogs: recentLogs.map((l: any) => ({
|
|
4615
|
+
timestamp: l.timestamp,
|
|
4616
|
+
level: l.level,
|
|
4617
|
+
channel: l.channel,
|
|
4618
|
+
message: l.message,
|
|
4619
|
+
})),
|
|
4620
|
+
logStats,
|
|
4621
|
+
health,
|
|
4622
|
+
roles: roles.map((r: any) => ({
|
|
4623
|
+
id: r.id,
|
|
4624
|
+
name: r.name,
|
|
4625
|
+
guard: r.guard,
|
|
4626
|
+
description: r.description,
|
|
4627
|
+
created_at: r.created_at,
|
|
4628
|
+
})),
|
|
4629
|
+
permissions: permissions.map((p: any) => ({
|
|
4630
|
+
id: p.id,
|
|
4631
|
+
name: p.name,
|
|
4632
|
+
guard: p.guard,
|
|
4633
|
+
description: p.description,
|
|
4634
|
+
created_at: p.created_at,
|
|
4635
|
+
})),
|
|
4636
|
+
rolePermissionsMap,
|
|
4637
|
+
userRolesMap: Object.fromEntries(
|
|
4638
|
+
Object.entries(userRolesMap).map(([uid, roles]) => [
|
|
4639
|
+
uid,
|
|
4640
|
+
roles.map((r: any) => ({ id: r.id, name: r.name })),
|
|
4641
|
+
]),
|
|
4642
|
+
),
|
|
4643
|
+
userDirectPermsMap: Object.fromEntries(
|
|
4644
|
+
Object.entries(userDirectPermsMap).map(([uid, perms]) => [
|
|
4645
|
+
uid,
|
|
4646
|
+
perms.map((p: any) => ({ id: p.id, name: p.name })),
|
|
4647
|
+
]),
|
|
4648
|
+
),
|
|
5021
4649
|
};
|
|
5022
4650
|
}
|
|
5023
4651
|
`}static adminPageSvelte(){return`<script lang="ts">
|
|
5024
|
-
import {
|
|
4652
|
+
import { page } from '$app/stores';
|
|
4653
|
+
import { Button, Badge, Card, CardHeader, CardTitle, CardDescription, CardContent, Alert, Input, Label } from '@beeblock/svelar/ui';
|
|
5025
4654
|
|
|
5026
4655
|
let { data } = $props();
|
|
5027
4656
|
let users = $state(data.users);
|
|
4657
|
+
let message = $state('');
|
|
4658
|
+
let messageType = $state<'success' | 'error'>('success');
|
|
5028
4659
|
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
4660
|
+
// Real data from server
|
|
4661
|
+
let queueCounts = $state(data.queueCounts);
|
|
4662
|
+
let scheduledTasks = $state(data.scheduledTasks);
|
|
4663
|
+
let recentLogs = $state(data.recentLogs);
|
|
4664
|
+
let logStats = $state(data.logStats);
|
|
4665
|
+
let health = $state(data.health);
|
|
4666
|
+
|
|
4667
|
+
// Roles & Permissions
|
|
4668
|
+
let roles = $state(data.roles ?? []);
|
|
4669
|
+
let permissions = $state(data.permissions ?? []);
|
|
4670
|
+
let rolePermissionsMap = $state<Record<number, number[]>>(data.rolePermissionsMap ?? {});
|
|
4671
|
+
let userRolesMap = $state<Record<number, { id: number; name: string }[]>>(data.userRolesMap ?? {});
|
|
4672
|
+
let userDirectPermsMap = $state<Record<number, { id: number; name: string }[]>>(data.userDirectPermsMap ?? {});
|
|
4673
|
+
|
|
4674
|
+
// Form state
|
|
4675
|
+
let newRoleName = $state('');
|
|
4676
|
+
let newRoleDesc = $state('');
|
|
4677
|
+
let newPermName = $state('');
|
|
4678
|
+
let newPermDesc = $state('');
|
|
4679
|
+
let showRoleForm = $state(false);
|
|
4680
|
+
let showPermForm = $state(false);
|
|
4681
|
+
|
|
4682
|
+
let logFilter = $state<'all' | 'info' | 'warn' | 'error'>('all');
|
|
4683
|
+
|
|
4684
|
+
const activeTab = $derived($page.url.searchParams.get('tab') ?? 'overview');
|
|
4685
|
+
|
|
4686
|
+
const filteredLogs = $derived(
|
|
4687
|
+
logFilter === 'all' ? recentLogs : recentLogs.filter((log: any) => log.level === logFilter)
|
|
4688
|
+
);
|
|
4689
|
+
|
|
4690
|
+
function flash(msg: string, type: 'success' | 'error' = 'success') {
|
|
4691
|
+
message = msg;
|
|
4692
|
+
messageType = type;
|
|
4693
|
+
}
|
|
4694
|
+
|
|
4695
|
+
async function refreshDashboard() {
|
|
4696
|
+
try {
|
|
4697
|
+
const res = await fetch('/api/admin/stats');
|
|
4698
|
+
if (res.ok) {
|
|
4699
|
+
const stats = await res.json();
|
|
4700
|
+
if (stats.queue) {
|
|
4701
|
+
queueCounts = stats.queue.queues?.default ?? queueCounts;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
} catch { /* ignore refresh errors */ }
|
|
4705
|
+
}
|
|
4706
|
+
|
|
4707
|
+
async function updateUserRole(userId: number, newRole: string) {
|
|
4708
|
+
try {
|
|
4709
|
+
const res = await fetch('/api/admin/users', {
|
|
4710
|
+
method: 'PUT',
|
|
4711
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4712
|
+
body: JSON.stringify({ userId, role: newRole }),
|
|
4713
|
+
});
|
|
4714
|
+
|
|
4715
|
+
if (res.ok) {
|
|
4716
|
+
flash('User role updated successfully!');
|
|
4717
|
+
await refreshUsers();
|
|
4718
|
+
} else {
|
|
4719
|
+
const error = await res.json();
|
|
4720
|
+
flash(error.message || 'Failed to update user role', 'error');
|
|
4721
|
+
}
|
|
4722
|
+
} catch {
|
|
4723
|
+
flash('Network error', 'error');
|
|
5037
4724
|
}
|
|
5038
4725
|
}
|
|
5039
4726
|
|
|
5040
|
-
async function deleteUser(userId: number) {
|
|
5041
|
-
if (!confirm(
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
4727
|
+
async function deleteUser(userId: number, userName: string) {
|
|
4728
|
+
if (!confirm(\`Are you sure you want to delete \${userName}? This cannot be undone.\`)) {
|
|
4729
|
+
return;
|
|
4730
|
+
}
|
|
4731
|
+
|
|
4732
|
+
try {
|
|
4733
|
+
const res = await fetch('/api/admin/users', {
|
|
4734
|
+
method: 'DELETE',
|
|
4735
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4736
|
+
body: JSON.stringify({ userId }),
|
|
4737
|
+
});
|
|
4738
|
+
|
|
4739
|
+
if (res.ok) {
|
|
4740
|
+
flash('User deleted successfully!');
|
|
4741
|
+
await refreshUsers();
|
|
4742
|
+
} else {
|
|
4743
|
+
const error = await res.json();
|
|
4744
|
+
flash(error.message || 'Failed to delete user', 'error');
|
|
4745
|
+
}
|
|
4746
|
+
} catch {
|
|
4747
|
+
flash('Network error', 'error');
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
|
|
4751
|
+
async function refreshUsers() {
|
|
4752
|
+
const res = await fetch('/api/admin/users');
|
|
5047
4753
|
if (res.ok) {
|
|
5048
|
-
|
|
4754
|
+
const data = await res.json();
|
|
4755
|
+
users = data;
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
|
|
4759
|
+
async function retryJob(jobId: string) {
|
|
4760
|
+
try {
|
|
4761
|
+
const res = await fetch(\`/api/admin/queue/\${jobId}/retry\`, { method: 'POST' });
|
|
4762
|
+
if (res.ok) {
|
|
4763
|
+
flash('Job queued for retry');
|
|
4764
|
+
await refreshQueue();
|
|
4765
|
+
}
|
|
4766
|
+
} catch {
|
|
4767
|
+
flash('Failed to retry job', 'error');
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
|
|
4771
|
+
async function refreshQueue() {
|
|
4772
|
+
try {
|
|
4773
|
+
const res = await fetch('/api/admin/queue');
|
|
4774
|
+
if (res.ok) {
|
|
4775
|
+
const data = await res.json();
|
|
4776
|
+
queueCounts = data.counts;
|
|
4777
|
+
}
|
|
4778
|
+
} catch { /* ignore */ }
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
async function runTask(taskName: string) {
|
|
4782
|
+
try {
|
|
4783
|
+
const res = await fetch(\`/api/admin/scheduler/\${taskName}/run\`, { method: 'POST' });
|
|
4784
|
+
if (res.ok) {
|
|
4785
|
+
flash(\`Task '\${taskName}' triggered\`);
|
|
4786
|
+
} else {
|
|
4787
|
+
const err = await res.json();
|
|
4788
|
+
flash(err.error || 'Failed to run task', 'error');
|
|
4789
|
+
}
|
|
4790
|
+
} catch {
|
|
4791
|
+
flash('Failed to run task', 'error');
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
async function toggleTask(taskName: string, enabled: boolean) {
|
|
4796
|
+
try {
|
|
4797
|
+
const res = await fetch(\`/api/admin/scheduler/\${taskName}/toggle\`, {
|
|
4798
|
+
method: 'POST',
|
|
4799
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4800
|
+
body: JSON.stringify({ enabled }),
|
|
4801
|
+
});
|
|
4802
|
+
if (res.ok) {
|
|
4803
|
+
scheduledTasks = scheduledTasks.map((t: any) =>
|
|
4804
|
+
t.name === taskName ? { ...t, enabled } : t
|
|
4805
|
+
);
|
|
4806
|
+
flash(\`Task '\${taskName}' \${enabled ? 'enabled' : 'disabled'}\`);
|
|
4807
|
+
}
|
|
4808
|
+
} catch {
|
|
4809
|
+
flash('Failed to toggle task', 'error');
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
|
|
4813
|
+
// -- Roles CRUD --
|
|
4814
|
+
|
|
4815
|
+
async function createRole() {
|
|
4816
|
+
if (!newRoleName.trim()) return;
|
|
4817
|
+
try {
|
|
4818
|
+
const res = await fetch('/api/admin/roles', {
|
|
4819
|
+
method: 'POST',
|
|
4820
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4821
|
+
body: JSON.stringify({ name: newRoleName.trim(), description: newRoleDesc.trim() || undefined }),
|
|
4822
|
+
});
|
|
4823
|
+
if (res.ok) {
|
|
4824
|
+
const role = await res.json();
|
|
4825
|
+
roles = [...roles, role];
|
|
4826
|
+
rolePermissionsMap[role.id] = [];
|
|
4827
|
+
newRoleName = '';
|
|
4828
|
+
newRoleDesc = '';
|
|
4829
|
+
showRoleForm = false;
|
|
4830
|
+
flash('Role created');
|
|
4831
|
+
} else {
|
|
4832
|
+
const err = await res.json();
|
|
4833
|
+
flash(err.message || 'Failed to create role', 'error');
|
|
4834
|
+
}
|
|
4835
|
+
} catch {
|
|
4836
|
+
flash('Network error', 'error');
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
async function deleteRole(name: string) {
|
|
4841
|
+
if (!confirm(\`Delete role "\${name}"? This will remove it from all users.\`)) return;
|
|
4842
|
+
try {
|
|
4843
|
+
const res = await fetch('/api/admin/roles', {
|
|
4844
|
+
method: 'DELETE',
|
|
4845
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4846
|
+
body: JSON.stringify({ name }),
|
|
4847
|
+
});
|
|
4848
|
+
if (res.ok) {
|
|
4849
|
+
const deleted = roles.find((r: any) => r.name === name);
|
|
4850
|
+
roles = roles.filter((r: any) => r.name !== name);
|
|
4851
|
+
if (deleted) delete rolePermissionsMap[deleted.id];
|
|
4852
|
+
for (const uid of Object.keys(userRolesMap)) {
|
|
4853
|
+
userRolesMap[uid as any] = userRolesMap[uid as any].filter((r: any) => r.name !== name);
|
|
4854
|
+
}
|
|
4855
|
+
flash('Role deleted');
|
|
4856
|
+
} else {
|
|
4857
|
+
const err = await res.json();
|
|
4858
|
+
flash(err.message || 'Failed to delete role', 'error');
|
|
4859
|
+
}
|
|
4860
|
+
} catch {
|
|
4861
|
+
flash('Network error', 'error');
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4865
|
+
// -- Permissions CRUD --
|
|
4866
|
+
|
|
4867
|
+
async function createPermission() {
|
|
4868
|
+
if (!newPermName.trim()) return;
|
|
4869
|
+
try {
|
|
4870
|
+
const res = await fetch('/api/admin/permissions', {
|
|
4871
|
+
method: 'POST',
|
|
4872
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4873
|
+
body: JSON.stringify({ name: newPermName.trim(), description: newPermDesc.trim() || undefined }),
|
|
4874
|
+
});
|
|
4875
|
+
if (res.ok) {
|
|
4876
|
+
const perm = await res.json();
|
|
4877
|
+
permissions = [...permissions, perm];
|
|
4878
|
+
newPermName = '';
|
|
4879
|
+
newPermDesc = '';
|
|
4880
|
+
showPermForm = false;
|
|
4881
|
+
flash('Permission created');
|
|
4882
|
+
} else {
|
|
4883
|
+
const err = await res.json();
|
|
4884
|
+
flash(err.message || 'Failed to create permission', 'error');
|
|
4885
|
+
}
|
|
4886
|
+
} catch {
|
|
4887
|
+
flash('Network error', 'error');
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4890
|
+
|
|
4891
|
+
async function deletePermission(name: string) {
|
|
4892
|
+
if (!confirm(\`Delete permission "\${name}"? This will revoke it from all roles and users.\`)) return;
|
|
4893
|
+
try {
|
|
4894
|
+
const res = await fetch('/api/admin/permissions', {
|
|
4895
|
+
method: 'DELETE',
|
|
4896
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4897
|
+
body: JSON.stringify({ name }),
|
|
4898
|
+
});
|
|
4899
|
+
if (res.ok) {
|
|
4900
|
+
const deleted = permissions.find((p: any) => p.name === name);
|
|
4901
|
+
permissions = permissions.filter((p: any) => p.name !== name);
|
|
4902
|
+
if (deleted) {
|
|
4903
|
+
for (const rid of Object.keys(rolePermissionsMap)) {
|
|
4904
|
+
rolePermissionsMap[rid as any] = rolePermissionsMap[rid as any].filter((pid: number) => pid !== deleted.id);
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
flash('Permission deleted');
|
|
4908
|
+
} else {
|
|
4909
|
+
const err = await res.json();
|
|
4910
|
+
flash(err.message || 'Failed to delete permission', 'error');
|
|
4911
|
+
}
|
|
4912
|
+
} catch {
|
|
4913
|
+
flash('Network error', 'error');
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
|
|
4917
|
+
// -- Role <-> Permission --
|
|
4918
|
+
|
|
4919
|
+
async function toggleRolePermission(roleId: number, permissionId: number) {
|
|
4920
|
+
const current = rolePermissionsMap[roleId] ?? [];
|
|
4921
|
+
const has = current.includes(permissionId);
|
|
4922
|
+
|
|
4923
|
+
try {
|
|
4924
|
+
const res = await fetch('/api/admin/role-permissions', {
|
|
4925
|
+
method: has ? 'DELETE' : 'POST',
|
|
4926
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4927
|
+
body: JSON.stringify({ roleId, permissionId }),
|
|
4928
|
+
});
|
|
4929
|
+
if (res.ok) {
|
|
4930
|
+
if (has) {
|
|
4931
|
+
rolePermissionsMap[roleId] = current.filter((id: number) => id !== permissionId);
|
|
4932
|
+
} else {
|
|
4933
|
+
rolePermissionsMap[roleId] = [...current, permissionId];
|
|
4934
|
+
}
|
|
4935
|
+
rolePermissionsMap = { ...rolePermissionsMap };
|
|
4936
|
+
} else {
|
|
4937
|
+
const err = await res.json();
|
|
4938
|
+
flash(err.message || 'Failed to update', 'error');
|
|
4939
|
+
}
|
|
4940
|
+
} catch {
|
|
4941
|
+
flash('Network error', 'error');
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
|
|
4945
|
+
// -- User <-> Role --
|
|
4946
|
+
|
|
4947
|
+
async function assignRoleToUser(userId: number, roleId: number) {
|
|
4948
|
+
try {
|
|
4949
|
+
const res = await fetch('/api/admin/user-roles', {
|
|
4950
|
+
method: 'POST',
|
|
4951
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4952
|
+
body: JSON.stringify({ userId, roleId }),
|
|
4953
|
+
});
|
|
4954
|
+
if (res.ok) {
|
|
4955
|
+
const role = roles.find((r: any) => r.id === roleId);
|
|
4956
|
+
if (role) {
|
|
4957
|
+
const current = userRolesMap[userId] ?? [];
|
|
4958
|
+
if (!current.some((r: any) => r.id === roleId)) {
|
|
4959
|
+
userRolesMap[userId] = [...current, { id: role.id, name: role.name }];
|
|
4960
|
+
userRolesMap = { ...userRolesMap };
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
flash('Role assigned');
|
|
4964
|
+
} else {
|
|
4965
|
+
const err = await res.json();
|
|
4966
|
+
flash(err.message || 'Failed to assign role', 'error');
|
|
4967
|
+
}
|
|
4968
|
+
} catch {
|
|
4969
|
+
flash('Network error', 'error');
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
|
|
4973
|
+
async function removeRoleFromUser(userId: number, roleId: number) {
|
|
4974
|
+
try {
|
|
4975
|
+
const res = await fetch('/api/admin/user-roles', {
|
|
4976
|
+
method: 'DELETE',
|
|
4977
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4978
|
+
body: JSON.stringify({ userId, roleId }),
|
|
4979
|
+
});
|
|
4980
|
+
if (res.ok) {
|
|
4981
|
+
userRolesMap[userId] = (userRolesMap[userId] ?? []).filter((r: any) => r.id !== roleId);
|
|
4982
|
+
userRolesMap = { ...userRolesMap };
|
|
4983
|
+
flash('Role removed');
|
|
4984
|
+
} else {
|
|
4985
|
+
const err = await res.json();
|
|
4986
|
+
flash(err.message || 'Failed to remove role', 'error');
|
|
4987
|
+
}
|
|
4988
|
+
} catch {
|
|
4989
|
+
flash('Network error', 'error');
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
|
|
4993
|
+
// -- User <-> Direct Permission --
|
|
4994
|
+
|
|
4995
|
+
async function grantPermToUser(userId: number, permissionId: number) {
|
|
4996
|
+
try {
|
|
4997
|
+
const res = await fetch('/api/admin/user-permissions', {
|
|
4998
|
+
method: 'POST',
|
|
4999
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5000
|
+
body: JSON.stringify({ userId, permissionId }),
|
|
5001
|
+
});
|
|
5002
|
+
if (res.ok) {
|
|
5003
|
+
const perm = permissions.find((p: any) => p.id === permissionId);
|
|
5004
|
+
if (perm) {
|
|
5005
|
+
const current = userDirectPermsMap[userId] ?? [];
|
|
5006
|
+
if (!current.some((p: any) => p.id === permissionId)) {
|
|
5007
|
+
userDirectPermsMap[userId] = [...current, { id: perm.id, name: perm.name }];
|
|
5008
|
+
userDirectPermsMap = { ...userDirectPermsMap };
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
flash('Permission granted');
|
|
5012
|
+
} else {
|
|
5013
|
+
const err = await res.json();
|
|
5014
|
+
flash(err.message || 'Failed to grant permission', 'error');
|
|
5015
|
+
}
|
|
5016
|
+
} catch {
|
|
5017
|
+
flash('Network error', 'error');
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
|
|
5021
|
+
async function revokePermFromUser(userId: number, permissionId: number) {
|
|
5022
|
+
try {
|
|
5023
|
+
const res = await fetch('/api/admin/user-permissions', {
|
|
5024
|
+
method: 'DELETE',
|
|
5025
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5026
|
+
body: JSON.stringify({ userId, permissionId }),
|
|
5027
|
+
});
|
|
5028
|
+
if (res.ok) {
|
|
5029
|
+
userDirectPermsMap[userId] = (userDirectPermsMap[userId] ?? []).filter((p: any) => p.id !== permissionId);
|
|
5030
|
+
userDirectPermsMap = { ...userDirectPermsMap };
|
|
5031
|
+
flash('Permission revoked');
|
|
5032
|
+
} else {
|
|
5033
|
+
const err = await res.json();
|
|
5034
|
+
flash(err.message || 'Failed to revoke permission', 'error');
|
|
5035
|
+
}
|
|
5036
|
+
} catch {
|
|
5037
|
+
flash('Network error', 'error');
|
|
5049
5038
|
}
|
|
5050
5039
|
}
|
|
5040
|
+
|
|
5041
|
+
function formatDate(date: string | null): string {
|
|
5042
|
+
if (!date) return 'Never';
|
|
5043
|
+
try {
|
|
5044
|
+
return new Date(date).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' });
|
|
5045
|
+
} catch {
|
|
5046
|
+
return date;
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
|
|
5050
|
+
function formatUptime(seconds: number): string {
|
|
5051
|
+
const h = Math.floor(seconds / 3600);
|
|
5052
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
5053
|
+
return h > 0 ? \`\${h}h \${m}m\` : \`\${m}m\`;
|
|
5054
|
+
}
|
|
5055
|
+
|
|
5056
|
+
function getLogBadgeVariant(level: string): 'default' | 'secondary' | 'destructive' {
|
|
5057
|
+
return level === 'error' || level === 'fatal' ? 'destructive' : level === 'warn' ? 'secondary' : 'default';
|
|
5058
|
+
}
|
|
5051
5059
|
</script>
|
|
5052
5060
|
|
|
5053
5061
|
<svelte:head>
|
|
5054
|
-
<title>Admin
|
|
5062
|
+
<title>Admin Dashboard</title>
|
|
5055
5063
|
</svelte:head>
|
|
5056
5064
|
|
|
5057
5065
|
<div class="space-y-8">
|
|
5058
|
-
<div>
|
|
5059
|
-
<
|
|
5060
|
-
|
|
5066
|
+
<div class="flex justify-between items-center">
|
|
5067
|
+
<div>
|
|
5068
|
+
<h1 class="text-3xl font-bold text-gray-900">Admin Dashboard</h1>
|
|
5069
|
+
<p class="text-gray-600 mt-1">System health, queue monitoring, and task management</p>
|
|
5070
|
+
</div>
|
|
5071
|
+
<Button variant="outline" onclick={refreshDashboard}>Refresh</Button>
|
|
5061
5072
|
</div>
|
|
5062
5073
|
|
|
5063
|
-
|
|
5064
|
-
<
|
|
5065
|
-
<
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5074
|
+
{#if message}
|
|
5075
|
+
<Alert variant={messageType === 'error' ? 'destructive' : 'success'}>
|
|
5076
|
+
<span class="text-sm">{message}</span>
|
|
5077
|
+
</Alert>
|
|
5078
|
+
{/if}
|
|
5079
|
+
|
|
5080
|
+
<!-- Overview -->
|
|
5081
|
+
{#if activeTab === 'overview'}
|
|
5082
|
+
<div class="space-y-6">
|
|
5083
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
5084
|
+
<Card>
|
|
5085
|
+
<CardContent class="pt-6">
|
|
5086
|
+
<div>
|
|
5087
|
+
<p class="text-sm text-gray-600">Total Users</p>
|
|
5088
|
+
<p class="text-3xl font-bold text-[var(--color-brand)] mt-2">{data.stats.userCount}</p>
|
|
5089
|
+
</div>
|
|
5090
|
+
</CardContent>
|
|
5091
|
+
</Card>
|
|
5092
|
+
|
|
5093
|
+
<Card>
|
|
5094
|
+
<CardContent class="pt-6">
|
|
5095
|
+
<div>
|
|
5096
|
+
<p class="text-sm text-gray-600">Total Posts</p>
|
|
5097
|
+
<p class="text-3xl font-bold text-[var(--color-brand)] mt-2">{data.stats.postCount}</p>
|
|
5098
|
+
</div>
|
|
5099
|
+
</CardContent>
|
|
5100
|
+
</Card>
|
|
5101
|
+
|
|
5102
|
+
<Card>
|
|
5103
|
+
<CardContent class="pt-6">
|
|
5104
|
+
<div>
|
|
5105
|
+
<p class="text-sm text-gray-600">Queue Pending</p>
|
|
5106
|
+
<p class="text-3xl font-bold text-yellow-600 mt-2">{queueCounts.waiting}</p>
|
|
5107
|
+
</div>
|
|
5108
|
+
</CardContent>
|
|
5109
|
+
</Card>
|
|
5110
|
+
|
|
5111
|
+
<Card>
|
|
5112
|
+
<CardContent class="pt-6">
|
|
5113
|
+
<div>
|
|
5114
|
+
<p class="text-sm text-gray-600">Failed Jobs</p>
|
|
5115
|
+
<p class="text-3xl font-bold text-red-600 mt-2">{queueCounts.failed}</p>
|
|
5116
|
+
</div>
|
|
5117
|
+
</CardContent>
|
|
5118
|
+
</Card>
|
|
5119
|
+
</div>
|
|
5120
|
+
|
|
5121
|
+
<Card>
|
|
5122
|
+
<CardHeader>
|
|
5123
|
+
<CardTitle>System Health</CardTitle>
|
|
5124
|
+
</CardHeader>
|
|
5125
|
+
<CardContent class="space-y-4">
|
|
5126
|
+
<div class="flex justify-between text-sm">
|
|
5127
|
+
<span>Status</span>
|
|
5128
|
+
<Badge variant="default">{health.status}</Badge>
|
|
5129
|
+
</div>
|
|
5130
|
+
<div class="flex justify-between text-sm">
|
|
5131
|
+
<span>Uptime</span>
|
|
5132
|
+
<span class="font-medium">{formatUptime(health.uptime)}</span>
|
|
5133
|
+
</div>
|
|
5134
|
+
<div class="space-y-2">
|
|
5135
|
+
<div class="flex justify-between text-sm">
|
|
5136
|
+
<span>Memory Usage</span>
|
|
5137
|
+
<Badge variant={health.memoryPercent > 90 ? 'destructive' : health.memoryPercent > 70 ? 'secondary' : 'default'}>
|
|
5138
|
+
{health.memoryUsedMB} MB / {health.memoryTotalMB} MB ({health.memoryPercent}%)
|
|
5139
|
+
</Badge>
|
|
5140
|
+
</div>
|
|
5141
|
+
<div class="h-2 bg-gray-200 rounded-full overflow-hidden">
|
|
5142
|
+
<div
|
|
5143
|
+
class="h-full transition-all"
|
|
5144
|
+
class:bg-green-500={health.memoryPercent <= 70}
|
|
5145
|
+
class:bg-yellow-500={health.memoryPercent > 70 && health.memoryPercent <= 90}
|
|
5146
|
+
class:bg-red-500={health.memoryPercent > 90}
|
|
5147
|
+
style="width: {health.memoryPercent}%"
|
|
5148
|
+
></div>
|
|
5149
|
+
</div>
|
|
5150
|
+
</div>
|
|
5151
|
+
<div class="flex justify-between text-sm">
|
|
5152
|
+
<span>Queue Throughput</span>
|
|
5153
|
+
<span class="font-medium">{queueCounts.total} total jobs</span>
|
|
5154
|
+
</div>
|
|
5155
|
+
<div class="flex justify-between text-sm">
|
|
5156
|
+
<span>Log Entries</span>
|
|
5157
|
+
<span class="font-medium">{logStats.totalEntries} entries ({logStats.byLevel?.error ?? 0} errors)</span>
|
|
5158
|
+
</div>
|
|
5159
|
+
</CardContent>
|
|
5160
|
+
</Card>
|
|
5161
|
+
</div>
|
|
5162
|
+
{/if}
|
|
5163
|
+
|
|
5164
|
+
<!-- Users -->
|
|
5165
|
+
{#if activeTab === 'users'}
|
|
5071
5166
|
<Card>
|
|
5072
|
-
<
|
|
5073
|
-
<
|
|
5074
|
-
<
|
|
5075
|
-
</
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5167
|
+
<CardHeader>
|
|
5168
|
+
<CardTitle>User Management</CardTitle>
|
|
5169
|
+
<CardDescription>Manage user roles and permissions ({data.stats.userCount} users)</CardDescription>
|
|
5170
|
+
</CardHeader>
|
|
5171
|
+
<CardContent>
|
|
5172
|
+
<div class="overflow-x-auto">
|
|
5173
|
+
<table class="w-full text-sm">
|
|
5174
|
+
<thead>
|
|
5175
|
+
<tr class="border-b border-gray-200">
|
|
5176
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Name</th>
|
|
5177
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Email</th>
|
|
5178
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Column Role</th>
|
|
5179
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Assigned Roles</th>
|
|
5180
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Direct Permissions</th>
|
|
5181
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Actions</th>
|
|
5182
|
+
</tr>
|
|
5183
|
+
</thead>
|
|
5184
|
+
<tbody>
|
|
5185
|
+
{#each users as user (user.id)}
|
|
5186
|
+
<tr class="border-b border-gray-100 hover:bg-gray-50">
|
|
5187
|
+
<td class="py-3 px-4 font-medium text-gray-900">{user.name}</td>
|
|
5188
|
+
<td class="py-3 px-4 text-gray-600">{user.email}</td>
|
|
5189
|
+
<td class="py-3 px-4">
|
|
5190
|
+
<Badge variant={user.role === 'admin' ? 'default' : 'secondary'}>
|
|
5191
|
+
{user.role}
|
|
5192
|
+
</Badge>
|
|
5193
|
+
</td>
|
|
5194
|
+
<td class="py-3 px-4">
|
|
5195
|
+
<div class="flex flex-wrap gap-1">
|
|
5196
|
+
{#each (userRolesMap[user.id] ?? []) as role (role.id)}
|
|
5197
|
+
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs bg-blue-100 text-blue-800">
|
|
5198
|
+
{role.name}
|
|
5199
|
+
<button
|
|
5200
|
+
type="button"
|
|
5201
|
+
class="hover:text-red-600 font-bold"
|
|
5202
|
+
onclick={() => removeRoleFromUser(user.id, role.id)}
|
|
5203
|
+
>×</button>
|
|
5204
|
+
</span>
|
|
5205
|
+
{/each}
|
|
5206
|
+
{#if roles.length > 0}
|
|
5207
|
+
<select
|
|
5208
|
+
class="text-xs border border-gray-200 rounded px-1 py-0.5"
|
|
5209
|
+
onchange={(e) => {
|
|
5210
|
+
const val = Number((e.target as HTMLSelectElement).value);
|
|
5211
|
+
if (val) { assignRoleToUser(user.id, val); (e.target as HTMLSelectElement).value = ''; }
|
|
5212
|
+
}}
|
|
5213
|
+
>
|
|
5214
|
+
<option value="">+ role</option>
|
|
5215
|
+
{#each roles.filter((r) => !(userRolesMap[user.id] ?? []).some((ur) => ur.id === r.id)) as role (role.id)}
|
|
5216
|
+
<option value={role.id}>{role.name}</option>
|
|
5217
|
+
{/each}
|
|
5218
|
+
</select>
|
|
5219
|
+
{/if}
|
|
5220
|
+
</div>
|
|
5221
|
+
</td>
|
|
5222
|
+
<td class="py-3 px-4">
|
|
5223
|
+
<div class="flex flex-wrap gap-1">
|
|
5224
|
+
{#each (userDirectPermsMap[user.id] ?? []) as perm (perm.id)}
|
|
5225
|
+
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs bg-purple-100 text-purple-800">
|
|
5226
|
+
{perm.name}
|
|
5227
|
+
<button
|
|
5228
|
+
type="button"
|
|
5229
|
+
class="hover:text-red-600 font-bold"
|
|
5230
|
+
onclick={() => revokePermFromUser(user.id, perm.id)}
|
|
5231
|
+
>×</button>
|
|
5232
|
+
</span>
|
|
5233
|
+
{/each}
|
|
5234
|
+
{#if permissions.length > 0}
|
|
5235
|
+
<select
|
|
5236
|
+
class="text-xs border border-gray-200 rounded px-1 py-0.5"
|
|
5237
|
+
onchange={(e) => {
|
|
5238
|
+
const val = Number((e.target as HTMLSelectElement).value);
|
|
5239
|
+
if (val) { grantPermToUser(user.id, val); (e.target as HTMLSelectElement).value = ''; }
|
|
5240
|
+
}}
|
|
5241
|
+
>
|
|
5242
|
+
<option value="">+ perm</option>
|
|
5243
|
+
{#each permissions.filter((p) => !(userDirectPermsMap[user.id] ?? []).some((up) => up.id === p.id)) as perm (perm.id)}
|
|
5244
|
+
<option value={perm.id}>{perm.name}</option>
|
|
5245
|
+
{/each}
|
|
5246
|
+
</select>
|
|
5247
|
+
{/if}
|
|
5248
|
+
</div>
|
|
5249
|
+
</td>
|
|
5250
|
+
<td class="py-3 px-4">
|
|
5251
|
+
<div class="flex gap-2">
|
|
5252
|
+
{#if user.role === 'user'}
|
|
5253
|
+
<Button size="sm" variant="outline" onclick={() => updateUserRole(user.id, 'admin')}>
|
|
5254
|
+
Make Admin
|
|
5255
|
+
</Button>
|
|
5256
|
+
{:else if data.stats.roleDistribution.admin > 1}
|
|
5257
|
+
<Button size="sm" variant="outline" onclick={() => updateUserRole(user.id, 'user')}>
|
|
5258
|
+
Demote
|
|
5259
|
+
</Button>
|
|
5260
|
+
{/if}
|
|
5261
|
+
{#if user.id !== data.user.id}
|
|
5262
|
+
<Button size="sm" variant="destructive" onclick={() => deleteUser(user.id, user.name)}>
|
|
5263
|
+
Delete
|
|
5264
|
+
</Button>
|
|
5265
|
+
{/if}
|
|
5266
|
+
</div>
|
|
5267
|
+
</td>
|
|
5268
|
+
</tr>
|
|
5269
|
+
{/each}
|
|
5270
|
+
</tbody>
|
|
5271
|
+
</table>
|
|
5272
|
+
</div>
|
|
5082
5273
|
</CardContent>
|
|
5083
5274
|
</Card>
|
|
5084
|
-
|
|
5275
|
+
{/if}
|
|
5085
5276
|
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
<
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
<
|
|
5109
|
-
<
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5277
|
+
<!-- Roles -->
|
|
5278
|
+
{#if activeTab === 'roles'}
|
|
5279
|
+
<div class="space-y-6">
|
|
5280
|
+
<Card>
|
|
5281
|
+
<CardHeader>
|
|
5282
|
+
<div class="flex justify-between items-start">
|
|
5283
|
+
<div>
|
|
5284
|
+
<CardTitle>Roles</CardTitle>
|
|
5285
|
+
<CardDescription>{roles.length} roles defined</CardDescription>
|
|
5286
|
+
</div>
|
|
5287
|
+
<Button size="sm" onclick={() => (showRoleForm = !showRoleForm)}>
|
|
5288
|
+
{showRoleForm ? 'Cancel' : 'Create Role'}
|
|
5289
|
+
</Button>
|
|
5290
|
+
</div>
|
|
5291
|
+
</CardHeader>
|
|
5292
|
+
{#if showRoleForm}
|
|
5293
|
+
<CardContent>
|
|
5294
|
+
<form
|
|
5295
|
+
class="flex flex-wrap gap-3 items-end border-b border-gray-100 pb-4 mb-4"
|
|
5296
|
+
onsubmit={(e) => { e.preventDefault(); createRole(); }}
|
|
5297
|
+
>
|
|
5298
|
+
<div class="flex-1 min-w-[200px]">
|
|
5299
|
+
<Label for="role-name">Name</Label>
|
|
5300
|
+
<Input id="role-name" bind:value={newRoleName} placeholder="e.g. editor" />
|
|
5301
|
+
</div>
|
|
5302
|
+
<div class="flex-1 min-w-[200px]">
|
|
5303
|
+
<Label for="role-desc">Description (optional)</Label>
|
|
5304
|
+
<Input id="role-desc" bind:value={newRoleDesc} placeholder="Can edit content" />
|
|
5305
|
+
</div>
|
|
5306
|
+
<Button type="submit" size="sm">Create</Button>
|
|
5307
|
+
</form>
|
|
5308
|
+
</CardContent>
|
|
5309
|
+
{/if}
|
|
5310
|
+
<CardContent>
|
|
5311
|
+
{#if roles.length > 0}
|
|
5312
|
+
<div class="space-y-4">
|
|
5313
|
+
{#each roles as role (role.id)}
|
|
5314
|
+
<div class="border border-gray-200 rounded-lg p-4">
|
|
5315
|
+
<div class="flex items-center justify-between mb-3">
|
|
5316
|
+
<div>
|
|
5317
|
+
<span class="font-medium text-gray-900">{role.name}</span>
|
|
5318
|
+
<Badge variant="secondary" class="ml-2">{role.guard}</Badge>
|
|
5319
|
+
{#if role.description}
|
|
5320
|
+
<p class="text-xs text-gray-500 mt-1">{role.description}</p>
|
|
5321
|
+
{/if}
|
|
5322
|
+
</div>
|
|
5323
|
+
<Button size="sm" variant="destructive" onclick={() => deleteRole(role.name)}>Delete</Button>
|
|
5324
|
+
</div>
|
|
5325
|
+
<div>
|
|
5326
|
+
<p class="text-xs font-semibold text-gray-500 uppercase mb-2">Permissions</p>
|
|
5327
|
+
<div class="flex flex-wrap gap-2">
|
|
5328
|
+
{#each permissions as perm (perm.id)}
|
|
5329
|
+
{@const has = (rolePermissionsMap[role.id] ?? []).includes(perm.id)}
|
|
5330
|
+
<button
|
|
5331
|
+
type="button"
|
|
5332
|
+
class="px-2 py-1 rounded text-xs border transition-colors {has
|
|
5333
|
+
? 'bg-[var(--color-brand)] text-white border-[var(--color-brand)]'
|
|
5334
|
+
: 'bg-white text-gray-600 border-gray-200 hover:border-gray-400'}"
|
|
5335
|
+
onclick={() => toggleRolePermission(role.id, perm.id)}
|
|
5336
|
+
>
|
|
5337
|
+
{perm.name}
|
|
5338
|
+
</button>
|
|
5339
|
+
{/each}
|
|
5340
|
+
{#if permissions.length === 0}
|
|
5341
|
+
<span class="text-xs text-gray-400">No permissions defined yet</span>
|
|
5342
|
+
{/if}
|
|
5343
|
+
</div>
|
|
5344
|
+
</div>
|
|
5345
|
+
</div>
|
|
5346
|
+
{/each}
|
|
5347
|
+
</div>
|
|
5348
|
+
{:else}
|
|
5349
|
+
<p class="text-sm text-gray-500 py-4 text-center">
|
|
5350
|
+
No roles defined. Create one to start assigning permissions.
|
|
5351
|
+
</p>
|
|
5352
|
+
{/if}
|
|
5353
|
+
</CardContent>
|
|
5354
|
+
</Card>
|
|
5355
|
+
</div>
|
|
5356
|
+
{/if}
|
|
5357
|
+
|
|
5358
|
+
<!-- Permissions -->
|
|
5359
|
+
{#if activeTab === 'permissions'}
|
|
5360
|
+
<div class="space-y-6">
|
|
5361
|
+
<Card>
|
|
5362
|
+
<CardHeader>
|
|
5363
|
+
<div class="flex justify-between items-start">
|
|
5364
|
+
<div>
|
|
5365
|
+
<CardTitle>Permissions</CardTitle>
|
|
5366
|
+
<CardDescription>{permissions.length} permissions defined</CardDescription>
|
|
5367
|
+
</div>
|
|
5368
|
+
<Button size="sm" onclick={() => (showPermForm = !showPermForm)}>
|
|
5369
|
+
{showPermForm ? 'Cancel' : 'Create Permission'}
|
|
5370
|
+
</Button>
|
|
5371
|
+
</div>
|
|
5372
|
+
</CardHeader>
|
|
5373
|
+
{#if showPermForm}
|
|
5374
|
+
<CardContent>
|
|
5375
|
+
<form
|
|
5376
|
+
class="flex flex-wrap gap-3 items-end border-b border-gray-100 pb-4 mb-4"
|
|
5377
|
+
onsubmit={(e) => { e.preventDefault(); createPermission(); }}
|
|
5378
|
+
>
|
|
5379
|
+
<div class="flex-1 min-w-[200px]">
|
|
5380
|
+
<Label for="perm-name">Name</Label>
|
|
5381
|
+
<Input id="perm-name" bind:value={newPermName} placeholder="e.g. manage-users" />
|
|
5382
|
+
</div>
|
|
5383
|
+
<div class="flex-1 min-w-[200px]">
|
|
5384
|
+
<Label for="perm-desc">Description (optional)</Label>
|
|
5385
|
+
<Input id="perm-desc" bind:value={newPermDesc} placeholder="Can manage user accounts" />
|
|
5386
|
+
</div>
|
|
5387
|
+
<Button type="submit" size="sm">Create</Button>
|
|
5388
|
+
</form>
|
|
5389
|
+
</CardContent>
|
|
5390
|
+
{/if}
|
|
5391
|
+
<CardContent>
|
|
5392
|
+
{#if permissions.length > 0}
|
|
5393
|
+
<div class="overflow-x-auto">
|
|
5394
|
+
<table class="w-full text-sm">
|
|
5395
|
+
<thead>
|
|
5396
|
+
<tr class="border-b border-gray-200">
|
|
5397
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Name</th>
|
|
5398
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Guard</th>
|
|
5399
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Description</th>
|
|
5400
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Used by Roles</th>
|
|
5401
|
+
<th class="text-left py-3 px-4 font-semibold text-gray-900">Actions</th>
|
|
5402
|
+
</tr>
|
|
5403
|
+
</thead>
|
|
5404
|
+
<tbody>
|
|
5405
|
+
{#each permissions as perm (perm.id)}
|
|
5406
|
+
{@const usedBy = roles.filter((r) => (rolePermissionsMap[r.id] ?? []).includes(perm.id))}
|
|
5407
|
+
<tr class="border-b border-gray-100 hover:bg-gray-50">
|
|
5408
|
+
<td class="py-3 px-4 font-medium text-gray-900">{perm.name}</td>
|
|
5409
|
+
<td class="py-3 px-4">
|
|
5410
|
+
<Badge variant="secondary">{perm.guard}</Badge>
|
|
5411
|
+
</td>
|
|
5412
|
+
<td class="py-3 px-4 text-gray-600">{perm.description || '---'}</td>
|
|
5413
|
+
<td class="py-3 px-4">
|
|
5414
|
+
<div class="flex flex-wrap gap-1">
|
|
5415
|
+
{#each usedBy as role (role.id)}
|
|
5416
|
+
<Badge variant="outline">{role.name}</Badge>
|
|
5417
|
+
{/each}
|
|
5418
|
+
{#if usedBy.length === 0}
|
|
5419
|
+
<span class="text-xs text-gray-400">None</span>
|
|
5420
|
+
{/if}
|
|
5421
|
+
</div>
|
|
5422
|
+
</td>
|
|
5423
|
+
<td class="py-3 px-4">
|
|
5424
|
+
<Button size="sm" variant="destructive" onclick={() => deletePermission(perm.name)}>Delete</Button>
|
|
5425
|
+
</td>
|
|
5426
|
+
</tr>
|
|
5427
|
+
{/each}
|
|
5428
|
+
</tbody>
|
|
5429
|
+
</table>
|
|
5430
|
+
</div>
|
|
5431
|
+
{:else}
|
|
5432
|
+
<p class="text-sm text-gray-500 py-4 text-center">
|
|
5433
|
+
No permissions defined. Create one to start building your authorization system.
|
|
5434
|
+
</p>
|
|
5435
|
+
{/if}
|
|
5436
|
+
</CardContent>
|
|
5437
|
+
</Card>
|
|
5438
|
+
</div>
|
|
5439
|
+
{/if}
|
|
5440
|
+
|
|
5441
|
+
<!-- Queue -->
|
|
5442
|
+
{#if activeTab === 'queue'}
|
|
5443
|
+
<div class="space-y-6">
|
|
5444
|
+
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
|
5445
|
+
<Card>
|
|
5446
|
+
<CardContent class="pt-6">
|
|
5447
|
+
<div>
|
|
5448
|
+
<p class="text-sm text-gray-600">Waiting</p>
|
|
5449
|
+
<p class="text-3xl font-bold text-yellow-600 mt-2">{queueCounts.waiting}</p>
|
|
5450
|
+
</div>
|
|
5451
|
+
</CardContent>
|
|
5452
|
+
</Card>
|
|
5453
|
+
<Card>
|
|
5454
|
+
<CardContent class="pt-6">
|
|
5455
|
+
<div>
|
|
5456
|
+
<p class="text-sm text-gray-600">Active</p>
|
|
5457
|
+
<p class="text-3xl font-bold text-blue-600 mt-2">{queueCounts.active}</p>
|
|
5458
|
+
</div>
|
|
5459
|
+
</CardContent>
|
|
5460
|
+
</Card>
|
|
5461
|
+
<Card>
|
|
5462
|
+
<CardContent class="pt-6">
|
|
5463
|
+
<div>
|
|
5464
|
+
<p class="text-sm text-gray-600">Failed</p>
|
|
5465
|
+
<p class="text-3xl font-bold text-red-600 mt-2">{queueCounts.failed}</p>
|
|
5466
|
+
</div>
|
|
5467
|
+
</CardContent>
|
|
5468
|
+
</Card>
|
|
5469
|
+
<Card>
|
|
5470
|
+
<CardContent class="pt-6">
|
|
5471
|
+
<div>
|
|
5472
|
+
<p class="text-sm text-gray-600">Completed</p>
|
|
5473
|
+
<p class="text-3xl font-bold text-green-600 mt-2">{queueCounts.completed}</p>
|
|
5474
|
+
</div>
|
|
5475
|
+
</CardContent>
|
|
5476
|
+
</Card>
|
|
5477
|
+
<Card>
|
|
5478
|
+
<CardContent class="pt-6">
|
|
5479
|
+
<div>
|
|
5480
|
+
<p class="text-sm text-gray-600">Delayed</p>
|
|
5481
|
+
<p class="text-3xl font-bold text-gray-600 mt-2">{queueCounts.delayed}</p>
|
|
5482
|
+
</div>
|
|
5483
|
+
</CardContent>
|
|
5484
|
+
</Card>
|
|
5128
5485
|
</div>
|
|
5129
|
-
|
|
5130
|
-
|
|
5486
|
+
<Card>
|
|
5487
|
+
<CardHeader>
|
|
5488
|
+
<CardTitle>Queue Actions</CardTitle>
|
|
5489
|
+
<CardDescription>Manage job queue</CardDescription>
|
|
5490
|
+
</CardHeader>
|
|
5491
|
+
<CardContent class="flex gap-3">
|
|
5492
|
+
<Button variant="outline" onclick={refreshQueue}>Refresh Counts</Button>
|
|
5493
|
+
</CardContent>
|
|
5494
|
+
</Card>
|
|
5495
|
+
</div>
|
|
5496
|
+
{/if}
|
|
5131
5497
|
|
|
5132
|
-
|
|
5498
|
+
<!-- Scheduler -->
|
|
5499
|
+
{#if activeTab === 'scheduler'}
|
|
5133
5500
|
<Card>
|
|
5134
5501
|
<CardHeader>
|
|
5135
|
-
<CardTitle>
|
|
5502
|
+
<CardTitle>Scheduled Tasks</CardTitle>
|
|
5503
|
+
<CardDescription>
|
|
5504
|
+
{scheduledTasks.length > 0
|
|
5505
|
+
? \`\${scheduledTasks.length} registered tasks\`
|
|
5506
|
+
: 'No tasks registered. Configure your scheduler to see tasks here.'}
|
|
5507
|
+
</CardDescription>
|
|
5136
5508
|
</CardHeader>
|
|
5137
5509
|
<CardContent>
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
<
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5510
|
+
{#if scheduledTasks.length > 0}
|
|
5511
|
+
<div class="space-y-3">
|
|
5512
|
+
{#each scheduledTasks as task (task.name)}
|
|
5513
|
+
<div class="flex items-center justify-between p-4 border border-gray-200 rounded-lg hover:bg-gray-50">
|
|
5514
|
+
<div class="flex-1">
|
|
5515
|
+
<p class="font-medium text-gray-900">{task.name}</p>
|
|
5516
|
+
<p class="text-sm text-gray-600">Schedule: {task.humanReadable}</p>
|
|
5517
|
+
<p class="text-xs text-gray-500 mt-1">Last run: {formatDate(task.lastRun)}</p>
|
|
5518
|
+
<p class="text-xs text-gray-500">Next run: {formatDate(task.nextRun)}</p>
|
|
5519
|
+
</div>
|
|
5520
|
+
<div class="flex items-center gap-3">
|
|
5521
|
+
{#if task.lastStatus}
|
|
5522
|
+
<Badge variant={task.lastStatus === 'success' ? 'default' : 'destructive'}>
|
|
5523
|
+
{task.lastStatus}
|
|
5524
|
+
</Badge>
|
|
5525
|
+
{/if}
|
|
5526
|
+
<Badge variant={task.enabled ? 'default' : 'secondary'}>
|
|
5527
|
+
{task.enabled ? 'enabled' : 'disabled'}
|
|
5528
|
+
</Badge>
|
|
5529
|
+
<Button size="sm" variant="outline" onclick={() => runTask(task.name)}>Run Now</Button>
|
|
5530
|
+
<Button size="sm" variant="outline" onclick={() => toggleTask(task.name, !task.enabled)}>
|
|
5531
|
+
{task.enabled ? 'Disable' : 'Enable'}
|
|
5532
|
+
</Button>
|
|
5533
|
+
</div>
|
|
5534
|
+
</div>
|
|
5535
|
+
{/each}
|
|
5536
|
+
</div>
|
|
5537
|
+
{:else}
|
|
5538
|
+
<p class="text-sm text-gray-500 py-4 text-center">
|
|
5539
|
+
No scheduled tasks found. Configure the Scheduler in your app.ts to register tasks.
|
|
5540
|
+
</p>
|
|
5541
|
+
{/if}
|
|
5148
5542
|
</CardContent>
|
|
5149
5543
|
</Card>
|
|
5150
5544
|
{/if}
|
|
5151
5545
|
|
|
5152
|
-
|
|
5546
|
+
<!-- Logs -->
|
|
5547
|
+
{#if activeTab === 'logs'}
|
|
5153
5548
|
<Card>
|
|
5154
5549
|
<CardHeader>
|
|
5155
|
-
<CardTitle>
|
|
5550
|
+
<CardTitle>Application Logs</CardTitle>
|
|
5551
|
+
<CardDescription>
|
|
5552
|
+
{logStats.totalEntries} total entries
|
|
5553
|
+
{#if logStats.byLevel?.error}
|
|
5554
|
+
({logStats.byLevel.error} errors)
|
|
5555
|
+
{/if}
|
|
5556
|
+
</CardDescription>
|
|
5156
5557
|
</CardHeader>
|
|
5157
5558
|
<CardContent>
|
|
5158
|
-
<div class="flex
|
|
5159
|
-
{
|
|
5160
|
-
|
|
5161
|
-
|
|
5559
|
+
<div class="flex gap-2 mb-4">
|
|
5560
|
+
<Button size="sm" variant={logFilter === 'all' ? 'default' : 'outline'} onclick={() => (logFilter = 'all')}>
|
|
5561
|
+
All ({logStats.totalEntries})
|
|
5562
|
+
</Button>
|
|
5563
|
+
<Button size="sm" variant={logFilter === 'info' ? 'default' : 'outline'} onclick={() => (logFilter = 'info')}>
|
|
5564
|
+
Info ({logStats.byLevel?.info ?? 0})
|
|
5565
|
+
</Button>
|
|
5566
|
+
<Button size="sm" variant={logFilter === 'warn' ? 'default' : 'outline'} onclick={() => (logFilter = 'warn')}>
|
|
5567
|
+
Warning ({logStats.byLevel?.warn ?? 0})
|
|
5568
|
+
</Button>
|
|
5569
|
+
<Button size="sm" variant={logFilter === 'error' ? 'default' : 'outline'} onclick={() => (logFilter = 'error')}>
|
|
5570
|
+
Error ({logStats.byLevel?.error ?? 0})
|
|
5571
|
+
</Button>
|
|
5162
5572
|
</div>
|
|
5573
|
+
|
|
5574
|
+
{#if filteredLogs.length > 0}
|
|
5575
|
+
<div class="space-y-2 max-h-96 overflow-y-auto">
|
|
5576
|
+
{#each filteredLogs as log, i (i)}
|
|
5577
|
+
<div class="flex items-start gap-3 p-3 border border-gray-200 rounded bg-gray-50 text-sm">
|
|
5578
|
+
<Badge variant={getLogBadgeVariant(log.level)} class="mt-0.5">
|
|
5579
|
+
{log.level.toUpperCase()}
|
|
5580
|
+
</Badge>
|
|
5581
|
+
<div class="flex-1">
|
|
5582
|
+
<p class="text-gray-900">{log.message}</p>
|
|
5583
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
5584
|
+
{formatDate(log.timestamp)}
|
|
5585
|
+
{#if log.channel && log.channel !== 'default'}
|
|
5586
|
+
<span class="ml-2 text-gray-400">[{log.channel}]</span>
|
|
5587
|
+
{/if}
|
|
5588
|
+
</p>
|
|
5589
|
+
</div>
|
|
5590
|
+
</div>
|
|
5591
|
+
{/each}
|
|
5592
|
+
</div>
|
|
5593
|
+
{:else}
|
|
5594
|
+
<p class="text-sm text-gray-500 py-4 text-center">
|
|
5595
|
+
No log entries found. Logs appear here as your application runs.
|
|
5596
|
+
</p>
|
|
5597
|
+
{/if}
|
|
5163
5598
|
</CardContent>
|
|
5164
5599
|
</Card>
|
|
5165
5600
|
{/if}
|
|
5166
|
-
|
|
5167
|
-
<Card>
|
|
5168
|
-
<CardContent class="pt-6">
|
|
5169
|
-
<p class="text-sm text-gray-600">
|
|
5170
|
-
Need full monitoring with queue, scheduler, and log views?
|
|
5171
|
-
Run <code class="bg-gray-100 px-1.5 py-0.5 rounded text-sm">npx svelar make:dashboard</code> to generate the complete admin dashboard.
|
|
5172
|
-
</p>
|
|
5173
|
-
</CardContent>
|
|
5174
|
-
</Card>
|
|
5175
5601
|
</div>
|
|
5176
5602
|
`}static apiHealth(){return`import type { RequestHandler } from '@sveltejs/kit';
|
|
5177
5603
|
import { json } from '@sveltejs/kit';
|
|
@@ -5368,6 +5794,168 @@ export const DELETE = ctrl.handle('revokeUserPermission');
|
|
|
5368
5794
|
|
|
5369
5795
|
const ctrl = new AdminController();
|
|
5370
5796
|
export const POST = ctrl.handle('exportData');
|
|
5797
|
+
`}static apiAdminHealth(){return`import { json } from '@sveltejs/kit';
|
|
5798
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5799
|
+
|
|
5800
|
+
export const GET: RequestHandler = async () => {
|
|
5801
|
+
const health = {
|
|
5802
|
+
status: 'ok',
|
|
5803
|
+
timestamp: new Date().toISOString(),
|
|
5804
|
+
uptime: process.uptime(),
|
|
5805
|
+
memory: process.memoryUsage(),
|
|
5806
|
+
version: '0.1.0',
|
|
5807
|
+
};
|
|
5808
|
+
|
|
5809
|
+
return json(health);
|
|
5810
|
+
};
|
|
5811
|
+
`}static apiAdminQueue(){return`import { json } from '@sveltejs/kit';
|
|
5812
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5813
|
+
import { JobMonitor } from '@beeblock/svelar/queue/JobMonitor';
|
|
5814
|
+
|
|
5815
|
+
export const GET: RequestHandler = async (event) => {
|
|
5816
|
+
const { searchParams } = event.url;
|
|
5817
|
+
const status = searchParams.get('status') || 'all';
|
|
5818
|
+
const queueName = searchParams.get('queue') || 'default';
|
|
5819
|
+
const limit = parseInt(searchParams.get('limit') || '50');
|
|
5820
|
+
const offset = parseInt(searchParams.get('offset') || '0');
|
|
5821
|
+
|
|
5822
|
+
try {
|
|
5823
|
+
const jobs = await JobMonitor.listJobs({
|
|
5824
|
+
queue: queueName,
|
|
5825
|
+
status: status === 'all' ? undefined : status as any,
|
|
5826
|
+
limit,
|
|
5827
|
+
offset,
|
|
5828
|
+
});
|
|
5829
|
+
const counts = await JobMonitor.getCounts(queueName);
|
|
5830
|
+
return json({ jobs, counts, queueName });
|
|
5831
|
+
} catch (error: any) {
|
|
5832
|
+
return json({ error: error.message || 'Failed to fetch queue jobs' }, { status: 500 });
|
|
5833
|
+
}
|
|
5834
|
+
};
|
|
5835
|
+
`}static apiAdminQueueRetry(){return`import { json } from '@sveltejs/kit';
|
|
5836
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5837
|
+
import { JobMonitor } from '@beeblock/svelar/queue/JobMonitor';
|
|
5838
|
+
|
|
5839
|
+
export const POST: RequestHandler = async (event) => {
|
|
5840
|
+
const { id } = event.params;
|
|
5841
|
+
try {
|
|
5842
|
+
await JobMonitor.retryJob(id);
|
|
5843
|
+
return json({ success: true, message: 'Job queued for retry' });
|
|
5844
|
+
} catch (error: any) {
|
|
5845
|
+
return json({ error: error.message || 'Failed to retry job' }, { status: 500 });
|
|
5846
|
+
}
|
|
5847
|
+
};
|
|
5848
|
+
`}static apiAdminQueueDelete(){return`import { json } from '@sveltejs/kit';
|
|
5849
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5850
|
+
import { JobMonitor } from '@beeblock/svelar/queue/JobMonitor';
|
|
5851
|
+
|
|
5852
|
+
export const DELETE: RequestHandler = async (event) => {
|
|
5853
|
+
const { id } = event.params;
|
|
5854
|
+
try {
|
|
5855
|
+
await JobMonitor.deleteJob(id);
|
|
5856
|
+
return json({ success: true, message: 'Job removed' });
|
|
5857
|
+
} catch (error: any) {
|
|
5858
|
+
return json({ error: error.message || 'Failed to remove job' }, { status: 500 });
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
`}static apiAdminScheduler(){return`import { json } from '@sveltejs/kit';
|
|
5862
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5863
|
+
import { ScheduleMonitor } from '@beeblock/svelar/scheduler/ScheduleMonitor';
|
|
5864
|
+
|
|
5865
|
+
export const GET: RequestHandler = async () => {
|
|
5866
|
+
try {
|
|
5867
|
+
const tasks = await ScheduleMonitor.listTasks();
|
|
5868
|
+
const health = await ScheduleMonitor.getHealth();
|
|
5869
|
+
return json({ tasks, health });
|
|
5870
|
+
} catch (error: any) {
|
|
5871
|
+
return json({ error: error.message || 'Failed to fetch scheduled tasks' }, { status: 500 });
|
|
5872
|
+
}
|
|
5873
|
+
};
|
|
5874
|
+
`}static apiAdminSchedulerRun(){return`import { json } from '@sveltejs/kit';
|
|
5875
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5876
|
+
import { ScheduleMonitor } from '@beeblock/svelar/scheduler/ScheduleMonitor';
|
|
5877
|
+
|
|
5878
|
+
export const POST: RequestHandler = async (event) => {
|
|
5879
|
+
const { name } = event.params;
|
|
5880
|
+
try {
|
|
5881
|
+
await ScheduleMonitor.runTask(name);
|
|
5882
|
+
return json({ success: true, message: \`Task '\${name}' triggered\` });
|
|
5883
|
+
} catch (error: any) {
|
|
5884
|
+
return json({ error: error.message || 'Failed to run task' }, { status: 500 });
|
|
5885
|
+
}
|
|
5886
|
+
};
|
|
5887
|
+
`}static apiAdminSchedulerToggle(){return`import { json } from '@sveltejs/kit';
|
|
5888
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5889
|
+
import { ScheduleMonitor } from '@beeblock/svelar/scheduler/ScheduleMonitor';
|
|
5890
|
+
|
|
5891
|
+
export const POST: RequestHandler = async (event) => {
|
|
5892
|
+
const { name } = event.params;
|
|
5893
|
+
const body = await event.request.json();
|
|
5894
|
+
const enabled = body.enabled ?? true;
|
|
5895
|
+
|
|
5896
|
+
try {
|
|
5897
|
+
if (enabled) {
|
|
5898
|
+
ScheduleMonitor.enableTask(name);
|
|
5899
|
+
} else {
|
|
5900
|
+
ScheduleMonitor.disableTask(name);
|
|
5901
|
+
}
|
|
5902
|
+
return json({ success: true, message: \`Task '\${name}' \${enabled ? 'enabled' : 'disabled'}\` });
|
|
5903
|
+
} catch (error: any) {
|
|
5904
|
+
return json({ error: error.message || 'Failed to toggle task' }, { status: 500 });
|
|
5905
|
+
}
|
|
5906
|
+
};
|
|
5907
|
+
`}static apiAdminLogs(){return`import { json } from '@sveltejs/kit';
|
|
5908
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5909
|
+
import { LogViewer } from '@beeblock/svelar/logging/LogViewer';
|
|
5910
|
+
|
|
5911
|
+
export const GET: RequestHandler = async (event) => {
|
|
5912
|
+
const { searchParams } = event.url;
|
|
5913
|
+
const level = searchParams.get('level');
|
|
5914
|
+
const channel = searchParams.get('channel');
|
|
5915
|
+
const search = searchParams.get('search');
|
|
5916
|
+
const limit = parseInt(searchParams.get('limit') || '100');
|
|
5917
|
+
const offset = parseInt(searchParams.get('offset') || '0');
|
|
5918
|
+
|
|
5919
|
+
try {
|
|
5920
|
+
const logs = LogViewer.query({
|
|
5921
|
+
level: level as any,
|
|
5922
|
+
channel: channel ?? undefined,
|
|
5923
|
+
search: search ?? undefined,
|
|
5924
|
+
limit,
|
|
5925
|
+
offset,
|
|
5926
|
+
});
|
|
5927
|
+
const stats = LogViewer.getStats();
|
|
5928
|
+
return json({ logs, total: stats.totalEntries, limit, offset });
|
|
5929
|
+
} catch (error: any) {
|
|
5930
|
+
return json({ error: error.message || 'Failed to fetch logs' }, { status: 500 });
|
|
5931
|
+
}
|
|
5932
|
+
};
|
|
5933
|
+
`}static apiAdminStats(){return`import { json } from '@sveltejs/kit';
|
|
5934
|
+
import type { RequestHandler } from '@sveltejs/kit';
|
|
5935
|
+
import { JobMonitor } from '@beeblock/svelar/queue/JobMonitor';
|
|
5936
|
+
import { ScheduleMonitor } from '@beeblock/svelar/scheduler/ScheduleMonitor';
|
|
5937
|
+
import { LogViewer } from '@beeblock/svelar/logging/LogViewer';
|
|
5938
|
+
|
|
5939
|
+
export const GET: RequestHandler = async () => {
|
|
5940
|
+
try {
|
|
5941
|
+
const [queueHealth, recentErrors] = await Promise.all([
|
|
5942
|
+
JobMonitor.getHealth(),
|
|
5943
|
+
Promise.resolve(LogViewer.getRecentErrors(10)),
|
|
5944
|
+
]);
|
|
5945
|
+
const schedulerHealth = await ScheduleMonitor.getHealth();
|
|
5946
|
+
const logStats = LogViewer.getStats();
|
|
5947
|
+
|
|
5948
|
+
return json({
|
|
5949
|
+
queue: queueHealth,
|
|
5950
|
+
scheduler: schedulerHealth,
|
|
5951
|
+
logs: logStats,
|
|
5952
|
+
recentErrors,
|
|
5953
|
+
timestamp: new Date().toISOString(),
|
|
5954
|
+
});
|
|
5955
|
+
} catch (error: any) {
|
|
5956
|
+
return json({ error: error.message || 'Failed to fetch stats' }, { status: 500 });
|
|
5957
|
+
}
|
|
5958
|
+
};
|
|
5371
5959
|
`}static sendWelcomeEmail(){return`import { Job } from '@beeblock/svelar/queue';
|
|
5372
5960
|
import { EmailTemplates } from '@beeblock/svelar/email-templates';
|
|
5373
5961
|
import { Mailer } from '@beeblock/svelar/mail';
|
|
@@ -5845,12 +6433,12 @@ export class EventServiceProvider extends BaseProvider {
|
|
|
5845
6433
|
</Card>
|
|
5846
6434
|
</div>
|
|
5847
6435
|
</div>
|
|
5848
|
-
`}};var
|
|
5849
|
-
`);let d=(M,Ar)=>{let K=s(u,M);i(s(K,".."),{recursive:!0}),a(K,Ar)};this.info("Creating project structure...");let p=["","src","src/lib","src/lib/models","src/lib/repositories","src/lib/services","src/lib/controllers","src/lib/dtos","src/lib/actions","src/lib/auth","src/lib/schemas","src/lib/shared/jobs","src/lib/shared/scheduler","src/lib/events","src/lib/listeners","src/lib/resources","src/lib/notifications","src/lib/shared/middleware","src/lib/shared/components","src/lib/shared/stores","src/lib/shared/plugins","src/lib/shared/channels","src/lib/shared/commands","src/lib/shared/providers","src/lib/database/migrations","src/lib/database/seeders","src/routes","src/routes/api","static","storage/logs","storage/cache","storage/uploads","storage/sessions"];for(let M of p)i(s(u,M),{recursive:!0});this.info("Writing config files...");let{dirname:f}=await import("path"),{fileURLToPath:y}=await import("url"),S=s(f(f(y(import.meta.url))),"package.json"),ls=JSON.parse((await import("fs")).readFileSync(S,"utf-8")).version??"0.4.0";d("package.json",m.packageJson(c,ls)),d("svelte.config.js",m.svelteConfig()),d("vite.config.ts",m.viteConfig()),d("tsconfig.json",m.tsConfig()),d("src/app.html",m.appHtml()),d("src/app.css",m.appCss()),d("src/app.ts",m.appTs()),d("src/hooks.server.ts",m.hooksServerTs()),d(".env.example",m.envExample());let{randomBytes:ve}=await import("crypto"),Be=ve(32).toString("hex"),be=ve(16).toString("hex"),we=m.envExample().replace("APP_KEY=change-me-to-a-random-string",`APP_KEY=${Be}`).replace("INTERNAL_SECRET=change-me-to-a-random-string",`INTERNAL_SECRET=${be}`);d(".env",we),d(".gitignore",m.gitignore()),d("svelar.database.json",m.svelarDatabaseJson());for(let M of["storage/logs","storage/cache","storage/uploads","storage/sessions"])d(`${M}/.gitkeep`,"");if(this.info("Scaffolding domain layer..."),d("src/lib/models/User.ts",m.userModel()),d("src/lib/models/Post.ts",m.postModel()),d("src/lib/repositories/UserRepository.ts",m.userRepository()),d("src/lib/repositories/PostRepository.ts",m.postRepository()),d("src/lib/services/AuthService.ts",m.authService()),d("src/lib/services/PostService.ts",m.postService()),d("src/lib/controllers/AuthController.ts",m.authController()),d("src/lib/controllers/PostController.ts",m.postController()),d("src/lib/controllers/AdminController.ts",m.adminController()),d("src/lib/dtos/RegisterRequest.ts",m.registerRequest()),d("src/lib/dtos/LoginRequest.ts",m.loginRequest()),d("src/lib/dtos/CreatePostRequest.ts",m.createPostRequest()),d("src/lib/dtos/UpdatePostRequest.ts",m.updatePostRequest()),d("src/lib/dtos/UpdateUserRoleRequest.ts",m.updateUserRoleRequest()),d("src/lib/dtos/DeleteUserRequest.ts",m.deleteUserRequest()),d("src/lib/dtos/CreateRoleRequest.ts",m.createRoleRequest()),d("src/lib/dtos/DeleteRoleRequest.ts",m.deleteRoleRequest()),d("src/lib/dtos/CreatePermissionRequest.ts",m.createPermissionRequest()),d("src/lib/dtos/DeletePermissionRequest.ts",m.deletePermissionRequest()),d("src/lib/dtos/RolePermissionRequest.ts",m.rolePermissionRequest()),d("src/lib/dtos/UserRoleRequest.ts",m.userRoleRequest()),d("src/lib/dtos/UserPermissionRequest.ts",m.userPermissionRequest()),d("src/lib/dtos/ExportDataRequest.ts",m.exportDataRequest()),d("src/lib/actions/RegisterUserAction.ts",m.registerUserAction()),d("src/lib/actions/CreatePostAction.ts",m.createPostAction()),d("src/lib/auth/gates.ts",m.gates()),d("src/lib/schemas/auth.ts",m.authSchema()),d("src/lib/schemas/post.ts",m.postSchema()),d("src/lib/schemas/admin.ts",m.adminSchema()),d("src/lib/services/AdminService.ts",m.adminService()),d("src/lib/resources/RoleResource.ts",m.roleResource()),d("src/lib/resources/PermissionResource.ts",m.permissionResource()),d("src/lib/shared/providers/EventServiceProvider.ts",m.eventServiceProvider()),d("src/lib/resources/UserResource.ts",m.userResource()),d("src/lib/resources/PostResource.ts",m.postResource()),d("src/lib/events/UserRegistered.ts",m.userRegisteredEvent()),d("src/lib/listeners/SendWelcomeEmailListener.ts",m.sendWelcomeEmailListener()),d("src/lib/notifications/WelcomeNotification.ts",m.welcomeNotification()),this.info("Creating migrations..."),d("src/lib/database/migrations/00000001_create_users_table.ts",m.createUsersTable()),d("src/lib/database/migrations/00000002_create_posts_table.ts",m.createPostsTable()),d("src/lib/database/migrations/00000003_create_permissions_tables.ts",m.createPermissionsTables()),d("src/lib/database/migrations/00000004_add_role_to_users.ts",m.addRoleToUsers()),d("src/lib/database/migrations/00000005_create_sessions_table.ts",m.createSessionsTable()),d("src/lib/database/migrations/00000006_create_audit_logs_table.ts",m.createAuditLogsTable()),d("src/lib/database/migrations/00000007_create_notifications_table.ts",m.createNotificationsTable()),d("src/lib/database/migrations/00000008_create_failed_jobs_table.ts",m.createFailedJobsTable()),d("src/lib/database/seeders/DatabaseSeeder.ts",m.databaseSeeder()),this.info("Creating auth pages..."),d("src/routes/login/+page.server.ts",m.loginPageServer()),d("src/routes/login/+page.svelte",m.loginPageSvelte()),d("src/routes/register/+page.server.ts",m.registerPageServer()),d("src/routes/register/+page.svelte",m.registerPageSvelte()),d("src/routes/logout/+page.server.ts",m.logoutPageServer()),d("src/routes/forgot-password/+page.server.ts",m.forgotPasswordPageServer()),d("src/routes/forgot-password/+page.svelte",m.forgotPasswordPageSvelte()),d("src/routes/reset-password/+page.server.ts",m.resetPasswordPageServer()),d("src/routes/reset-password/+page.svelte",m.resetPasswordPageSvelte()),d("src/routes/otp-login/+page.server.ts",m.otpLoginPageServer()),d("src/routes/otp-login/+page.svelte",m.otpLoginPageSvelte()),d("src/routes/verify-email/+page.server.ts",m.verifyEmailPageServer()),d("src/routes/verify-email/+page.svelte",m.verifyEmailPageSvelte()),this.info("Creating dashboard..."),d("src/routes/dashboard/+layout.server.ts",m.dashboardLayoutServer()),d("src/routes/dashboard/+page.server.ts",m.dashboardPageServer()),d("src/routes/dashboard/+page.svelte",m.dashboardPageSvelte()),d("src/routes/dashboard/api-keys/+page.server.ts",m.apiKeysPageServer()),d("src/routes/dashboard/api-keys/+page.svelte",m.apiKeysPageSvelte()),d("src/routes/dashboard/team/+page.server.ts",m.teamPageServer()),d("src/routes/dashboard/team/+page.svelte",m.teamPageSvelte()),this.info("Creating admin panel..."),d("src/routes/admin/+layout.server.ts",m.adminLayoutServer()),d("src/routes/admin/+page.server.ts",m.adminPageServer()),d("src/routes/admin/+page.svelte",m.adminPageSvelte()),this.info("Creating API routes..."),d("src/routes/api/health/+server.ts",m.apiHealth()),d("src/routes/api/auth/register/+server.ts",m.apiAuthRegister()),d("src/routes/api/auth/login/+server.ts",m.apiAuthLogin()),d("src/routes/api/auth/logout/+server.ts",m.apiAuthLogout()),d("src/routes/api/auth/me/+server.ts",m.apiAuthMe()),d("src/routes/api/auth/forgot-password/+server.ts",m.apiAuthForgotPassword()),d("src/routes/api/auth/reset-password/+server.ts",m.apiAuthResetPassword()),d("src/routes/api/auth/otp/send/+server.ts",m.apiAuthOtpSend()),d("src/routes/api/auth/otp/verify/+server.ts",m.apiAuthOtpVerify()),d("src/routes/api/auth/verify-email/+server.ts",m.apiAuthVerifyEmail()),d("src/routes/api/posts/+server.ts",m.apiPosts()),d("src/routes/api/posts/[id]/+server.ts",m.apiPostsSingle()),d("src/routes/api/posts/mine/+server.ts",m.apiPostsMine()),d("src/routes/api/broadcasting/[channel]/+server.ts",m.apiBroadcasting()),d("src/routes/api/internal/broadcast/+server.ts",m.apiInternalBroadcast()),d("src/routes/api/admin/users/+server.ts",m.apiAdminUsers()),d("src/routes/api/admin/roles/+server.ts",m.apiAdminRoles()),d("src/routes/api/admin/permissions/+server.ts",m.apiAdminPermissions()),d("src/routes/api/admin/role-permissions/+server.ts",m.apiAdminRolePermissions()),d("src/routes/api/admin/user-roles/+server.ts",m.apiAdminUserRoles()),d("src/routes/api/admin/user-permissions/+server.ts",m.apiAdminUserPermissions()),d("src/routes/api/admin/export/+server.ts",m.apiAdminExport()),this.info("Creating background jobs..."),d("src/lib/shared/jobs/SendWelcomeEmail.ts",m.sendWelcomeEmail()),d("src/lib/shared/jobs/DailyDigestJob.ts",m.dailyDigestJob()),d("src/lib/shared/jobs/ExportDataJob.ts",m.exportDataJob()),this.info("Creating scheduled tasks..."),d("src/lib/shared/scheduler/CleanupExpiredTokens.ts",m.cleanupExpiredTokens()),d("src/lib/shared/scheduler/CleanExpiredSessions.ts",m.cleanExpiredSessions()),d("src/lib/shared/scheduler/DailyDigestEmail.ts",m.dailyDigestEmail()),d("src/lib/shared/scheduler/PruneAuditLogs.ts",m.pruneAuditLogs()),d("src/lib/shared/scheduler/QueueHealthCheck.ts",m.queueHealthCheck()),this.info("Creating layouts..."),d("src/routes/+layout.svelte",m.rootLayoutSvelte(c)),d("src/routes/+layout.server.ts",m.rootLayoutServer()),d("src/routes/+error.svelte",m.errorSvelte()),d("src/routes/+page.svelte",m.homePage(c)),this.success("Project structure created"),!t["no-install"]){this.info("Installing dependencies...");try{o("npm install",{cwd:u,stdio:"inherit"}),this.success("Dependencies installed")}catch{this.warn("npm install failed \u2014 run it manually with: cd "+c+" && npm install")}this.info("Running migrations...");try{o("npx svelar migrate",{cwd:u,stdio:"inherit"}),this.success("Migrations complete")}catch{this.warn("Migrations failed \u2014 run manually: cd "+c+" && npx svelar migrate")}this.info("Seeding database...");try{o("npx svelar seed:run",{cwd:u,stdio:"inherit"}),this.success("Database seeded")}catch{this.warn("Seeding failed \u2014 run manually: cd "+c+" && npx svelar seed:run")}}this.log(""),this.log(` \x1B[32m+\x1B[0m Project \x1B[1m${c}\x1B[0m created successfully!
|
|
6436
|
+
`}};var Zt=class extends h{name="new";description="Create a new SvelteKit project with Svelar pre-configured";arguments=["name"];flags=[{name:"no-install",alias:"n",description:"Skip npm install",type:"boolean",default:!1}];async handle(e,t){let{join:s,resolve:r}=await import("path"),{existsSync:i,mkdirSync:n,writeFileSync:a}=await import("fs"),{execSync:o}=await import("child_process"),c=e[0];c||(this.error("Please provide a project name: npx svelar new my-app"),process.exit(1));let u=r(process.cwd(),c);i(u)&&(this.error(`Directory "${c}" already exists.`),process.exit(1)),this.log(""),this.log(` \x1B[1m\x1B[38;5;208m</> Svelar\x1B[0m \u2014 Creating new project
|
|
6437
|
+
`);let d=(N,kr)=>{let z=s(u,N);n(s(z,".."),{recursive:!0}),a(z,kr)};this.info("Creating project structure...");let p=["","src","src/lib","src/lib/models","src/lib/repositories","src/lib/services","src/lib/controllers","src/lib/dtos","src/lib/actions","src/lib/auth","src/lib/schemas","src/lib/shared/jobs","src/lib/shared/scheduler","src/lib/events","src/lib/listeners","src/lib/resources","src/lib/notifications","src/lib/shared/middleware","src/lib/shared/components","src/lib/shared/stores","src/lib/shared/plugins","src/lib/shared/channels","src/lib/shared/commands","src/lib/shared/providers","src/lib/database/migrations","src/lib/database/seeders","src/routes","src/routes/api","static","storage/logs","storage/cache","storage/uploads","storage/sessions"];for(let N of p)n(s(u,N),{recursive:!0});this.info("Writing config files...");let{dirname:f}=await import("path"),{fileURLToPath:b}=await import("url"),C=s(f(f(f(b(import.meta.url)))),"package.json"),as=JSON.parse((await import("fs")).readFileSync(C,"utf-8")).version??"0.4.0";d("package.json",m.packageJson(c,as)),d("svelte.config.js",m.svelteConfig()),d("vite.config.ts",m.viteConfig()),d("tsconfig.json",m.tsConfig()),d("src/app.html",m.appHtml()),d("src/app.css",m.appCss()),d("src/app.ts",m.appTs()),d("src/hooks.server.ts",m.hooksServerTs()),d(".env.example",m.envExample());let{randomBytes:ye}=await import("crypto"),Be=ye(32).toString("hex"),ve=ye(16).toString("hex"),be=m.envExample().replace("APP_KEY=change-me-to-a-random-string",`APP_KEY=${Be}`).replace("INTERNAL_SECRET=change-me-to-a-random-string",`INTERNAL_SECRET=${ve}`);d(".env",be),d(".gitignore",m.gitignore()),d("svelar.database.json",m.svelarDatabaseJson());for(let N of["storage/logs","storage/cache","storage/uploads","storage/sessions"])d(`${N}/.gitkeep`,"");if(this.info("Scaffolding domain layer..."),d("src/lib/models/User.ts",m.userModel()),d("src/lib/models/Post.ts",m.postModel()),d("src/lib/repositories/UserRepository.ts",m.userRepository()),d("src/lib/repositories/PostRepository.ts",m.postRepository()),d("src/lib/services/AuthService.ts",m.authService()),d("src/lib/services/PostService.ts",m.postService()),d("src/lib/controllers/AuthController.ts",m.authController()),d("src/lib/controllers/PostController.ts",m.postController()),d("src/lib/controllers/AdminController.ts",m.adminController()),d("src/lib/dtos/RegisterRequest.ts",m.registerRequest()),d("src/lib/dtos/LoginRequest.ts",m.loginRequest()),d("src/lib/dtos/CreatePostRequest.ts",m.createPostRequest()),d("src/lib/dtos/UpdatePostRequest.ts",m.updatePostRequest()),d("src/lib/dtos/UpdateUserRoleRequest.ts",m.updateUserRoleRequest()),d("src/lib/dtos/DeleteUserRequest.ts",m.deleteUserRequest()),d("src/lib/dtos/CreateRoleRequest.ts",m.createRoleRequest()),d("src/lib/dtos/DeleteRoleRequest.ts",m.deleteRoleRequest()),d("src/lib/dtos/CreatePermissionRequest.ts",m.createPermissionRequest()),d("src/lib/dtos/DeletePermissionRequest.ts",m.deletePermissionRequest()),d("src/lib/dtos/RolePermissionRequest.ts",m.rolePermissionRequest()),d("src/lib/dtos/UserRoleRequest.ts",m.userRoleRequest()),d("src/lib/dtos/UserPermissionRequest.ts",m.userPermissionRequest()),d("src/lib/dtos/ExportDataRequest.ts",m.exportDataRequest()),d("src/lib/actions/RegisterUserAction.ts",m.registerUserAction()),d("src/lib/actions/CreatePostAction.ts",m.createPostAction()),d("src/lib/auth/gates.ts",m.gates()),d("src/lib/schemas/auth.ts",m.authSchema()),d("src/lib/schemas/post.ts",m.postSchema()),d("src/lib/schemas/admin.ts",m.adminSchema()),d("src/lib/services/AdminService.ts",m.adminService()),d("src/lib/resources/RoleResource.ts",m.roleResource()),d("src/lib/resources/PermissionResource.ts",m.permissionResource()),d("src/lib/shared/providers/EventServiceProvider.ts",m.eventServiceProvider()),d("src/lib/resources/UserResource.ts",m.userResource()),d("src/lib/resources/PostResource.ts",m.postResource()),d("src/lib/events/UserRegistered.ts",m.userRegisteredEvent()),d("src/lib/listeners/SendWelcomeEmailListener.ts",m.sendWelcomeEmailListener()),d("src/lib/notifications/WelcomeNotification.ts",m.welcomeNotification()),this.info("Creating migrations..."),d("src/lib/database/migrations/00000001_create_users_table.ts",m.createUsersTable()),d("src/lib/database/migrations/00000002_create_posts_table.ts",m.createPostsTable()),d("src/lib/database/migrations/00000003_create_permissions_tables.ts",m.createPermissionsTables()),d("src/lib/database/migrations/00000004_add_role_to_users.ts",m.addRoleToUsers()),d("src/lib/database/migrations/00000005_create_sessions_table.ts",m.createSessionsTable()),d("src/lib/database/migrations/00000006_create_audit_logs_table.ts",m.createAuditLogsTable()),d("src/lib/database/migrations/00000007_create_notifications_table.ts",m.createNotificationsTable()),d("src/lib/database/migrations/00000008_create_failed_jobs_table.ts",m.createFailedJobsTable()),d("src/lib/database/seeders/DatabaseSeeder.ts",m.databaseSeeder()),this.info("Creating auth pages..."),d("src/routes/login/+page.server.ts",m.loginPageServer()),d("src/routes/login/+page.svelte",m.loginPageSvelte()),d("src/routes/register/+page.server.ts",m.registerPageServer()),d("src/routes/register/+page.svelte",m.registerPageSvelte()),d("src/routes/logout/+page.server.ts",m.logoutPageServer()),d("src/routes/forgot-password/+page.server.ts",m.forgotPasswordPageServer()),d("src/routes/forgot-password/+page.svelte",m.forgotPasswordPageSvelte()),d("src/routes/reset-password/+page.server.ts",m.resetPasswordPageServer()),d("src/routes/reset-password/+page.svelte",m.resetPasswordPageSvelte()),d("src/routes/otp-login/+page.server.ts",m.otpLoginPageServer()),d("src/routes/otp-login/+page.svelte",m.otpLoginPageSvelte()),d("src/routes/verify-email/+page.server.ts",m.verifyEmailPageServer()),d("src/routes/verify-email/+page.svelte",m.verifyEmailPageSvelte()),this.info("Creating dashboard..."),d("src/routes/dashboard/+layout.server.ts",m.dashboardLayoutServer()),d("src/routes/dashboard/+layout.svelte",m.dashboardLayoutSvelte()),d("src/routes/dashboard/+page.server.ts",m.dashboardPageServer()),d("src/routes/dashboard/+page.svelte",m.dashboardPageSvelte()),d("src/routes/dashboard/api-keys/+page.server.ts",m.apiKeysPageServer()),d("src/routes/dashboard/api-keys/+page.svelte",m.apiKeysPageSvelte()),d("src/routes/dashboard/team/+page.server.ts",m.teamPageServer()),d("src/routes/dashboard/team/+page.svelte",m.teamPageSvelte()),this.info("Creating admin panel..."),d("src/routes/admin/+layout.server.ts",m.adminLayoutServer()),d("src/routes/admin/+layout.svelte",m.adminLayoutSvelte()),d("src/routes/admin/+page.server.ts",m.adminPageServer()),d("src/routes/admin/+page.svelte",m.adminPageSvelte()),this.info("Creating API routes..."),d("src/routes/api/health/+server.ts",m.apiHealth()),d("src/routes/api/auth/register/+server.ts",m.apiAuthRegister()),d("src/routes/api/auth/login/+server.ts",m.apiAuthLogin()),d("src/routes/api/auth/logout/+server.ts",m.apiAuthLogout()),d("src/routes/api/auth/me/+server.ts",m.apiAuthMe()),d("src/routes/api/auth/forgot-password/+server.ts",m.apiAuthForgotPassword()),d("src/routes/api/auth/reset-password/+server.ts",m.apiAuthResetPassword()),d("src/routes/api/auth/otp/send/+server.ts",m.apiAuthOtpSend()),d("src/routes/api/auth/otp/verify/+server.ts",m.apiAuthOtpVerify()),d("src/routes/api/auth/verify-email/+server.ts",m.apiAuthVerifyEmail()),d("src/routes/api/posts/+server.ts",m.apiPosts()),d("src/routes/api/posts/[id]/+server.ts",m.apiPostsSingle()),d("src/routes/api/posts/mine/+server.ts",m.apiPostsMine()),d("src/routes/api/broadcasting/[channel]/+server.ts",m.apiBroadcasting()),d("src/routes/api/internal/broadcast/+server.ts",m.apiInternalBroadcast()),d("src/routes/api/admin/users/+server.ts",m.apiAdminUsers()),d("src/routes/api/admin/roles/+server.ts",m.apiAdminRoles()),d("src/routes/api/admin/permissions/+server.ts",m.apiAdminPermissions()),d("src/routes/api/admin/role-permissions/+server.ts",m.apiAdminRolePermissions()),d("src/routes/api/admin/user-roles/+server.ts",m.apiAdminUserRoles()),d("src/routes/api/admin/user-permissions/+server.ts",m.apiAdminUserPermissions()),d("src/routes/api/admin/export/+server.ts",m.apiAdminExport()),d("src/routes/api/admin/health/+server.ts",m.apiAdminHealth()),d("src/routes/api/admin/queue/+server.ts",m.apiAdminQueue()),d("src/routes/api/admin/queue/[id]/retry/+server.ts",m.apiAdminQueueRetry()),d("src/routes/api/admin/queue/[id]/+server.ts",m.apiAdminQueueDelete()),d("src/routes/api/admin/scheduler/+server.ts",m.apiAdminScheduler()),d("src/routes/api/admin/scheduler/[name]/run/+server.ts",m.apiAdminSchedulerRun()),d("src/routes/api/admin/scheduler/[name]/toggle/+server.ts",m.apiAdminSchedulerToggle()),d("src/routes/api/admin/logs/+server.ts",m.apiAdminLogs()),d("src/routes/api/admin/stats/+server.ts",m.apiAdminStats()),this.info("Creating background jobs..."),d("src/lib/shared/jobs/SendWelcomeEmail.ts",m.sendWelcomeEmail()),d("src/lib/shared/jobs/DailyDigestJob.ts",m.dailyDigestJob()),d("src/lib/shared/jobs/ExportDataJob.ts",m.exportDataJob()),this.info("Creating scheduled tasks..."),d("src/lib/shared/scheduler/CleanupExpiredTokens.ts",m.cleanupExpiredTokens()),d("src/lib/shared/scheduler/CleanExpiredSessions.ts",m.cleanExpiredSessions()),d("src/lib/shared/scheduler/DailyDigestEmail.ts",m.dailyDigestEmail()),d("src/lib/shared/scheduler/PruneAuditLogs.ts",m.pruneAuditLogs()),d("src/lib/shared/scheduler/QueueHealthCheck.ts",m.queueHealthCheck()),this.info("Creating layouts..."),d("src/routes/+layout.svelte",m.rootLayoutSvelte(c)),d("src/routes/+layout.server.ts",m.rootLayoutServer()),d("src/routes/+error.svelte",m.errorSvelte()),d("src/routes/+page.svelte",m.homePage(c)),this.success("Project structure created"),!t["no-install"]){this.info("Installing dependencies...");try{o("npm install",{cwd:u,stdio:"inherit"}),this.success("Dependencies installed")}catch{this.warn("npm install failed \u2014 run it manually with: cd "+c+" && npm install")}this.info("Running migrations...");try{o("npx svelar migrate",{cwd:u,stdio:"inherit"}),this.success("Migrations complete")}catch{this.warn("Migrations failed \u2014 run manually: cd "+c+" && npx svelar migrate")}this.info("Seeding database...");try{o("npx svelar seed:run",{cwd:u,stdio:"inherit"}),this.success("Database seeded")}catch{this.warn("Seeding failed \u2014 run manually: cd "+c+" && npx svelar seed:run")}}this.log(""),this.log(` \x1B[32m+\x1B[0m Project \x1B[1m${c}\x1B[0m created successfully!
|
|
5850
6438
|
`),this.log(` Next steps:
|
|
5851
|
-
`),this.log(` cd ${c}`),t["no-install"]&&(this.log(" npm install"),this.log(" npx svelar migrate"),this.log(" npx svelar seed:run")),this.log(" npm run dev"),this.log(""),this.log(" Default accounts:"),this.log(" Admin: admin@svelar.dev / admin123"),this.log(" Demo: demo@svelar.dev / password"),this.log("")}};var
|
|
6439
|
+
`),this.log(` cd ${c}`),t["no-install"]&&(this.log(" npm install"),this.log(" npx svelar migrate"),this.log(" npx svelar seed:run")),this.log(" npm run dev"),this.log(""),this.log(" Default accounts:"),this.log(" Admin: admin@svelar.dev / admin123"),this.log(" Demo: demo@svelar.dev / password"),this.log("")}};var es=class extends h{name="key:generate";description="Generate a new APP_KEY and set it in .env";flags=[{name:"show",alias:"s",description:"Only display the key, do not write to .env",type:"boolean",default:!1},{name:"force",alias:"f",description:"Overwrite existing APP_KEY",type:"boolean",default:!1}];async handle(e,t){let{randomBytes:s}=await import("crypto"),{join:r}=await import("path"),{existsSync:i,readFileSync:n,writeFileSync:a}=await import("fs"),o=s(32).toString("hex");if(t.show){this.log(`
|
|
5852
6440
|
APP_KEY=${o}
|
|
5853
|
-
`);return}let c=r(process.cwd(),".env");if(!
|
|
5854
|
-
`),this.success("Application key set (created .env).");return}let u=
|
|
5855
|
-
${u}`);this.success("Application key set.")}};var
|
|
5856
|
-
`)){let e=l.trim();if(!e||e.startsWith("#"))continue;let t=e.indexOf("=");if(t===-1)continue;let s=e.slice(0,t).trim(),r=e.slice(t+1).trim();(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),s in process.env||(process.env[s]=r)}var
|
|
6441
|
+
`);return}let c=r(process.cwd(),".env");if(!i(c)){let p=r(process.cwd(),".env.example");if(i(p)){let f=n(p,"utf-8");f=f.replace(/^APP_KEY=.*$/m,`APP_KEY=${o}`),a(c,f),this.success("Application key set (created .env from .env.example).")}else a(c,`APP_KEY=${o}
|
|
6442
|
+
`),this.success("Application key set (created .env).");return}let u=n(c,"utf-8"),d=u.match(/^APP_KEY=(.*)$/m);if(d&&d[1]&&d[1]!=="change-me-to-a-random-string"&&!t.force){this.warn("APP_KEY already set. Use --force to overwrite.");return}if(d){let p=u.replace(/^APP_KEY=.*$/m,`APP_KEY=${o}`);a(c,p)}else a(c,`APP_KEY=${o}
|
|
6443
|
+
${u}`);this.success("Application key set.")}};var rs=class extends h{name="plugin:list";description="List all discovered and enabled plugins";arguments=[];flags=[];async handle(e,t){try{let{PluginRegistry:s}=await Promise.resolve().then(()=>(ss(),Cr)),r=s;await r.discover();let i=r.list();if(i.length===0){this.info("No plugins discovered.");return}let n=["Name","Version","Description","Status","Config","Migrations"],a=i.map(o=>[o.name,o.version,o.description||"-",o.enabled?"\u2713 Enabled":" Disabled",o.hasConfig?"\u2713":"-",o.hasMigrations?"\u2713":"-"]);this.newLine(),this.table(n,a),this.newLine(),this.info(`Total: ${i.length} plugin(s)`)}catch(s){this.error(`Failed to list plugins: ${s?.message??String(s)}`)}}};var is=class extends h{name="plugin:publish";description="Publish a plugin's config and migration files";arguments=["name"];flags=[{name:"force",alias:"f",description:"Overwrite existing files",type:"boolean"},{name:"only",alias:"o",description:"Publish only config|migrations|assets",type:"string"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a plugin name.");return}try{let{PluginRegistry:r}=await Promise.resolve().then(()=>(ss(),Cr)),{PluginPublisher:i}=await Promise.resolve().then(()=>(Rr(),ln)),n=r,a=i;await n.discover();let o=n.get(s);if(!o){this.error(`Plugin "${s}" not found.`);return}let c=await this.loadPluginClass(o.packageName);if(!c){this.error(`Failed to load plugin class for "${s}".`);return}let u=new c,d={force:t.force||!1,only:t.only};this.info(`Publishing plugin: ${s}`);let p=await a.publish(u,d);this.newLine(),p.configs.length>0&&(this.success(`${p.configs.length} config file(s) published:`),p.configs.forEach(f=>this.log(` - ${f}`))),p.migrations.length>0&&(this.success(`${p.migrations.length} migration file(s) published:`),p.migrations.forEach(f=>this.log(` - ${f}`))),p.assets.length>0&&(this.success(`${p.assets.length} asset file(s) published:`),p.assets.forEach(f=>this.log(` - ${f}`))),p.configs.length===0&&p.migrations.length===0&&p.assets.length===0&&this.warn("No publishable files found for this plugin."),this.newLine()}catch(r){this.error(`Failed to publish plugin: ${r?.message??String(r)}`)}}async loadPluginClass(e){try{let t=await import(e);return t.default||Object.values(t)[0]}catch{return null}}};var ns=class extends h{name="plugin:install";description="Install a plugin from npm";arguments=["package"];flags=[{name:"no-publish",alias:"n",description:"Skip auto-publishing plugin assets",type:"boolean"}];async handle(e,t){let s=e[0];if(!s){this.error("Please provide a package name.");return}try{let{PluginInstaller:r}=await Promise.resolve().then(()=>(dn(),cn)),i=r;this.info(`Installing plugin package: ${s}`),this.newLine();let n=await i.install(s,{publish:!t["no-publish"]});this.newLine(),n.success?(this.success(`Plugin installed: ${n.pluginName} (v${n.version})`),n.published&&(n.published.configs.length>0&&this.info(`${n.published.configs.length} config file(s) published`),n.published.migrations.length>0&&this.info(`${n.published.migrations.length} migration file(s) published`),n.published.assets.length>0&&this.info(`${n.published.assets.length} asset file(s) published`)),this.newLine(),this.log("You can now use the plugin in your application by registering it in your app bootstrap code.")):this.error(`Failed to install plugin: ${n.error}`),this.newLine()}catch(r){this.error(`Installation error: ${r?.message??String(r)}`)}}};var un=Er(process.cwd(),".env");if(go(un))for(let l of hn(un,"utf-8").split(`
|
|
6444
|
+
`)){let e=l.trim();if(!e||e.startsWith("#"))continue;let t=e.indexOf("=");if(t===-1)continue;let s=e.slice(0,t).trim(),r=e.slice(t+1).trim();(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),s in process.env||(process.env[s]=r)}var fo=mn(pn(import.meta.url));ho(po(Er(fo,"ts-resolve-hook.mjs")).href,import.meta.url);var yo=mn(pn(import.meta.url)),vo=Er(yo,"..","..","package.json"),bo=JSON.parse(hn(vo,"utf-8")),v=new He(bo.version);v.register(Zt);v.register(es);v.register(ze);v.register(Ke);v.register(We);v.register(Je);v.register(Ve);v.register(Qe);v.register(Ge);v.register(Ye);v.register(Xe);v.register(Ze);v.register(lt);v.register(ct);v.register(et);v.register(tt);v.register(st);v.register(rt);v.register(it);v.register(nt);v.register(at);v.register(ot);v.register(dt);v.register(ut);v.register(mt);v.register(pt);v.register(ht);v.register(gt);v.register(ft);v.register(bt);v.register(Ct);v.register(Pt);v.register(St);v.register(Rt);v.register(Xt);v.register(rs);v.register(is);v.register(ns);async function wo(){let{join:l}=await import("path"),{existsSync:e,readdirSync:t}=await import("fs"),{pathToFileURL:s}=await import("url"),r=l(process.cwd(),"src","lib","shared","commands");if(!e(r))return;let i=t(r).filter(n=>(n.endsWith(".ts")||n.endsWith(".js"))&&!n.startsWith("."));for(let n of i)try{let a=l(r,n),c=await import(s(a).href),u=c.default??Object.values(c).find(d=>typeof d=="function"&&d.prototype&&"handle"in d.prototype);u&&typeof u=="function"&&v.add(new u)}catch{}}wo().then(()=>v.run());
|