@seneris/nosework 0.1.0 → 0.3.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/README.md CHANGED
@@ -6,10 +6,24 @@ Privacy-focused, self-hosted analytics for your app suite.
6
6
 
7
7
  - **Cookieless tracking** - No consent banners needed
8
8
  - **City-level geolocation** - Via Vercel's geo headers
9
- - **GDPR compliant** - No PII stored, IPs are hashed
9
+ - **No PII stored** - IPs are used for hashing only and never written; raw User-Agents are discarded
10
10
  - **Multi-site support** - Track all your apps with one shared database
11
11
  - **Full API** - Query your analytics data programmatically
12
12
 
13
+ See [Privacy and compliance](#privacy-and-compliance) below for the short
14
+ version. `docs/PRIVACY.md` in the source repository has the full accounting —
15
+ exactly what is collected, how visitors are counted without cookies, the
16
+ limits of those claims, and text you can adapt for a privacy page. That
17
+ document is not included in the npm package.
18
+
19
+ ## Upgrading to 0.3.0
20
+
21
+ - The visitor hash is now scoped per site: the input changed from `ip|userAgent|salt` to `siteId|ip|userAgent|salt`.
22
+ - This is not a data migration — hashes already rotate nightly at UTC midnight, so deploying this change is equivalent to one extra rotation.
23
+ - The only practical effect: visitors who appear both before and after the deploy are counted twice on the deploy day. Historical rows keep their old hashes and remain internally consistent within each day; no backfill is possible or needed.
24
+ - Why it changed: previously the same visitor produced an identical hash on every site sharing the database, which made `visitorHash` a cross-site join key.
25
+ - **API removal:** `computeVisitorIds` is no longer exported from the package root (it remains exported from its module for testing). Update any import of it from `@seneris/nosework`.
26
+
13
27
  ## Architecture
14
28
 
15
29
  ```
@@ -19,7 +33,7 @@ Privacy-focused, self-hosted analytics for your app suite.
19
33
  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
20
34
  │ │ │
21
35
  │ import { trackPageView } │
22
- │ from 'nosework'
36
+ │ from '@seneris/nosework'
23
37
  │ │ │
24
38
  └────────────────┼────────────────┘
25
39
 
@@ -81,7 +95,7 @@ If you're not using Vercel, you have options:
81
95
  ### 1. Install
82
96
 
83
97
  ```bash
84
- bun add nosework
98
+ bun add @seneris/nosework
85
99
  ```
86
100
 
87
101
  ### 2. Set Environment Variables
@@ -89,26 +103,16 @@ bun add nosework
89
103
  Add to your `.env`:
90
104
 
91
105
  ```env
92
- DATABASE_URL="postgresql://user:pass@host/dbname"
106
+ ANALYTICS_DATABASE_URL="postgresql://user:pass@host/dbname"
93
107
  ANALYTICS_SITE_ID="my-app" # Unique identifier for this app
94
108
  ```
95
109
 
96
- **MoopySuite Users:** If you're using MoopySuite OAuth, use your existing `MOOPY_CLIENT_ID` as the site identifier - no new env var needed! See [MoopySuite Integration](./docs/MOOPYSUITE_INTEGRATION.md).
97
-
98
- ### 3. Run Database Migration
99
-
100
- First time only (or when updating nosework):
101
-
102
- ```bash
103
- bunx prisma migrate deploy --schema=./node_modules/nosework/prisma/schema.prisma
104
- ```
105
-
106
- ### 4. Add Tracking Endpoint
110
+ ### 3. Add Tracking Endpoint
107
111
 
108
112
  Create `app/api/analytics/track/route.ts`:
109
113
 
110
114
  ```typescript
111
- import { trackPageView } from 'nosework';
115
+ import { trackPageView } from '@seneris/nosework';
112
116
  import { NextRequest, NextResponse } from 'next/server';
113
117
 
114
118
  export async function POST(request: NextRequest) {
@@ -137,7 +141,7 @@ export async function POST(request: NextRequest) {
137
141
  }
138
142
  ```
139
143
 
140
- ### 5. Add Client Component
144
+ ### 4. Add Client Component
141
145
 
142
146
  Create `components/Analytics.tsx`:
143
147
 
@@ -177,7 +181,7 @@ export function Analytics() {
177
181
  }
178
182
  ```
179
183
 
180
- ### 6. Add to Layout
184
+ ### 5. Add to Layout
181
185
 
182
186
  In `app/layout.tsx`:
183
187
 
@@ -210,7 +214,7 @@ If you prefer not to use a client component, you can track in Next.js middleware
210
214
  Create `middleware.ts`:
211
215
 
212
216
  ```typescript
213
- import { trackPageView } from 'nosework';
217
+ import { trackPageView } from '@seneris/nosework';
214
218
  import { NextResponse } from 'next/server';
215
219
  import type { NextRequest } from 'next/server';
216
220
 
@@ -261,7 +265,7 @@ export const config = {
261
265
  Beyond page views, you can track custom events:
262
266
 
263
267
  ```typescript
264
- import { trackEvent } from 'nosework';
268
+ import { trackEvent } from '@seneris/nosework';
265
269
 
266
270
  // In an API route or server action
267
271
  await trackEvent({
@@ -289,7 +293,7 @@ Common events to track:
289
293
  ### Basic Stats
290
294
 
291
295
  ```typescript
292
- import { getStats } from 'nosework';
296
+ import { getStats } from '@seneris/nosework';
293
297
 
294
298
  const stats = await getStats({
295
299
  siteId: 'my-app',
@@ -309,7 +313,7 @@ const stats = await getStats({
309
313
  ### Top Pages
310
314
 
311
315
  ```typescript
312
- import { getTopPages } from 'nosework';
316
+ import { getTopPages } from '@seneris/nosework';
313
317
 
314
318
  const pages = await getTopPages({
315
319
  siteId: 'my-app',
@@ -329,7 +333,7 @@ const pages = await getTopPages({
329
333
  ### Location Breakdown
330
334
 
331
335
  ```typescript
332
- import { getLocations } from 'nosework';
336
+ import { getLocations } from '@seneris/nosework';
333
337
 
334
338
  const locations = await getLocations({
335
339
  siteId: 'my-app',
@@ -349,7 +353,7 @@ const locations = await getLocations({
349
353
  ### Traffic Sources
350
354
 
351
355
  ```typescript
352
- import { getReferrers } from 'nosework';
356
+ import { getReferrers } from '@seneris/nosework';
353
357
 
354
358
  const referrers = await getReferrers({
355
359
  siteId: 'my-app',
@@ -362,7 +366,7 @@ const referrers = await getReferrers({
362
366
  ### Device/Browser Breakdown
363
367
 
364
368
  ```typescript
365
- import { getDevices } from 'nosework';
369
+ import { getDevices } from '@seneris/nosework';
366
370
 
367
371
  const devices = await getDevices({
368
372
  siteId: 'my-app',
@@ -374,7 +378,7 @@ const devices = await getDevices({
374
378
  ### Time Series (for charts)
375
379
 
376
380
  ```typescript
377
- import { getTimeSeries } from 'nosework';
381
+ import { getTimeSeries } from '@seneris/nosework';
378
382
 
379
383
  const data = await getTimeSeries({
380
384
  siteId: 'my-app',
@@ -391,18 +395,6 @@ const data = await getTimeSeries({
391
395
  // ]
392
396
  ```
393
397
 
394
- ### Site Management
395
-
396
- ```typescript
397
- import { listSites, getOrCreateSite } from 'nosework';
398
-
399
- // List all tracked sites
400
- const sites = await listSites();
401
-
402
- // Get or create a site (useful for initial setup)
403
- const site = await getOrCreateSite('example.com', 'My Example Site');
404
- ```
405
-
406
398
  ---
407
399
 
408
400
  ## Privacy Design
@@ -410,7 +402,9 @@ const site = await getOrCreateSite('example.com', 'My Example Site');
410
402
  nosework is designed to be privacy-friendly by default:
411
403
 
412
404
  ### No Cookies
413
- Visitors are identified by a hash of IP + User-Agent + daily salt. This hash rotates daily, so you can't track users across days (by design).
405
+ Visitors are identified by a hash of site ID + IP + User-Agent + daily salt. The site ID is part of the input, so the same visitor produces a different hash on every site sharing the database — you cannot group page views to follow one person across your apps. The salt rotates daily, so you can't follow anyone across days either (by design).
406
+
407
+ That separation holds against a query, not against you. For as long as a day's salt still exists — 7 days, if you schedule `cleanupOldSalts()` — anyone holding the database can recompute the hash for any site ID from a candidate IP and User-Agent. And the location, browser, OS and device columns are identical for the same visitor on all your sites, so on a low-traffic site that combination can correlate them with no hash involved at all.
414
408
 
415
409
  ### No PII Storage
416
410
  - IP addresses are **never stored** - only used for hashing
@@ -423,13 +417,35 @@ Sessions are inferred using a 30-minute window hash. No session cookies needed.
423
417
  ### Bot Filtering
424
418
  Known bots (Googlebot, crawlers, etc.) are automatically flagged and excluded from statistics.
425
419
 
426
- ### GDPR Compliance
427
- Because no cookies are used and no PII is stored, you typically don't need:
428
- - Cookie consent banners
429
- - Privacy policy updates for analytics
430
- - Data processing agreements
420
+ ---
431
421
 
432
- *Note: Consult with a legal professional for your specific situation.*
422
+ ## Privacy and Compliance
423
+
424
+ Short version, for the GDPR/ePrivacy question this package usually gets asked:
425
+
426
+ - **No cookie consent banner.** Nothing is written to, or read from, the
427
+ visitor's device, so ePrivacy Directive Art. 5(3) — the rule that makes
428
+ consent banners necessary — is never triggered. This is the same basis
429
+ cookieless analytics products rely on.
430
+ - **You do still need privacy-policy text.** The processing that does happen
431
+ (deriving the hashes, storing page metadata) rests on GDPR Art. 6(1)(f)
432
+ legitimate interest, which carries Art. 13 transparency duties. Not needing
433
+ a banner is not the same as not needing a disclosure.
434
+ - **A DPA may still apply.** nosework is self-hosted, but whoever hosts your
435
+ app and your database (Vercel and Neon, in the common setup) processes that
436
+ data on your behalf and is a sub-processor like any other.
437
+ - **No cross-site identifier.** The visitor hash is scoped to the site ID, so
438
+ running several apps against one database does not produce a value that
439
+ links a person's activity between them. See [No Cookies](#no-cookies) above
440
+ for the two limits on that — it stops a join, not a determined operator.
441
+
442
+ `docs/PRIVACY.md` in the source repository documents every stored field, the
443
+ 7-day salt lifetime the anonymity argument depends on, the retention jobs your
444
+ deployment has to schedule itself, and a paragraph you can adapt for a privacy
445
+ page.
446
+
447
+ *This is a summary of how the software behaves, not legal advice. Consult a
448
+ legal professional for your specific situation.*
433
449
 
434
450
  ---
435
451
 
@@ -495,31 +511,37 @@ interface PaginatedQueryOptions extends QueryOptions {
495
511
  | `getReferrers(options)` | `[{ referrer, pageViews, visitors }]` |
496
512
  | `getDevices(options)` | `[{ device, browser, os, pageViews, visitors }]` |
497
513
  | `getTimeSeries(options)` | `[{ date, pageViews, visitors }]` |
498
- | `listSites()` | `[{ id, name, domain, createdAt }]` |
499
- | `getOrCreateSite(domain, name?)` | `{ id, name, domain }` |
500
514
 
501
515
  ### Utility Functions
502
516
 
503
517
  | Function | Description |
504
518
  |----------|-------------|
505
- | `getClient()` | Get the Prisma client for custom queries |
519
+ | `getClient()` | Get the Drizzle client for custom queries |
506
520
  | `disconnect()` | Disconnect from the database |
507
521
  | `isBot(userAgent)` | Check if a user-agent is a bot |
508
522
  | `parseUserAgent(ua)` | Parse a user-agent string |
509
523
  | `cleanupOldSalts()` | Remove daily salts older than 7 days |
524
+ | `listSites()` | List every site that has reported a page view, derived from page-view data (there is no sites table) |
525
+ | `deleteOldPageViews(olderThan, siteId?)` | Delete page views older than a cutoff; omit `siteId` to sweep all sites |
510
526
 
511
527
  ---
512
528
 
513
529
  ## Database Schema
514
530
 
515
- The package uses these tables (auto-created via Prisma migrations):
531
+ The package uses these tables (created by this repo's own Drizzle migrations in `drizzle/`, generated with `bun run db:generate` and applied with `bun run db:migrate`):
532
+
533
+ - **page_views** - Individual page view events with location, device info
534
+ - **events** - Custom events with properties
535
+ - **daily_salts** - Rotating salts for visitor hashing (privacy)
536
+ - **analytics_errors** - Individual error occurrences
537
+ - **error_groups** - Aggregated errors by fingerprint
516
538
 
517
- - **Site** - Your tracked sites/apps
518
- - **PageView** - Individual page view events
519
- - **Event** - Custom events
520
- - **DailySalt** - Rotating salts for visitor hashing (privacy)
539
+ See `src/schema.ts` for the Drizzle schema definitions.
521
540
 
522
- See `prisma/schema.prisma` for the full schema.
541
+ `bun run db:migrate` assumes an empty database. If these tables already exist,
542
+ migration 0000 fails with `relation "page_views" already exists` — do not drop
543
+ and recreate them; mark the migration as applied instead. The catch-up
544
+ procedure is in `docs/ARCHITECTURE.md` in the source repository.
523
545
 
524
546
  ---
525
547
 
package/dist/client.d.ts CHANGED
@@ -1,7 +1,13 @@
1
- import { PrismaClient } from "@prisma/client";
1
+ import { drizzle } from "drizzle-orm/postgres-js";
2
+ import postgres from "postgres";
3
+ import * as schema from "./schema.js";
2
4
  declare global {
3
- var __noseworkPrisma: PrismaClient | undefined;
5
+ var __noseworkDb: ReturnType<typeof drizzle<typeof schema>> | undefined;
6
+ var __noseworkSql: ReturnType<typeof postgres> | undefined;
4
7
  }
5
- export declare function getClient(): PrismaClient;
8
+ export declare function getClient(): import("drizzle-orm/postgres-js").PostgresJsDatabase<typeof schema> & {
9
+ $client: postgres.Sql<{}>;
10
+ };
6
11
  export declare function disconnect(): Promise<void>;
12
+ export { schema };
7
13
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,gBAAgB,EAAE,YAAY,GAAG,SAAS,CAAC;CAChD;AAID,wBAAgB,SAAS,IAAI,YAAY,CAoBxC;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhD"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,YAAY,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAExE,IAAI,aAAa,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,GAAG,SAAS,CAAC;CAC5D;AAKD,wBAAgB,SAAS;;EAwBxB;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAMhD;AAGD,OAAO,EAAE,MAAM,EAAE,CAAC"}
package/dist/client.js CHANGED
@@ -1,29 +1,38 @@
1
- import { PrismaClient } from "@prisma/client";
2
- let prismaClient = null;
1
+ import { drizzle } from "drizzle-orm/postgres-js";
2
+ import postgres from "postgres";
3
+ import * as schema from "./schema.js";
4
+ let db = null;
5
+ let sql = null;
3
6
  export function getClient() {
4
- if (prismaClient) {
5
- return prismaClient;
7
+ if (db) {
8
+ return db;
6
9
  }
7
- // In development, use global to preserve client across hot reloads
10
+ const connectionString = process.env.ANALYTICS_DATABASE_URL;
11
+ if (!connectionString) {
12
+ throw new Error("ANALYTICS_DATABASE_URL environment variable is not set");
13
+ }
14
+ // In development, use global to preserve connection across hot reloads
8
15
  if (process.env.NODE_ENV === "development") {
9
- if (!global.__noseworkPrisma) {
10
- global.__noseworkPrisma = new PrismaClient({
11
- datasourceUrl: process.env.ANALYTICS_DATABASE_URL,
12
- });
16
+ if (!global.__noseworkSql) {
17
+ global.__noseworkSql = postgres(connectionString);
18
+ global.__noseworkDb = drizzle(global.__noseworkSql, { schema });
13
19
  }
14
- prismaClient = global.__noseworkPrisma;
20
+ sql = global.__noseworkSql;
21
+ db = global.__noseworkDb;
15
22
  }
16
23
  else {
17
- prismaClient = new PrismaClient({
18
- datasourceUrl: process.env.ANALYTICS_DATABASE_URL,
19
- });
24
+ sql = postgres(connectionString);
25
+ db = drizzle(sql, { schema });
20
26
  }
21
- return prismaClient;
27
+ return db;
22
28
  }
23
29
  export async function disconnect() {
24
- if (prismaClient) {
25
- await prismaClient.$disconnect();
26
- prismaClient = null;
30
+ if (sql) {
31
+ await sql.end();
32
+ sql = null;
33
+ db = null;
27
34
  }
28
35
  }
36
+ // Re-export schema for convenience
37
+ export { schema };
29
38
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C,IAAI,YAAY,GAAwB,IAAI,CAAC;AAE7C,MAAM,UAAU,SAAS;IACvB,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,mEAAmE;IACnE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC7B,MAAM,CAAC,gBAAgB,GAAG,IAAI,YAAY,CAAC;gBACzC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;aAClD,CAAC,CAAC;QACL,CAAC;QACD,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,YAAY,GAAG,IAAI,YAAY,CAAC;YAC9B,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;SAClD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC;QACjC,YAAY,GAAG,IAAI,CAAC;IACtB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAStC,IAAI,EAAE,GAAqD,IAAI,CAAC;AAChE,IAAI,GAAG,GAAuC,IAAI,CAAC;AAEnD,MAAM,UAAU,SAAS;IACvB,IAAI,EAAE,EAAE,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;IAC5D,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,uEAAuE;IACvE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,CAAC,aAAa,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;YAClD,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;QAC3B,EAAE,GAAG,MAAM,CAAC,YAAa,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACjC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC;QAChB,GAAG,GAAG,IAAI,CAAC;QACX,EAAE,GAAG,IAAI,CAAC;IACZ,CAAC;AACH,CAAC;AAED,mCAAmC;AACnC,OAAO,EAAE,MAAM,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAgDpB;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD1E;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,IAAI,CAAA;CAAE,GAC7C,OAAO,CAAC,UAAU,CAAC,CA2CrB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,OAAO,CAAC,cAAc,EAAE,CAAC,CAyB3B;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,aAAa,EAAE,CAAC,CA4B1B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,GACd,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAwC3D"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EACjB,MAAM,YAAY,CAAC;AA8CpB;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD1E;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,IAAI,CAAA;CAAE,GAC7C,OAAO,CAAC,UAAU,CAAC,CA4CrB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,OAAO,CAAC,cAAc,EAAE,CAAC,CA4B3B;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,aAAa,EAAE,CAAC,CA6B1B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,GACd,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA0C3D"}