@beeblock/svelar 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/actions/index.d.ts +101 -0
- package/dist/actions/index.js +1 -0
- package/dist/api-keys/index.d.ts +58 -0
- package/dist/api-keys/index.js +1 -0
- package/dist/audit/index.d.ts +52 -0
- package/dist/audit/index.js +1 -0
- package/dist/auth/Auth.d.ts +283 -0
- package/dist/auth/Gate.d.ts +166 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +80 -0
- package/dist/broadcasting/client.d.ts +195 -0
- package/dist/broadcasting/client.js +1 -0
- package/dist/broadcasting/index.d.ts +318 -0
- package/dist/broadcasting/index.js +20 -0
- package/dist/cache/index.d.ts +77 -0
- package/dist/cache/index.js +1 -0
- package/dist/cli/Cli.d.ts +23 -0
- package/dist/cli/Command.d.ts +36 -0
- package/dist/cli/bin.d.ts +8 -0
- package/dist/cli/bin.js +5856 -0
- package/dist/cli/commands/KeyGenerateCommand.d.ts +16 -0
- package/dist/cli/commands/MakeActionCommand.d.ts +15 -0
- package/dist/cli/commands/MakeBroadcastingCommand.d.ts +29 -0
- package/dist/cli/commands/MakeChannelCommand.d.ts +18 -0
- package/dist/cli/commands/MakeCommandCommand.d.ts +16 -0
- package/dist/cli/commands/MakeConfigCommand.d.ts +13 -0
- package/dist/cli/commands/MakeControllerCommand.d.ts +28 -0
- package/dist/cli/commands/MakeDashboardCommand.d.ts +34 -0
- package/dist/cli/commands/MakeDockerCommand.d.ts +32 -0
- package/dist/cli/commands/MakeEventCommand.d.ts +11 -0
- package/dist/cli/commands/MakeJobCommand.d.ts +11 -0
- package/dist/cli/commands/MakeListenerCommand.d.ts +16 -0
- package/dist/cli/commands/MakeMiddlewareCommand.d.ts +11 -0
- package/dist/cli/commands/MakeMigrationCommand.d.ts +17 -0
- package/dist/cli/commands/MakeModelCommand.d.ts +25 -0
- package/dist/cli/commands/MakeObserverCommand.d.ts +23 -0
- package/dist/cli/commands/MakePluginCommand.d.ts +11 -0
- package/dist/cli/commands/MakeProviderCommand.d.ts +11 -0
- package/dist/cli/commands/MakeRepositoryCommand.d.ts +22 -0
- package/dist/cli/commands/MakeRequestCommand.d.ts +15 -0
- package/dist/cli/commands/MakeResourceCommand.d.ts +30 -0
- package/dist/cli/commands/MakeRouteCommand.d.ts +42 -0
- package/dist/cli/commands/MakeSchemaCommand.d.ts +20 -0
- package/dist/cli/commands/MakeSeederCommand.d.ts +11 -0
- package/dist/cli/commands/MakeServiceCommand.d.ts +28 -0
- package/dist/cli/commands/MakeTaskCommand.d.ts +12 -0
- package/dist/cli/commands/MigrateCommand.d.ts +26 -0
- package/dist/cli/commands/NewCommand.d.ts +21 -0
- package/dist/cli/commands/NewCommandTemplates.d.ts +123 -0
- package/dist/cli/commands/PluginInstallCommand.d.ts +16 -0
- package/dist/cli/commands/PluginListCommand.d.ts +11 -0
- package/dist/cli/commands/PluginPublishCommand.d.ts +22 -0
- package/dist/cli/commands/QueueFailedCommand.d.ts +9 -0
- package/dist/cli/commands/QueueFlushCommand.d.ts +9 -0
- package/dist/cli/commands/QueueRetryCommand.d.ts +16 -0
- package/dist/cli/commands/QueueWorkCommand.d.ts +25 -0
- package/dist/cli/commands/RoutesListCommand.d.ts +30 -0
- package/dist/cli/commands/ScheduleRunCommand.d.ts +15 -0
- package/dist/cli/commands/SeedCommand.d.ts +14 -0
- package/dist/cli/commands/TinkerCommand.d.ts +10 -0
- package/dist/cli/index.d.ts +36 -0
- package/dist/cli/index.js +1973 -0
- package/dist/cli/ts-resolve-hook.mjs +74 -0
- package/dist/cli/ts-resolver.mjs +8 -0
- package/dist/config/Config.d.ts +65 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/container/Application.d.ts +33 -0
- package/dist/container/Container.d.ts +70 -0
- package/dist/container/ServiceProvider.d.ts +21 -0
- package/dist/container/index.d.ts +3 -0
- package/dist/container/index.js +1 -0
- package/dist/dashboard/index.d.ts +123 -0
- package/dist/dashboard/index.js +5 -0
- package/dist/database/Connection.d.ts +80 -0
- package/dist/database/Migration.d.ts +76 -0
- package/dist/database/SchemaBuilder.d.ts +91 -0
- package/dist/database/Seeder.d.ts +9 -0
- package/dist/database/index.d.ts +4 -0
- package/dist/database/index.js +4 -0
- package/dist/email-templates/index.d.ts +51 -0
- package/dist/email-templates/index.js +57 -0
- package/dist/errors/Handler.d.ts +100 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +5 -0
- package/dist/events/EventServiceProvider.d.ts +82 -0
- package/dist/events/Listener.d.ts +28 -0
- package/dist/events/index.d.ts +80 -0
- package/dist/events/index.js +1 -0
- package/dist/excel/index.d.ts +154 -0
- package/dist/excel/index.js +1 -0
- package/dist/feature-flags/index.d.ts +158 -0
- package/dist/feature-flags/index.js +59 -0
- package/dist/forms/index.d.ts +81 -0
- package/dist/forms/index.js +1 -0
- package/dist/hashing/Hash.d.ts +51 -0
- package/dist/hashing/index.d.ts +1 -0
- package/dist/hashing/index.js +1 -0
- package/dist/hooks/index.d.ts +135 -0
- package/dist/hooks/index.js +5 -0
- package/dist/http/index.d.ts +201 -0
- package/dist/http/index.js +2 -0
- package/dist/i18n/index.d.ts +81 -0
- package/dist/i18n/index.js +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +127 -0
- package/dist/logging/LogViewer.d.ts +95 -0
- package/dist/logging/LogViewer.js +1 -0
- package/dist/logging/index.d.ts +83 -0
- package/dist/logging/index.js +3 -0
- package/dist/mail/index.d.ts +149 -0
- package/dist/mail/index.js +1 -0
- package/dist/middleware/Middleware.d.ts +208 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/notifications/index.d.ts +85 -0
- package/dist/notifications/index.js +2 -0
- package/dist/orm/Model.d.ts +123 -0
- package/dist/orm/Observer.d.ts +34 -0
- package/dist/orm/QueryBuilder.d.ts +119 -0
- package/dist/orm/Relationship.d.ts +58 -0
- package/dist/orm/index.d.ts +4 -0
- package/dist/orm/index.js +1 -0
- package/dist/pagination/index.d.ts +8 -0
- package/dist/pagination/index.js +0 -0
- package/dist/pdf/GeneratePdfJob.d.ts +99 -0
- package/dist/pdf/GeneratePdfJob.js +41 -0
- package/dist/pdf/index.d.ts +328 -0
- package/dist/pdf/index.js +41 -0
- package/dist/permissions/index.d.ts +161 -0
- package/dist/permissions/index.js +60 -0
- package/dist/plugins/BootstrapPlugins.d.ts +11 -0
- package/dist/plugins/PluginInstaller.d.ts +30 -0
- package/dist/plugins/PluginInstaller.js +1 -0
- package/dist/plugins/PluginPublisher.d.ts +32 -0
- package/dist/plugins/PluginPublisher.js +1 -0
- package/dist/plugins/PluginRegistry.d.ts +55 -0
- package/dist/plugins/PluginRegistry.js +1 -0
- package/dist/plugins/index.d.ts +206 -0
- package/dist/plugins/index.js +1 -0
- package/dist/queue/JobMonitor.d.ts +109 -0
- package/dist/queue/JobMonitor.js +5 -0
- package/dist/queue/index.d.ts +279 -0
- package/dist/queue/index.js +5 -0
- package/dist/repositories/index.d.ts +147 -0
- package/dist/repositories/index.js +1 -0
- package/dist/routing/Controller.d.ts +115 -0
- package/dist/routing/FormRequest.d.ts +94 -0
- package/dist/routing/Resource.d.ts +213 -0
- package/dist/routing/Response.d.ts +138 -0
- package/dist/routing/index.d.ts +4 -0
- package/dist/routing/index.js +5 -0
- package/dist/scheduler/ScheduleMonitor.d.ts +141 -0
- package/dist/scheduler/ScheduleMonitor.js +1 -0
- package/dist/scheduler/SchedulerLock.d.ts +33 -0
- package/dist/scheduler/index.d.ts +208 -0
- package/dist/scheduler/index.js +34 -0
- package/dist/services/index.d.ts +79 -0
- package/dist/services/index.js +1 -0
- package/dist/session/Session.d.ts +166 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +16 -0
- package/dist/storage/index.d.ts +154 -0
- package/dist/storage/index.js +1 -0
- package/dist/support/Pipeline.d.ts +65 -0
- package/dist/support/date.d.ts +136 -0
- package/dist/support/date.js +1 -0
- package/dist/support/index.d.ts +8 -0
- package/dist/support/index.js +1 -0
- package/dist/support/singleton.d.ts +10 -0
- package/dist/support/uuid.d.ts +40 -0
- package/dist/teams/index.d.ts +91 -0
- package/dist/teams/index.js +78 -0
- package/dist/uploads/index.d.ts +63 -0
- package/dist/uploads/index.js +2 -0
- package/dist/validation/index.d.ts +46 -0
- package/dist/validation/index.js +1 -0
- package/dist/webhooks/index.d.ts +66 -0
- package/dist/webhooks/index.js +1 -0
- package/package.json +338 -0
- package/src/i18n/LanguageSwitcher.svelte +47 -0
- package/src/i18n/index.ts +113 -0
- package/src/ui/Alert.svelte +22 -0
- package/src/ui/Avatar.svelte +18 -0
- package/src/ui/AvatarFallback.svelte +18 -0
- package/src/ui/AvatarImage.svelte +12 -0
- package/src/ui/Badge.svelte +27 -0
- package/src/ui/Button.svelte +51 -0
- package/src/ui/Card.svelte +15 -0
- package/src/ui/CardContent.svelte +15 -0
- package/src/ui/CardDescription.svelte +15 -0
- package/src/ui/CardFooter.svelte +15 -0
- package/src/ui/CardHeader.svelte +15 -0
- package/src/ui/CardTitle.svelte +15 -0
- package/src/ui/Icon.svelte +81 -0
- package/src/ui/Input.svelte +40 -0
- package/src/ui/Label.svelte +20 -0
- package/src/ui/Separator.svelte +10 -0
- package/src/ui/Tabs.svelte +23 -0
- package/src/ui/TabsContent.svelte +27 -0
- package/src/ui/TabsList.svelte +19 -0
- package/src/ui/TabsTrigger.svelte +28 -0
- package/src/ui/Toaster.svelte +279 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/toast.ts +212 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Session Manager
|
|
3
|
+
*
|
|
4
|
+
* Server-side session management with pluggable stores.
|
|
5
|
+
* Supports cookie-based sessions with in-memory, file, or database backing stores.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* // In hooks.server.ts
|
|
10
|
+
* import { SessionMiddleware, MemorySessionStore } from 'svelar/session';
|
|
11
|
+
*
|
|
12
|
+
* export const handle = createSvelarHooks({
|
|
13
|
+
* middleware: [
|
|
14
|
+
* new SessionMiddleware({ store: new MemorySessionStore() }),
|
|
15
|
+
* ],
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // In a controller or +server.ts
|
|
19
|
+
* event.locals.session.get('user_id');
|
|
20
|
+
* event.locals.session.set('flash_message', 'Login successful');
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export interface SessionData {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
export interface SessionStore {
|
|
27
|
+
/** Read session data by ID */
|
|
28
|
+
read(id: string): Promise<SessionData | null>;
|
|
29
|
+
/** Write session data */
|
|
30
|
+
write(id: string, data: SessionData, ttl: number): Promise<void>;
|
|
31
|
+
/** Destroy a session */
|
|
32
|
+
destroy(id: string): Promise<void>;
|
|
33
|
+
/** Garbage collect expired sessions */
|
|
34
|
+
gc?(maxLifetime: number): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export interface SessionConfig {
|
|
37
|
+
/** Session cookie name */
|
|
38
|
+
cookieName?: string;
|
|
39
|
+
/** Session lifetime in seconds (default: 7200 = 2 hours) */
|
|
40
|
+
lifetime?: number;
|
|
41
|
+
/** Secret for signing session IDs */
|
|
42
|
+
secret?: string;
|
|
43
|
+
/** Cookie path */
|
|
44
|
+
path?: string;
|
|
45
|
+
/** Cookie domain */
|
|
46
|
+
domain?: string;
|
|
47
|
+
/** Secure cookie (HTTPS only) */
|
|
48
|
+
secure?: boolean;
|
|
49
|
+
/** HttpOnly cookie */
|
|
50
|
+
httpOnly?: boolean;
|
|
51
|
+
/** SameSite policy */
|
|
52
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
53
|
+
/** Session store implementation */
|
|
54
|
+
store: SessionStore;
|
|
55
|
+
}
|
|
56
|
+
export declare class Session {
|
|
57
|
+
readonly id: string;
|
|
58
|
+
private data;
|
|
59
|
+
private dirty;
|
|
60
|
+
private flashData;
|
|
61
|
+
private previousFlashData;
|
|
62
|
+
constructor(id: string, initialData?: SessionData);
|
|
63
|
+
/**
|
|
64
|
+
* Get a session value
|
|
65
|
+
*/
|
|
66
|
+
get<T = any>(key: string, defaultValue?: T): T;
|
|
67
|
+
/**
|
|
68
|
+
* Set a session value
|
|
69
|
+
*/
|
|
70
|
+
set(key: string, value: any): void;
|
|
71
|
+
/**
|
|
72
|
+
* Check if a key exists
|
|
73
|
+
*/
|
|
74
|
+
has(key: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Remove a session value
|
|
77
|
+
*/
|
|
78
|
+
forget(key: string): void;
|
|
79
|
+
/**
|
|
80
|
+
* Clear all session data
|
|
81
|
+
*/
|
|
82
|
+
flush(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Flash data — available only on the next request
|
|
85
|
+
*/
|
|
86
|
+
flash(key: string, value: any): void;
|
|
87
|
+
/**
|
|
88
|
+
* Get all session data
|
|
89
|
+
*/
|
|
90
|
+
all(): SessionData;
|
|
91
|
+
/**
|
|
92
|
+
* Check if session has been modified
|
|
93
|
+
*/
|
|
94
|
+
isDirty(): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Get data to persist (including flash for next request)
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
toPersist(): SessionData;
|
|
100
|
+
/**
|
|
101
|
+
* Regenerate session ID (prevents session fixation)
|
|
102
|
+
* @internal Returns new ID
|
|
103
|
+
*/
|
|
104
|
+
regenerateId(): string;
|
|
105
|
+
static generateId(): string;
|
|
106
|
+
}
|
|
107
|
+
export declare class MemorySessionStore implements SessionStore {
|
|
108
|
+
private sessions;
|
|
109
|
+
private oldSessionIds;
|
|
110
|
+
read(id: string): Promise<SessionData | null>;
|
|
111
|
+
write(id: string, data: SessionData, ttl: number): Promise<void>;
|
|
112
|
+
destroy(id: string): Promise<void>;
|
|
113
|
+
gc(maxLifetime: number): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Mark a session ID as old/invalidated (used when a session is regenerated)
|
|
116
|
+
* @internal
|
|
117
|
+
*/
|
|
118
|
+
markOldSessionId(id: string): void;
|
|
119
|
+
}
|
|
120
|
+
export declare class DatabaseSessionStore implements SessionStore {
|
|
121
|
+
private tableName;
|
|
122
|
+
private connectionName?;
|
|
123
|
+
private tableEnsured;
|
|
124
|
+
constructor(tableName?: string, connectionName?: string | undefined);
|
|
125
|
+
private ensureTable;
|
|
126
|
+
read(id: string): Promise<SessionData | null>;
|
|
127
|
+
write(id: string, data: SessionData, ttl: number): Promise<void>;
|
|
128
|
+
destroy(id: string): Promise<void>;
|
|
129
|
+
gc(maxLifetime: number): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
export declare class FileSessionStore implements SessionStore {
|
|
132
|
+
private dir;
|
|
133
|
+
constructor(directory?: string);
|
|
134
|
+
private filePath;
|
|
135
|
+
private ensureDir;
|
|
136
|
+
read(id: string): Promise<SessionData | null>;
|
|
137
|
+
write(id: string, data: SessionData, ttl: number): Promise<void>;
|
|
138
|
+
destroy(id: string): Promise<void>;
|
|
139
|
+
gc(_maxLifetime: number): Promise<void>;
|
|
140
|
+
}
|
|
141
|
+
export declare class RedisSessionStore implements SessionStore {
|
|
142
|
+
private redis;
|
|
143
|
+
private prefix;
|
|
144
|
+
constructor(options?: {
|
|
145
|
+
client?: any;
|
|
146
|
+
prefix?: string;
|
|
147
|
+
url?: string;
|
|
148
|
+
});
|
|
149
|
+
private _url?;
|
|
150
|
+
private _connecting?;
|
|
151
|
+
private getClient;
|
|
152
|
+
read(id: string): Promise<SessionData | null>;
|
|
153
|
+
write(id: string, data: SessionData, ttl: number): Promise<void>;
|
|
154
|
+
destroy(id: string): Promise<void>;
|
|
155
|
+
gc(_maxLifetime: number): Promise<void>;
|
|
156
|
+
}
|
|
157
|
+
import { Middleware, type MiddlewareContext, type NextFunction } from '../middleware/Middleware.js';
|
|
158
|
+
export declare class SessionMiddleware extends Middleware {
|
|
159
|
+
private config;
|
|
160
|
+
constructor(userConfig: SessionConfig);
|
|
161
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
162
|
+
private getSessionIdFromCookie;
|
|
163
|
+
private signId;
|
|
164
|
+
private verifySignedId;
|
|
165
|
+
private buildCookieString;
|
|
166
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Session, SessionMiddleware, MemorySessionStore, DatabaseSessionStore, FileSessionStore, RedisSessionStore, type SessionStore, type SessionConfig, type SessionData, } from './Session.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var A=Object.defineProperty;var N=(c,e)=>()=>(c&&(e=c(c=0)),e);var E=(c,e)=>{for(var t in e)A(c,t,{get:e[t],enumerable:!0})};function T(c,e){let t=Symbol.for(c),s=globalThis;return s[t]||(s[t]=e()),s[t]}var D=N(()=>{"use strict"});var h={};E(h,{Connection:()=>M});var v,M,p=N(()=>{"use strict";D();v=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 n=await this.createConnection(s);return this.connections.set(t,n),n.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 n=await this.connection(s),i=this.getConfig(s);switch(i.driver){case"sqlite":{let r=await this.rawClient(s),a=t.map(l=>typeof l=="boolean"?l?1:0:l instanceof Date?l.toISOString():l),o=r.prepare(e),d=e.trimStart().toUpperCase();return d.startsWith("SELECT")||d.startsWith("PRAGMA")||d.startsWith("WITH")?o.all(...a):o.run(...a)}case"postgres":return(await this.rawClient(s))(e,...t);case"mysql":{let r=await this.rawClient(s),[a]=await r.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),n=await this.rawClient(t);switch(s.driver){case"sqlite":{n.exec("BEGIN");try{let i=await e();return n.exec("COMMIT"),i}catch(i){throw n.exec("ROLLBACK"),i}}case"postgres":{await n`BEGIN`;try{let i=await e();return await n`COMMIT`,i}catch(i){throw await n`ROLLBACK`,i}}case"mysql":{let i=await n.getConnection();await i.beginTransaction();try{let r=await e();return await i.commit(),i.release(),r}catch(r){throw await i.rollback(),i.release(),r}}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:n}=await import("drizzle-orm/better-sqlite3"),i=new s(t);return i.pragma("journal_mode = WAL"),i.pragma("foreign_keys = ON"),{drizzle:n(i),config:e,rawClient:i}}catch(s){let n;try{n=(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 n(t);i.exec("PRAGMA journal_mode = WAL"),i.exec("PRAGMA foreign_keys = ON");let r={prepare(o){let d=i.prepare(o);return{all(...l){return d.all(...l)},run(...l){return d.run(...l)},get(...l){return d.get(...l)}}},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(r)}catch{a=r}return{drizzle:a,config:e,rawClient:r}}}async createPostgresConnection(e){let t=(await import("postgres")).default,{drizzle:s}=await import("drizzle-orm/postgres-js"),n=e.url??`postgres://${e.user}:${e.password}@${e.host??"localhost"}:${e.port??5432}/${e.database}`,i=t(n);return{drizzle:s(i),config:e,rawClient:i}}async createMySQLConnection(e){let t=await import("mysql2/promise"),{drizzle:s}=await import("drizzle-orm/mysql2"),n=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(n),config:e,rawClient:n}}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{}}},M=T("svelar.connection",()=>new v)});import{randomBytes as R,createHmac as P,timingSafeEqual as O}from"crypto";import{promises as u}from"fs";import{join as g}from"path";var w=class{};var f=class c{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=c.generateId();this.id=t,this.dirty=!0;let s=m.get(e);return s instanceof y&&s.markOldSessionId(e),m.delete(e),t}static generateId(){return R(32).toString("hex")}},m=new Map,y=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}),m.set(e,this)}async destroy(e){this.sessions.delete(e),m.delete(e)}async gc(e){let t=Date.now();for(let[s,n]of this.sessions)t>n.expiresAt&&(this.sessions.delete(s),m.delete(s))}markOldSessionId(e){this.oldSessionIds.add(e),this.sessions.delete(e)}},S=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(()=>(p(),h));switch(e.getDriver(this.connectionName)){case"sqlite":await e.raw(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
payload TEXT NOT NULL,
|
|
4
|
+
expires_at TEXT NOT NULL
|
|
5
|
+
)`,[],this.connectionName);break;case"postgres":await e.raw(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
6
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
7
|
+
payload TEXT NOT NULL,
|
|
8
|
+
expires_at TIMESTAMPTZ NOT NULL
|
|
9
|
+
)`,[],this.connectionName);break;case"mysql":await e.raw(`CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
10
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
11
|
+
payload TEXT NOT NULL,
|
|
12
|
+
expires_at DATETIME NOT NULL
|
|
13
|
+
) ENGINE=InnoDB`,[],this.connectionName);break}this.tableEnsured=!0}catch{}}async read(e){await this.ensureTable();let{Connection:t}=await Promise.resolve().then(()=>(p(),h)),s=await t.raw(`SELECT payload, expires_at FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName);if(s.length===0)return null;let n=s[0];if(new Date(n.expires_at)<new Date)return await this.destroy(e),null;try{return JSON.parse(n.payload)}catch{return null}}async write(e,t,s){await this.ensureTable();let{Connection:n}=await Promise.resolve().then(()=>(p(),h)),i=JSON.stringify(t),r=new Date(Date.now()+s*1e3).toISOString(),a=n.getDriver(this.connectionName);a==="sqlite"?await n.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
14
|
+
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, expires_at = excluded.expires_at`,[e,i,r],this.connectionName):a==="postgres"?await n.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES ($1, $2, $3)
|
|
15
|
+
ON CONFLICT(id) DO UPDATE SET payload = $2, expires_at = $3`,[e,i,r],this.connectionName):await n.raw(`INSERT INTO ${this.tableName} (id, payload, expires_at) VALUES (?, ?, ?)
|
|
16
|
+
ON DUPLICATE KEY UPDATE payload = VALUES(payload), expires_at = VALUES(expires_at)`,[e,i,r],this.connectionName)}async destroy(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),h));await t.raw(`DELETE FROM ${this.tableName} WHERE id = ?`,[e],this.connectionName)}async gc(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),h));await t.raw(`DELETE FROM ${this.tableName} WHERE expires_at < ?`,[new Date().toISOString()],this.connectionName)}},C=class{dir;constructor(e){this.dir=e??g(process.cwd(),"storage","sessions")}filePath(e){let t=e.replace(/[^a-zA-Z0-9_-]/g,"");return g(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 n={data:t,expiresAt:new Date(Date.now()+s*1e3).toISOString()};await u.writeFile(this.filePath(e),JSON.stringify(n),"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 n of t)if(n.endsWith(".json"))try{let i=await u.readFile(g(this.dir,n),"utf-8"),r=JSON.parse(i);new Date(r.expiresAt)<s&&await u.unlink(g(this.dir,n))}catch{await u.unlink(g(this.dir,n)).catch(()=>{})}}catch{}}},x=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){}},b=class extends w{config;constructor(e){super(),this.config={cookieName:"svelar_session",lifetime:7200,secret:process.env.APP_KEY??(()=>{throw new Error("APP_KEY is not set. Set it in your .env file before using sessions.")})(),path:"/",domain:"",secure:process.env.NODE_ENV==="production",httpOnly:!0,sameSite:"lax",...e}}async handle(e,t){let s=e.event.request.headers.get("cookie")??"",n=this.getSessionIdFromCookie(s),i=null;if(n){let o=this.verifySignedId(n);o?(i=await this.config.store.read(o),n=o):n=null}n||(n=f.generateId());let r=new f(n,i??{});e.event.locals.session=r,e.locals.session=r;let a=await t();if(r.isDirty()&&await this.config.store.write(r.id,r.toPersist(),this.config.lifetime),a instanceof Response){let o=this.signId(r.id),d=this.buildCookieString(o);a.headers.append("Set-Cookie",d)}return a}getSessionIdFromCookie(e){let t=e.split(";").map(s=>s.trim());for(let s of t){let[n,...i]=s.split("=");if(n===this.config.cookieName)return decodeURIComponent(i.join("="))}return null}signId(e){let t=P("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),n=e.slice(t+1),i=P("sha256",this.config.secret).update(s).digest("base64url");if(n.length!==i.length)return null;let r=Buffer.from(n),a=Buffer.from(i);if(r.length!==a.length)return null;try{if(O(r,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("; ")}};export{S as DatabaseSessionStore,C as FileSessionStore,y as MemorySessionStore,x as RedisSessionStore,f as Session,b as SessionMiddleware};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Storage / Filesystem
|
|
3
|
+
*
|
|
4
|
+
* Laravel-inspired filesystem abstraction with local and S3-compatible drivers.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Storage } from 'svelar/storage';
|
|
9
|
+
*
|
|
10
|
+
* // Configure
|
|
11
|
+
* Storage.configure({
|
|
12
|
+
* default: 'local',
|
|
13
|
+
* disks: {
|
|
14
|
+
* local: { driver: 'local', root: './storage' },
|
|
15
|
+
* public: { driver: 'local', root: './storage/public', urlPrefix: '/storage' },
|
|
16
|
+
* },
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Usage
|
|
20
|
+
* await Storage.put('avatars/user-1.png', buffer);
|
|
21
|
+
* const content = await Storage.get('avatars/user-1.png');
|
|
22
|
+
* const exists = await Storage.exists('avatars/user-1.png');
|
|
23
|
+
* await Storage.delete('avatars/user-1.png');
|
|
24
|
+
* const files = await Storage.files('avatars');
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export type StorageDriver = 'local' | 's3';
|
|
28
|
+
export interface DiskConfig {
|
|
29
|
+
driver: StorageDriver;
|
|
30
|
+
/** Root path for local driver */
|
|
31
|
+
root?: string;
|
|
32
|
+
/** URL prefix for public files */
|
|
33
|
+
urlPrefix?: string;
|
|
34
|
+
/** S3 / RustFS configuration */
|
|
35
|
+
bucket?: string;
|
|
36
|
+
region?: string;
|
|
37
|
+
/** S3-compatible endpoint URL (e.g. http://rustfs:9000 for RustFS/MinIO) */
|
|
38
|
+
endpoint?: string;
|
|
39
|
+
accessKeyId?: string;
|
|
40
|
+
secretAccessKey?: string;
|
|
41
|
+
/** Force path-style addressing (required for RustFS/MinIO, default: true) */
|
|
42
|
+
forcePathStyle?: boolean;
|
|
43
|
+
/** Optional prefix/directory within the bucket */
|
|
44
|
+
prefix?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface StorageConfig {
|
|
47
|
+
default: string;
|
|
48
|
+
disks: Record<string, DiskConfig>;
|
|
49
|
+
}
|
|
50
|
+
export interface FileInfo {
|
|
51
|
+
path: string;
|
|
52
|
+
name: string;
|
|
53
|
+
extension: string;
|
|
54
|
+
size: number;
|
|
55
|
+
lastModified: Date;
|
|
56
|
+
}
|
|
57
|
+
interface Disk {
|
|
58
|
+
get(path: string): Promise<Buffer>;
|
|
59
|
+
getText(path: string): Promise<string>;
|
|
60
|
+
put(path: string, content: string | Buffer): Promise<void>;
|
|
61
|
+
append(path: string, content: string | Buffer): Promise<void>;
|
|
62
|
+
exists(path: string): Promise<boolean>;
|
|
63
|
+
delete(path: string): Promise<boolean>;
|
|
64
|
+
copy(from: string, to: string): Promise<void>;
|
|
65
|
+
move(from: string, to: string): Promise<void>;
|
|
66
|
+
files(directory?: string): Promise<string[]>;
|
|
67
|
+
allFiles(directory?: string): Promise<string[]>;
|
|
68
|
+
directories(directory?: string): Promise<string[]>;
|
|
69
|
+
makeDirectory(path: string): Promise<void>;
|
|
70
|
+
deleteDirectory(path: string): Promise<void>;
|
|
71
|
+
size(path: string): Promise<number>;
|
|
72
|
+
lastModified(path: string): Promise<Date>;
|
|
73
|
+
url(path: string): string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* S3-compatible storage disk using @aws-sdk/client-s3.
|
|
77
|
+
* Works with RustFS, MinIO, AWS S3, and any S3-compatible service.
|
|
78
|
+
* Requires: npm install @aws-sdk/client-s3 (peer dependency, loaded dynamically)
|
|
79
|
+
*/
|
|
80
|
+
declare class S3Disk implements Disk {
|
|
81
|
+
private config;
|
|
82
|
+
private _client;
|
|
83
|
+
private _s3Module;
|
|
84
|
+
constructor(config: DiskConfig);
|
|
85
|
+
private getS3;
|
|
86
|
+
private getClient;
|
|
87
|
+
private key;
|
|
88
|
+
get(path: string): Promise<Buffer>;
|
|
89
|
+
getText(path: string): Promise<string>;
|
|
90
|
+
put(path: string, content: string | Buffer): Promise<void>;
|
|
91
|
+
append(path: string, content: string | Buffer): Promise<void>;
|
|
92
|
+
exists(path: string): Promise<boolean>;
|
|
93
|
+
delete(path: string): Promise<boolean>;
|
|
94
|
+
copy(from: string, to: string): Promise<void>;
|
|
95
|
+
move(from: string, to: string): Promise<void>;
|
|
96
|
+
files(directory?: string): Promise<string[]>;
|
|
97
|
+
allFiles(directory?: string): Promise<string[]>;
|
|
98
|
+
directories(directory?: string): Promise<string[]>;
|
|
99
|
+
makeDirectory(_path: string): Promise<void>;
|
|
100
|
+
deleteDirectory(path: string): Promise<void>;
|
|
101
|
+
size(path: string): Promise<number>;
|
|
102
|
+
lastModified(path: string): Promise<Date>;
|
|
103
|
+
url(path: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* Generate a pre-signed URL for temporary access to a file.
|
|
106
|
+
* Requires: npm install @aws-sdk/s3-request-presigner
|
|
107
|
+
*/
|
|
108
|
+
temporaryUrl(path: string, expiresInSeconds?: number): Promise<string>;
|
|
109
|
+
/**
|
|
110
|
+
* Ensure the bucket exists, creating it if not.
|
|
111
|
+
* Useful for initial setup with RustFS/MinIO.
|
|
112
|
+
*/
|
|
113
|
+
ensureBucket(): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
declare class StorageManager {
|
|
116
|
+
private config;
|
|
117
|
+
private disks;
|
|
118
|
+
/**
|
|
119
|
+
* Configure the storage manager
|
|
120
|
+
*/
|
|
121
|
+
configure(config: StorageConfig): void;
|
|
122
|
+
/**
|
|
123
|
+
* Get a disk instance by name (or default)
|
|
124
|
+
*/
|
|
125
|
+
disk(name?: string): Disk;
|
|
126
|
+
get(path: string): Promise<Buffer>;
|
|
127
|
+
getText(path: string): Promise<string>;
|
|
128
|
+
put(path: string, content: string | Buffer): Promise<void>;
|
|
129
|
+
append(path: string, content: string | Buffer): Promise<void>;
|
|
130
|
+
exists(path: string): Promise<boolean>;
|
|
131
|
+
delete(path: string): Promise<boolean>;
|
|
132
|
+
copy(from: string, to: string): Promise<void>;
|
|
133
|
+
move(from: string, to: string): Promise<void>;
|
|
134
|
+
files(directory?: string): Promise<string[]>;
|
|
135
|
+
allFiles(directory?: string): Promise<string[]>;
|
|
136
|
+
directories(directory?: string): Promise<string[]>;
|
|
137
|
+
makeDirectory(path: string): Promise<void>;
|
|
138
|
+
deleteDirectory(path: string): Promise<void>;
|
|
139
|
+
size(path: string): Promise<number>;
|
|
140
|
+
lastModified(path: string): Promise<Date>;
|
|
141
|
+
url(path: string): string;
|
|
142
|
+
private createDisk;
|
|
143
|
+
/**
|
|
144
|
+
* Get the S3 disk instance with extended S3 methods (temporaryUrl, ensureBucket).
|
|
145
|
+
* Throws if the named disk is not an S3 disk.
|
|
146
|
+
*/
|
|
147
|
+
s3Disk(name?: string): S3Disk;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Global Storage singleton
|
|
151
|
+
*/
|
|
152
|
+
export declare const Storage: StorageManager;
|
|
153
|
+
export type { Disk, StorageManager };
|
|
154
|
+
export { S3Disk };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as w,writeFile as P,unlink as v,mkdir as a,readdir as f,stat as k,copyFile as b,rename as C}from"fs/promises";import{existsSync as x}from"fs";import{join as D,dirname as g}from"path";function p(c,t){let e=Symbol.for(c),i=globalThis;return i[e]||(i[e]=t()),i[e]}var h=class{constructor(t){this.config=t;if(!t.root)throw new Error('Local disk requires a "root" path.')}resolve(t){return D(this.config.root,t)}async get(t){return w(this.resolve(t))}async getText(t){return w(this.resolve(t),"utf-8")}async put(t,e){let i=this.resolve(t);await a(g(i),{recursive:!0}),await P(i,e)}async append(t,e){let{appendFile:i}=await import("fs/promises"),s=this.resolve(t);await a(g(s),{recursive:!0}),await i(s,e)}async exists(t){return x(this.resolve(t))}async delete(t){try{return await v(this.resolve(t)),!0}catch{return!1}}async copy(t,e){let i=this.resolve(e);await a(g(i),{recursive:!0}),await b(this.resolve(t),i)}async move(t,e){let i=this.resolve(e);await a(g(i),{recursive:!0}),await C(this.resolve(t),i)}async files(t=""){let e=this.resolve(t);try{return(await f(e,{withFileTypes:!0})).filter(s=>s.isFile()).map(s=>t?`${t}/${s.name}`:s.name)}catch{return[]}}async allFiles(t=""){let e=[],i=this.resolve(t);try{let s=await f(i,{withFileTypes:!0});for(let n of s){let r=t?`${t}/${n.name}`:n.name;n.isFile()?e.push(r):n.isDirectory()&&e.push(...await this.allFiles(r))}}catch{}return e}async directories(t=""){let e=this.resolve(t);try{return(await f(e,{withFileTypes:!0})).filter(s=>s.isDirectory()).map(s=>t?`${t}/${s.name}`:s.name)}catch{return[]}}async makeDirectory(t){await a(this.resolve(t),{recursive:!0})}async deleteDirectory(t){let{rm:e}=await import("fs/promises");await e(this.resolve(t),{recursive:!0,force:!0})}async size(t){return(await k(this.resolve(t))).size}async lastModified(t){return(await k(this.resolve(t))).mtime}url(t){return`${this.config.urlPrefix??""}/${t}`}},l=class{config;_client=null;_s3Module=null;constructor(t){if(!t.bucket)throw new Error('S3 disk requires a "bucket" name.');this.config=t}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 t=await this.getS3();return this._client=new t.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(t){let e=this.config.prefix;return e?`${e}/${t}`:t}async get(t){let e=await this.getS3(),n=await(await(await this.getClient()).send(new e.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(t)}))).Body.transformToByteArray();return Buffer.from(n)}async getText(t){return(await this.get(t)).toString("utf-8")}async put(t,e){let i=await this.getS3(),s=await this.getClient(),n=typeof e=="string"?Buffer.from(e,"utf-8"):e;await s.send(new i.PutObjectCommand({Bucket:this.config.bucket,Key:this.key(t),Body:n}))}async append(t,e){let i=null;try{i=await this.get(t)}catch{}let s=typeof e=="string"?Buffer.from(e,"utf-8"):e,n=i?Buffer.concat([i,s]):s;await this.put(t,n)}async exists(t){let e=await this.getS3(),i=await this.getClient();try{return await i.send(new e.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(t)})),!0}catch{return!1}}async delete(t){let e=await this.getS3(),i=await this.getClient();try{return await i.send(new e.DeleteObjectCommand({Bucket:this.config.bucket,Key:this.key(t)})),!0}catch{return!1}}async copy(t,e){let i=await this.getS3();await(await this.getClient()).send(new i.CopyObjectCommand({Bucket:this.config.bucket,CopySource:`${this.config.bucket}/${this.key(t)}`,Key:this.key(e)}))}async move(t,e){await this.copy(t,e),await this.delete(t)}async files(t=""){let e=await this.getS3(),i=await this.getClient(),s=this.key(t?`${t}/`:"");try{return((await i.send(new e.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:s,Delimiter:"/"}))).Contents??[]).map(r=>r.Key).filter(r=>r!==s).map(r=>{let o=this.config.prefix;return o?r.slice(o.length+1):r})}catch{return[]}}async allFiles(t=""){let e=await this.getS3(),i=await this.getClient(),s=this.key(t?`${t}/`:""),n=[],r;do{let o=await i.send(new e.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:s,ContinuationToken:r}));for(let u of o.Contents??[]){let m=this.config.prefix,y=m?u.Key.slice(m.length+1):u.Key;y&&n.push(y)}r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return n}async directories(t=""){let e=await this.getS3(),i=await this.getClient(),s=this.key(t?`${t}/`:"");try{return((await i.send(new e.ListObjectsV2Command({Bucket:this.config.bucket,Prefix:s,Delimiter:"/"}))).CommonPrefixes??[]).map(r=>{let o=this.config.prefix;return(o?r.Prefix.slice(o.length+1):r.Prefix).replace(/\/$/,"")}).filter(r=>r.length>0)}catch{return[]}}async makeDirectory(t){}async deleteDirectory(t){let e=await this.allFiles(t);for(let i of e)await this.delete(i)}async size(t){let e=await this.getS3();return(await(await this.getClient()).send(new e.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(t)}))).ContentLength??0}async lastModified(t){let e=await this.getS3();return(await(await this.getClient()).send(new e.HeadObjectCommand({Bucket:this.config.bucket,Key:this.key(t)}))).LastModified??new Date}url(t){let e=this.config.urlPrefix;if(e)return`${e}/${t}`;let i=this.config.endpoint??`https://s3.${this.config.region??"us-east-1"}.amazonaws.com`;return this.config.forcePathStyle!==!1?`${i}/${this.config.bucket}/${this.key(t)}`:`${i.replace("://",`://${this.config.bucket}.`)}/${this.key(t)}`}async temporaryUrl(t,e=3600){try{let i=await Function('return import("@aws-sdk/s3-request-presigner")')(),s=await this.getS3(),n=await this.getClient(),r=new s.GetObjectCommand({Bucket:this.config.bucket,Key:this.key(t)});return await i.getSignedUrl(n,r,{expiresIn:e})}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 t=await this.getS3(),e=await this.getClient();try{await e.send(new t.HeadBucketCommand({Bucket:this.config.bucket}))}catch{await e.send(new t.CreateBucketCommand({Bucket:this.config.bucket}))}}},d=class{config=null;disks=new Map;configure(t){this.config=t}disk(t){let e=t??this.config?.default??"local";if(this.disks.has(e))return this.disks.get(e);if(!this.config)throw new Error("Storage not configured. Call Storage.configure() first.");let i=this.config.disks[e];if(!i)throw new Error(`Storage disk "${e}" is not defined.`);let s=this.createDisk(i);return this.disks.set(e,s),s}async get(t){return this.disk().get(t)}async getText(t){return this.disk().getText(t)}async put(t,e){return this.disk().put(t,e)}async append(t,e){return this.disk().append(t,e)}async exists(t){return this.disk().exists(t)}async delete(t){return this.disk().delete(t)}async copy(t,e){return this.disk().copy(t,e)}async move(t,e){return this.disk().move(t,e)}async files(t){return this.disk().files(t)}async allFiles(t){return this.disk().allFiles(t)}async directories(t){return this.disk().directories(t)}async makeDirectory(t){return this.disk().makeDirectory(t)}async deleteDirectory(t){return this.disk().deleteDirectory(t)}async size(t){return this.disk().size(t)}async lastModified(t){return this.disk().lastModified(t)}url(t){return this.disk().url(t)}createDisk(t){switch(t.driver){case"local":return new h(t);case"s3":return new l(t);default:throw new Error(`Unknown storage driver: ${t.driver}`)}}s3Disk(t){let e=this.disk(t);if(!(e instanceof l))throw new Error(`Disk "${t??this.config?.default}" is not an S3 disk.`);return e}},M=p("svelar.storage",()=>new d);export{l as S3Disk,M as Storage};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Laravel-inspired Pipeline (Chain of Responsibility pattern).
|
|
5
|
+
* Passes data through a sequence of pipes, where each pipe can
|
|
6
|
+
* transform, validate, or halt the data before passing it on.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const result = await Pipeline.send(order)
|
|
11
|
+
* .through([
|
|
12
|
+
* ValidateStock,
|
|
13
|
+
* ApplyDiscount,
|
|
14
|
+
* CalculateTax,
|
|
15
|
+
* ChargePayment,
|
|
16
|
+
* ])
|
|
17
|
+
* .thenReturn();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* A pipe can be:
|
|
22
|
+
* - A class with a handle(data, next) method
|
|
23
|
+
* - An inline function (data, next) => result
|
|
24
|
+
*/
|
|
25
|
+
export type PipeFunction<T> = (passable: T, next: (passable: T) => Promise<T>) => Promise<T> | T;
|
|
26
|
+
export interface Pipe<T> {
|
|
27
|
+
handle(passable: T, next: (passable: T) => Promise<T>): Promise<T> | T;
|
|
28
|
+
}
|
|
29
|
+
export type PipeEntry<T> = (new () => Pipe<T>) | PipeFunction<T>;
|
|
30
|
+
export declare class Pipeline<T> {
|
|
31
|
+
private passable;
|
|
32
|
+
private pipes;
|
|
33
|
+
private onCatchFn?;
|
|
34
|
+
/**
|
|
35
|
+
* Set the data being sent through the pipeline.
|
|
36
|
+
*/
|
|
37
|
+
static send<T>(passable: T): Pipeline<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Set the pipes to process.
|
|
40
|
+
*/
|
|
41
|
+
through(pipes: PipeEntry<T>[]): this;
|
|
42
|
+
/**
|
|
43
|
+
* Add a single pipe to the pipeline.
|
|
44
|
+
*/
|
|
45
|
+
pipe(pipe: PipeEntry<T>): this;
|
|
46
|
+
/**
|
|
47
|
+
* Set an error handler for the pipeline.
|
|
48
|
+
* If a pipe throws, this handler receives the error and current passable,
|
|
49
|
+
* and can return a recovery value or re-throw.
|
|
50
|
+
*/
|
|
51
|
+
onCatch(handler: (error: Error, passable: T) => Promise<T> | T): this;
|
|
52
|
+
/**
|
|
53
|
+
* Run the pipeline and return the final result.
|
|
54
|
+
*/
|
|
55
|
+
thenReturn(): Promise<T>;
|
|
56
|
+
/**
|
|
57
|
+
* Run the pipeline with a final destination callback.
|
|
58
|
+
*/
|
|
59
|
+
then<R>(destination: (passable: T) => Promise<R> | R): Promise<R>;
|
|
60
|
+
/**
|
|
61
|
+
* Execute the pipeline.
|
|
62
|
+
*/
|
|
63
|
+
private run;
|
|
64
|
+
private isPipeClass;
|
|
65
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Date Utilities
|
|
3
|
+
*
|
|
4
|
+
* Timezone-safe date formatting, parsing, and relative time helpers.
|
|
5
|
+
* Built on date-fns to avoid the classic "off by one day" bug where
|
|
6
|
+
* `new Date('2024-01-15')` parses as UTC midnight and displays as
|
|
7
|
+
* January 14th in negative UTC offset timezones.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { formatDate, formatRelative, parseLocalDate, toLocal } from 'svelar/dates';
|
|
12
|
+
*
|
|
13
|
+
* // Safe date formatting (locale-aware)
|
|
14
|
+
* formatDate(user.createdAt); // "Mar 27, 2026"
|
|
15
|
+
* formatDate(user.createdAt, 'PPpp'); // "Mar 27, 2026, 2:30 PM"
|
|
16
|
+
* formatDate(user.createdAt, 'PP', { locale: pt }); // "27 de mar. de 2026"
|
|
17
|
+
*
|
|
18
|
+
* // Relative time
|
|
19
|
+
* formatRelative(Date.now() - 3600000); // "about 1 hour ago"
|
|
20
|
+
*
|
|
21
|
+
* // Parse date-only strings safely (as LOCAL, not UTC)
|
|
22
|
+
* parseLocalDate('2024-01-15'); // Jan 15 00:00 LOCAL time
|
|
23
|
+
*
|
|
24
|
+
* // Convert any Date/timestamp to local-safe Date
|
|
25
|
+
* toLocal(someUtcDate);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
import { format as fnsFormat, isValid, parseISO, differenceInDays, differenceInHours, differenceInMinutes, differenceInSeconds, startOfDay, endOfDay, isToday, isYesterday, isTomorrow, addDays, addHours, addMinutes, subDays, subHours, subMinutes, type Locale } from 'date-fns';
|
|
29
|
+
export type { Locale };
|
|
30
|
+
export interface FormatOptions {
|
|
31
|
+
/** date-fns locale object (e.g. import { pt } from 'date-fns/locale') */
|
|
32
|
+
locale?: Locale;
|
|
33
|
+
}
|
|
34
|
+
export type DateInput = Date | number | string;
|
|
35
|
+
/**
|
|
36
|
+
* Normalize any date input to a safe Date object.
|
|
37
|
+
*
|
|
38
|
+
* - Numbers (timestamps) → `new Date(ts)` (correct, timestamps are always UTC)
|
|
39
|
+
* - Date objects → returned as-is
|
|
40
|
+
* - ISO strings with time component → `parseISO()` (handles timezone offset)
|
|
41
|
+
* - Date-only strings like '2024-01-15' → parsed as LOCAL date to avoid
|
|
42
|
+
* the off-by-one-day bug (JS natively parses these as UTC midnight)
|
|
43
|
+
*/
|
|
44
|
+
export declare function toDate(input: DateInput): Date;
|
|
45
|
+
/**
|
|
46
|
+
* Parse a date-only string (e.g. '2024-01-15') as LOCAL midnight.
|
|
47
|
+
*
|
|
48
|
+
* The native `new Date('2024-01-15')` parses as UTC midnight, which causes
|
|
49
|
+
* the infamous off-by-one-day bug in negative UTC offset timezones.
|
|
50
|
+
* This function explicitly constructs the date from year/month/day parts
|
|
51
|
+
* using the local timezone.
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseLocalDate(dateStr: string): Date;
|
|
54
|
+
/**
|
|
55
|
+
* Ensure a date is treated as local midnight (strips time component).
|
|
56
|
+
* Useful when comparing dates without time (e.g. "is this the same day?").
|
|
57
|
+
*/
|
|
58
|
+
export declare function toLocal(input: DateInput): Date;
|
|
59
|
+
/**
|
|
60
|
+
* Format a date with a date-fns pattern. Defaults to 'PP' (locale-aware medium date).
|
|
61
|
+
*
|
|
62
|
+
* Common patterns:
|
|
63
|
+
* - 'PP' → "Mar 27, 2026"
|
|
64
|
+
* - 'PPP' → "March 27th, 2026"
|
|
65
|
+
* - 'PPpp' → "Mar 27, 2026, 2:30:00 PM"
|
|
66
|
+
* - 'p' → "2:30 PM"
|
|
67
|
+
* - 'Pp' → "Mar 27, 2026, 2:30 PM"
|
|
68
|
+
* - 'yyyy-MM-dd' → "2026-03-27"
|
|
69
|
+
*
|
|
70
|
+
* @see https://date-fns.org/docs/format
|
|
71
|
+
*/
|
|
72
|
+
export declare function formatDate(input: DateInput, pattern?: string, options?: FormatOptions): string;
|
|
73
|
+
/**
|
|
74
|
+
* Format a date as relative time from now (e.g. "about 2 hours ago", "in 3 days").
|
|
75
|
+
* Uses `formatDistanceToNow` with `addSuffix: true`.
|
|
76
|
+
*/
|
|
77
|
+
export declare function formatRelative(input: DateInput, options?: FormatOptions): string;
|
|
78
|
+
/**
|
|
79
|
+
* Format a short relative time without suffix (e.g. "2h", "3d", "5m").
|
|
80
|
+
* Useful for compact UI like tables and badges.
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatShortRelative(input: DateInput): string;
|
|
83
|
+
/**
|
|
84
|
+
* Format the distance between two dates (e.g. "about 2 hours").
|
|
85
|
+
*/
|
|
86
|
+
export declare function formatBetween(start: DateInput, end: DateInput, options?: FormatOptions): string;
|
|
87
|
+
/**
|
|
88
|
+
* Return a human-readable "time ago" string. Alias for `formatRelative`.
|
|
89
|
+
* Inspired by Laravel's Carbon `diffForHumans()`.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* timeAgo(user.createdAt) // "about 2 hours ago"
|
|
93
|
+
* timeAgo(post.publishedAt, { locale: pt }) // "cerca de 2 horas atrás"
|
|
94
|
+
*/
|
|
95
|
+
export declare function timeAgo(input: DateInput, options?: FormatOptions): string;
|
|
96
|
+
/**
|
|
97
|
+
* Cast date fields on a model-like object. Converts raw timestamps/strings
|
|
98
|
+
* to timezone-safe Date objects and adds a `.timeAgo()` method.
|
|
99
|
+
*
|
|
100
|
+
* Server stores UTC → UI always shows in the user's local timezone.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* // In a server load function
|
|
105
|
+
* const user = await db.query.users.findFirst({ where: eq(users.id, id) });
|
|
106
|
+
*
|
|
107
|
+
* return {
|
|
108
|
+
* user: castDates(user, ['createdAt', 'updatedAt', 'lastLoginAt'])
|
|
109
|
+
* };
|
|
110
|
+
*
|
|
111
|
+
* // In the template
|
|
112
|
+
* user.createdAt // Date object (local timezone)
|
|
113
|
+
* user.createdAt_ago // "about 2 hours ago"
|
|
114
|
+
* user.createdAt_short // "2h"
|
|
115
|
+
* user.createdAt_formatted // "Mar 27, 2026"
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare function castDates<T extends Record<string, any>>(obj: T, dateFields: (keyof T)[], options?: FormatOptions): T & Record<string, any>;
|
|
119
|
+
/**
|
|
120
|
+
* Create a reusable date caster for a specific set of fields.
|
|
121
|
+
* Useful when you cast the same fields on every request.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* // Define once
|
|
126
|
+
* const castUserDates = dateCaster(['createdAt', 'updatedAt', 'lastLoginAt']);
|
|
127
|
+
*
|
|
128
|
+
* // Use in load functions
|
|
129
|
+
* return { user: castUserDates(user) };
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export declare function dateCaster<K extends string>(fields: K[]): <T extends Record<string, any>>(obj: T, options?: FormatOptions) => T & Record<string, any>;
|
|
133
|
+
export { isToday, isYesterday, isTomorrow, isValid };
|
|
134
|
+
export { differenceInDays, differenceInHours, differenceInMinutes, differenceInSeconds, };
|
|
135
|
+
export { startOfDay, endOfDay, addDays, addHours, addMinutes, subDays, subHours, subMinutes, };
|
|
136
|
+
export { parseISO, fnsFormat as format };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{format as p,formatDistanceToNow as m,formatDistance as D,isValid as d,parseISO as y,differenceInDays as M,differenceInHours as O,differenceInMinutes as T,differenceInSeconds as F,startOfDay as _,endOfDay as b,isToday as S,isYesterday as L,isTomorrow as R,addDays as v,addHours as P,addMinutes as k,subDays as H,subHours as E,subMinutes as K}from"date-fns";import{enUS as c}from"date-fns/locale";function s(t){if(t instanceof Date)return t;if(typeof t=="number")return new Date(t);if(typeof t=="string"){let e=t.trim();if(/^\d{4}-\d{2}-\d{2}$/.test(e))return x(e);let r=y(e);if(d(r))return r;let o=new Date(e);if(d(o))return o;throw new Error(`Invalid date string: "${t}"`)}throw new Error(`Invalid date input: ${t}`)}function x(t){let[e,r,o]=t.split("-").map(Number);return new Date(e,r-1,o)}function Y(t){let e=s(t);return new Date(e.getFullYear(),e.getMonth(),e.getDate())}function g(t,e="PP",r){let o=s(t);return p(o,e,{locale:r?.locale??c})}function l(t,e){let r=s(t);return m(r,{addSuffix:!0,locale:e?.locale??c})}function h(t){let e=s(t),r=Date.now(),o=Math.abs(r-e.getTime()),i=Math.floor(o/1e3);if(i<60)return`${i}s`;let f=Math.floor(o/6e4);if(f<60)return`${f}m`;let a=Math.floor(o/36e5);if(a<24)return`${a}h`;let n=Math.floor(o/864e5);if(n<30)return`${n}d`;let u=Math.floor(n/30);return u<12?`${u}mo`:`${Math.floor(n/365)}y`}function A(t,e,r){return D(s(t),s(e),{locale:r?.locale??c})}function B(t,e){return l(t,e)}function I(t,e,r){let o={...t};for(let i of e){let f=t[i];if(f==null)continue;let a=String(i),n=s(f);o[a]=n,o[`${a}_ago`]=l(n,r),o[`${a}_short`]=h(n),o[`${a}_formatted`]=g(n,"PP",r)}return o}function C(t){return(e,r)=>I(e,t,r)}export{v as addDays,P as addHours,k as addMinutes,I as castDates,C as dateCaster,M as differenceInDays,O as differenceInHours,T as differenceInMinutes,F as differenceInSeconds,b as endOfDay,p as format,A as formatBetween,g as formatDate,l as formatRelative,h as formatShortRelative,S as isToday,R as isTomorrow,d as isValid,L as isYesterday,y as parseISO,x as parseLocalDate,_ as startOfDay,H as subDays,E as subHours,K as subMinutes,B as timeAgo,s as toDate,Y as toLocal};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Support Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helpers for UUIDv7, ULID, and other common operations.
|
|
5
|
+
*/
|
|
6
|
+
export { uuidv7, ulid, isUuidv7, isUlid, uuidv7Timestamp, ulidTimestamp } from './uuid.js';
|
|
7
|
+
export { singleton } from './singleton.js';
|
|
8
|
+
export { Pipeline, type Pipe, type PipeFunction, type PipeEntry } from './Pipeline.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomBytes as p}from"crypto";function u(){let s=Date.now(),e=Buffer.alloc(6);e.writeUIntBE(s,0,6);let t=p(10),n=Buffer.alloc(16);e.copy(n,0),n[6]=112|t[0]&15,n[7]=t[1],n[8]=128|t[2]&63,n[9]=t[3],n[10]=t[4],n[11]=t[5],n[12]=t[6],n[13]=t[7],n[14]=t[8],n[15]=t[9];let r=n.toString("hex");return[r.slice(0,8),r.slice(8,12),r.slice(12,16),r.slice(16,20),r.slice(20,32)].join("-")}var a="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function l(){let s=Date.now(),e="",t=s;for(let i=0;i<10;i++)e=a[t&31]+e,t=Math.floor(t/32);let n=p(10),r="";for(let i=0;i<10;i++)r+=a[n[i]&31];let o=p(6);for(let i=0;i<6;i++)r+=a[o[i]&31];return e+r}function T(s){return/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s)}function f(s){return/^[0-9A-HJKMNP-TV-Z]{26}$/i.test(s)}function h(s){let e=s.replace(/-/g,"").slice(0,12),t=parseInt(e,16);return new Date(t)}function P(s){let e=s.slice(0,10),t=0;for(let n of e)t=t*32+a.indexOf(n.toUpperCase());return new Date(t)}function m(s,e){let t=Symbol.for(s),n=globalThis;return n[t]||(n[t]=e()),n[t]}var c=class s{passable;pipes=[];onCatchFn;static send(e){let t=new s;return t.passable=e,t}through(e){return this.pipes=e,this}pipe(e){return this.pipes.push(e),this}onCatch(e){return this.onCatchFn=e,this}async thenReturn(){return this.run(e=>e)}async then(e){return this.run(e)}async run(e){let t=async n=>e(n);for(let n=this.pipes.length-1;n>=0;n--){let r=this.pipes[n],o=t;t=async i=>this.isPipeClass(r)?new r().handle(i,o):r(i,o)}try{return await t(this.passable)}catch(n){if(this.onCatchFn)return await this.onCatchFn(n,this.passable);throw n}}isPipeClass(e){return typeof e=="function"&&e.prototype&&typeof e.prototype.handle=="function"}};export{c as Pipeline,f as isUlid,T as isUuidv7,m as singleton,l as ulid,P as ulidTimestamp,u as uuidv7,h as uuidv7Timestamp};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-bundle singleton helper.
|
|
3
|
+
*
|
|
4
|
+
* tsup bundles each entry point independently, duplicating module-scoped variables.
|
|
5
|
+
* This means `export const Foo = new FooManager()` produces a separate instance in
|
|
6
|
+
* every bundle that imports the file. Using `Symbol.for()` on `globalThis` ensures
|
|
7
|
+
* only one instance exists per key across the entire process, regardless of how many
|
|
8
|
+
* bundles reference it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function singleton<T>(key: string, factory: () => T): T;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUIDv7 & ULID generators
|
|
3
|
+
*
|
|
4
|
+
* UUIDv7 (RFC 9562) embeds a Unix timestamp in the first 48 bits,
|
|
5
|
+
* making IDs time-sortable while remaining globally unique.
|
|
6
|
+
*
|
|
7
|
+
* ULID (Universally Unique Lexicographically Sortable Identifier)
|
|
8
|
+
* is a 26-character Crockford Base32 string with 48-bit timestamp + 80 random bits.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Generate a UUIDv7 (time-ordered UUID per RFC 9562).
|
|
12
|
+
*
|
|
13
|
+
* Layout (128 bits):
|
|
14
|
+
* 48-bit unix_ts_ms | 4-bit version (0111) | 12-bit rand_a
|
|
15
|
+
* 2-bit variant (10) | 62-bit rand_b
|
|
16
|
+
*/
|
|
17
|
+
export declare function uuidv7(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Generate a ULID (Universally Unique Lexicographically Sortable Identifier).
|
|
20
|
+
*
|
|
21
|
+
* 26 characters: 10-char timestamp (48-bit ms) + 16-char randomness (80-bit).
|
|
22
|
+
* Crockford Base32 encoded, naturally sortable by creation time.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ulid(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a string is a valid UUIDv7.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isUuidv7(value: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a string is a valid ULID.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isUlid(value: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Extract the timestamp from a UUIDv7.
|
|
35
|
+
*/
|
|
36
|
+
export declare function uuidv7Timestamp(uuid: string): Date;
|
|
37
|
+
/**
|
|
38
|
+
* Extract the timestamp from a ULID.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ulidTimestamp(id: string): Date;
|