sveltekit-admin 0.5.0 → 0.6.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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -1
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +1 -0
  5. package/dist/server/adapters/drizzle/dataAdapter.d.ts +11 -0
  6. package/dist/server/adapters/drizzle/dataAdapter.js +243 -0
  7. package/dist/server/adapters/drizzle/filterCompiler.d.ts +8 -0
  8. package/dist/server/adapters/drizzle/filterCompiler.js +61 -0
  9. package/dist/server/adapters/drizzle/index.d.ts +13 -0
  10. package/dist/server/adapters/drizzle/index.js +23 -0
  11. package/dist/server/adapters/drizzle/inspect.d.ts +23 -0
  12. package/dist/server/adapters/drizzle/inspect.js +220 -0
  13. package/dist/server/adapters/drizzle/introspector.d.ts +3 -0
  14. package/dist/server/adapters/drizzle/introspector.js +3 -0
  15. package/dist/server/adapters/filter.d.ts +12 -0
  16. package/dist/server/adapters/filter.js +72 -0
  17. package/dist/server/adapters/prisma/dataAdapter.d.ts +13 -0
  18. package/dist/server/adapters/prisma/dataAdapter.js +96 -0
  19. package/dist/server/adapters/prisma/filterCompiler.d.ts +9 -0
  20. package/dist/server/adapters/prisma/filterCompiler.js +42 -0
  21. package/dist/server/adapters/prisma/index.d.ts +28 -0
  22. package/dist/server/adapters/prisma/index.js +32 -0
  23. package/dist/server/adapters/prisma/introspector.d.ts +4 -0
  24. package/dist/server/adapters/prisma/introspector.js +9 -0
  25. package/dist/server/adapters/types.d.ts +79 -0
  26. package/dist/server/adapters/types.js +1 -0
  27. package/dist/server/data.d.ts +0 -8
  28. package/dist/server/data.js +0 -41
  29. package/dist/server/handler.d.ts +18 -2
  30. package/dist/server/handler.js +170 -134
  31. package/dist/server/introspection/parser.d.ts +4 -40
  32. package/dist/server/introspection/relations.d.ts +1 -1
  33. package/dist/server/introspection/relations.js +1 -1
  34. package/dist/server/query/listQuery.d.ts +10 -13
  35. package/dist/server/query/listQuery.js +47 -55
  36. package/dist/server/types/schema.d.ts +40 -0
  37. package/dist/server/types/schema.js +8 -0
  38. package/dist/server/views/Form.svelte +1 -1
  39. package/dist/server/views/RelationCheckboxes.svelte +1 -1
  40. package/dist/server/views/theme.js +72 -19
  41. package/package.json +24 -3
@@ -2,46 +2,10 @@
2
2
  * Prisma Schema Parser
3
3
  * Extracts model information from Prisma schema files
4
4
  */
5
- export interface PrismaField {
6
- name: string;
7
- type: string;
8
- isRequired: boolean;
9
- isList: boolean;
10
- isUnique: boolean;
11
- isId: boolean;
12
- isUpdatedAt: boolean;
13
- isCreatedAt: boolean;
14
- hasDefault: boolean;
15
- defaultValue?: string;
16
- /** true si `type` correspond à un `enum` déclaré dans le même schéma. */
17
- isEnum?: boolean;
18
- relation?: {
19
- name?: string;
20
- model: string;
21
- fields?: string[];
22
- references?: string[];
23
- };
24
- documentation?: string;
25
- }
26
- export interface PrismaModel {
27
- name: string;
28
- fields: PrismaField[];
29
- documentation?: string;
30
- primaryKey?: string;
31
- isPivotTable?: boolean;
32
- }
33
- export interface PrismaSchema {
34
- models: PrismaModel[];
35
- enums: Map<string, string[]>;
36
- /**
37
- * Provider du bloc `datasource` (ex. "postgresql", "sqlite"), tel qu'écrit
38
- * littéralement dans le schéma. `undefined` si absent ou si la valeur est
39
- * une expression (`env("...")`, un provider non littéral) — dans ce cas
40
- * le code appelant doit dégrader vers le comportement le plus prudent
41
- * (voir `caseInsensitiveSearch` dans query/listQuery.ts).
42
- */
43
- provider?: string;
44
- }
5
+ import type { Field, Model, Schema } from '../types/schema.js';
6
+ export type PrismaField = Field;
7
+ export type PrismaModel = Model;
8
+ export type PrismaSchema = Schema;
45
9
  export declare function parsePrismaSchema(schemaPath: string): PrismaSchema;
46
10
  export declare function parseSchemaContent(content: string): PrismaSchema;
47
11
  /**
@@ -13,7 +13,7 @@
13
13
  * par nom de relation, que Prisma garantit unique pour un couple de modèles.
14
14
  */
15
15
  import type { PrismaModel, PrismaSchema } from './parser.js';
16
- export type RelationKind = 'to-one-owning' | 'to-one-inverse' | 'to-many-inverse' | 'm2m-implicit';
16
+ export type RelationKind = 'to-one-owning' | 'to-one-inverse' | 'to-many-inverse' | 'm2m';
17
17
  export type UnsupportedReason = 'composite-fk' | 'ambiguous';
18
18
  export interface RelationEdge {
19
19
  /** Modèle porteur du champ */
@@ -74,7 +74,7 @@ export function buildRelationGraph(schema) {
74
74
  for (const c of group) {
75
75
  let kind;
76
76
  if (isImplicitM2M) {
77
- kind = 'm2m-implicit';
77
+ kind = 'm2m';
78
78
  }
79
79
  else if (c.field.isList) {
80
80
  kind = 'to-many-inverse';
@@ -12,6 +12,7 @@
12
12
  * string) becomes the Prisma operator key.
13
13
  */
14
14
  import type { PrismaModel } from '../introspection/parser.js';
15
+ import type { Filter } from '../adapters/types.js';
15
16
  export type FilterOp = 'equals' | 'contains' | 'startsWith' | 'gte' | 'lte' | 'isnull';
16
17
  export interface ActiveFilter {
17
18
  field: string;
@@ -32,7 +33,10 @@ export interface ListQuery {
32
33
  filters: ActiveFilter[];
33
34
  ignored: IgnoredFilter[];
34
35
  }
35
- /** Field-name candidates used both for relation labels and for the default search heuristic (§2.1). */
36
+ /**
37
+ * Field-name candidates for the default search heuristic (§2.1). Relation-label
38
+ * resolution (handler.ts) keeps its own separate list — the two are not shared.
39
+ */
36
40
  export declare const DEFAULT_LABEL_FIELDS: string[];
37
41
  /**
38
42
  * Fields eligible for the free-text search box.
@@ -73,17 +77,10 @@ export declare function resolveDateShortcut(raw: string, now?: () => Date): Date
73
77
  * is known to be eligible.
74
78
  */
75
79
  export declare function parseListQuery(searchParams: URLSearchParams, model: PrismaModel, enums: Map<string, string[]>, searchFields: string[], filterableFields: Set<string>, now?: () => Date): ListQuery;
76
- /** A Prisma `where` clause built from a `ListQuery`. Opaque to callers — pass straight to Prisma. */
77
- export type PrismaWhere = Record<string, unknown>;
78
80
  /**
79
- * Compose the final Prisma `where`: `AND: [scope, ...filters, {OR: search}]`.
80
- * NEVER a spread a spread of `{...scope, ...filterWhere}` lets a filter
81
- * on the same field as the developer's scoping silently overwrite it
82
- * (docs/design §0.c, the exact IDOR the previous `?filter=` had). Two
83
- * clauses on the same field inside `AND` intersect; they never merge.
84
- *
85
- * Returns `undefined` when nothing is active, so the query shape sent to
86
- * Prisma is byte-for-byte identical to today's unfiltered call — no
87
- * regression on existing snapshots/assertions.
81
+ * Compose the final generic `Filter`: `and: [scope, ...filters, {or: search}]`.
82
+ * NEVER a spread. Flat `{ tenantId: 1 }` scopes become `eq` leaves via
83
+ * `normalizeScope`; nested Prisma where objects stay opaque for the Prisma
84
+ * compiler. Drizzle's compiler throws on those opaques.
88
85
  */
89
- export declare function buildWhere(query: ListQuery, scope: Record<string, unknown> | undefined, caseInsensitiveSearch: boolean, model: PrismaModel): PrismaWhere | undefined;
86
+ export declare function buildWhere(query: ListQuery, scope: Record<string, unknown> | undefined, caseInsensitiveSearch: boolean, model: PrismaModel): Filter | Record<string, unknown> | undefined;
@@ -12,10 +12,25 @@
12
12
  * string) becomes the Prisma operator key.
13
13
  */
14
14
  import { isSensitiveFieldName } from '../introspection/parser.js';
15
+ import { normalizeScope } from '../adapters/filter.js';
15
16
  /** Max length accepted for the free-text search term. Longer input is truncated. */
16
17
  const MAX_SEARCH_LENGTH = 200;
17
- /** Field-name candidates used both for relation labels and for the default search heuristic (§2.1). */
18
- export const DEFAULT_LABEL_FIELDS = ['name', 'title', 'label', 'email', 'username', 'slug'];
18
+ /**
19
+ * Field-name candidates for the default search heuristic (§2.1). Relation-label
20
+ * resolution (handler.ts) keeps its own separate list — the two are not shared.
21
+ */
22
+ export const DEFAULT_LABEL_FIELDS = [
23
+ 'name',
24
+ 'title',
25
+ 'label',
26
+ 'email',
27
+ 'username',
28
+ 'slug',
29
+ 'description',
30
+ 'content',
31
+ 'body',
32
+ 'text'
33
+ ];
19
34
  /** Types eligible for the free-text search heuristic (String only, see §2.1). */
20
35
  function isSearchableByHeuristic(field) {
21
36
  return (field.type === 'String' &&
@@ -337,92 +352,69 @@ export function parseListQuery(searchParams, model, enums, searchFields, filtera
337
352
  }
338
353
  function clauseOf(filter) {
339
354
  if (filter.op === 'gte' && filter.value && typeof filter.value === 'object' && 'gte' in filter.value) {
340
- // Date shortcut carrying both bounds (see parseOneFilter's DateTime branch).
355
+ // Date shortcut carrying both bounds (see parseOneFilter's DateTime branch)
356
+ // becomes two leaf clauses, re-merged by the Prisma filterCompiler.
341
357
  const range = filter.value;
342
- return { [filter.field]: { gte: range.gte, lt: range.lt } };
358
+ return [
359
+ { op: 'gte', field: filter.field, value: range.gte },
360
+ { op: 'lt', field: filter.field, value: range.lt }
361
+ ];
343
362
  }
344
363
  if (filter.op === 'isnull') {
345
- return { [filter.field]: filter.value ? { equals: null } : { not: null } };
364
+ return [{ op: filter.value ? 'isNull' : 'isNotNull', field: filter.field }];
346
365
  }
347
366
  if (filter.op === 'equals') {
348
- return { [filter.field]: filter.value };
367
+ return [{ op: 'eq', field: filter.field, value: filter.value }];
349
368
  }
350
- return { [filter.field]: { [filter.op]: filter.value } };
369
+ return [{ op: filter.op, field: filter.field, value: filter.value }];
351
370
  }
352
371
  /**
353
- * Compose the final Prisma `where`: `AND: [scope, ...filters, {OR: search}]`.
354
- * NEVER a spread a spread of `{...scope, ...filterWhere}` lets a filter
355
- * on the same field as the developer's scoping silently overwrite it
356
- * (docs/design §0.c, the exact IDOR the previous `?filter=` had). Two
357
- * clauses on the same field inside `AND` intersect; they never merge.
358
- *
359
- * Returns `undefined` when nothing is active, so the query shape sent to
360
- * Prisma is byte-for-byte identical to today's unfiltered call — no
361
- * regression on existing snapshots/assertions.
372
+ * Compose the final generic `Filter`: `and: [scope, ...filters, {or: search}]`.
373
+ * NEVER a spread. Flat `{ tenantId: 1 }` scopes become `eq` leaves via
374
+ * `normalizeScope`; nested Prisma where objects stay opaque for the Prisma
375
+ * compiler. Drizzle's compiler throws on those opaques.
362
376
  */
363
377
  export function buildWhere(query, scope, caseInsensitiveSearch, model) {
364
378
  const and = [];
365
- if (scope)
366
- and.push(scope);
379
+ const normalized = normalizeScope(scope);
380
+ if (normalized)
381
+ and.push(normalized);
367
382
  for (const f of query.filters)
368
- and.push(clauseOf(f));
383
+ and.push(...clauseOf(f));
369
384
  if (query.q && query.searchFields.length > 0) {
370
385
  const or = [];
371
386
  for (const fieldName of query.searchFields) {
372
387
  const field = model.fields.find((f) => f.name === fieldName);
373
- const clause = searchClauseFor(field, query.q, caseInsensitiveSearch);
388
+ const clause = searchClauseFor(field, fieldName, query.q);
374
389
  if (clause)
375
- or.push({ [fieldName]: clause });
390
+ or.push(clause);
376
391
  }
377
- // Never emit `{OR: []}` — in Prisma that matches nothing, which would
378
- // silently turn "no searchable field" (or "every clause omitted", §2.4)
379
- // into "empty result". A no-op search must add nothing to the where.
380
392
  if (or.length > 0)
381
- and.push({ OR: or });
393
+ and.push({ op: 'or', clauses: or });
382
394
  }
383
395
  if (and.length === 0)
384
396
  return undefined;
385
397
  if (and.length === 1)
386
398
  return and[0];
387
- return { AND: and };
399
+ return { op: 'and', clauses: and };
388
400
  }
389
401
  /**
390
- * The per-field-type clause for a `searchFields` entry (§2.4):
391
- * - String @id -> `equals` (a `contains` on a cuid/uuid can't use the
392
- * index and never makes semantic sense; §2.1 talks ONLY about the id
393
- * here an earlier version of this function over-generalized to
394
- * `@id || @unique`, which silently broke fragment search on the most
395
- * common real-world case: `email`/`slug` fields are `@unique` in
396
- * nearly every Prisma schema and are exactly what §2.3's "a title, an
397
- * email" example means by free-text search. `@unique` alone is NOT a
398
- * reason to switch to `equals` — only `@id` is).
399
- * - other String (including @unique) -> `contains` (+ `mode:
400
- * 'insensitive'` when the provider supports it).
401
- * - Int/BigInt/Float/Decimal -> `equals` if `q` coerces to that type,
402
- * otherwise the clause is OMITTED — never `contains` on a numeric
403
- * column, which Prisma rejects with a hard error (`Unknown argument
404
- * contains`), turning any legitimate `?q=` into a 500 (§10's known
405
- * trap, discovered via review — the original implementation searched
406
- * this exactly wrong).
407
- * - anything else (enum, Boolean, DateTime, relation, Json/Bytes):
408
- * omitted. `resolveSearchFields`'s auto heuristic never proposes these,
409
- * but explicit `searchFields` config isn't type-checked against §2.4 at
410
- * boot (only against `isFilterableFieldType`), so this is reached in
411
- * practice for a misconfigured field — degrading to "omitted" here
412
- * keeps the guarantee that a legitimate URL never 500s, without adding
413
- * a boot-time validation pass this design doc doesn't ask for.
402
+ * The per-field-type clause for a `searchFields` entry (§2.4) — same rules as
403
+ * before (String @id -> eq, other String -> contains, numeric -> eq if
404
+ * coercible else omitted, anything else omitted). `caseInsensitiveSearch` no
405
+ * longer lives here: the generic `Filter` doesn't carry a case-sensitivity
406
+ * flag, `filterCompiler.ts` decides whether to add `mode: 'insensitive'` from
407
+ * the same boolean at the handler.ts call site instead.
414
408
  */
415
- function searchClauseFor(field, q, caseInsensitiveSearch) {
409
+ function searchClauseFor(field, fieldName, q) {
416
410
  if (!field)
417
411
  return undefined;
418
412
  if (field.type === 'String') {
419
- if (field.isId)
420
- return { equals: q };
421
- return caseInsensitiveSearch ? { contains: q, mode: 'insensitive' } : { contains: q };
413
+ return field.isId ? { op: 'eq', field: fieldName, value: q } : { op: 'contains', field: fieldName, value: q };
422
414
  }
423
415
  if (['Int', 'BigInt', 'Float', 'Decimal'].includes(field.type)) {
424
416
  const coerced = coerceValue(field, 'equals', q);
425
- return coerced === undefined ? undefined : { equals: coerced };
417
+ return coerced === undefined ? undefined : { op: 'eq', field: fieldName, value: coerced };
426
418
  }
427
419
  return undefined;
428
420
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Generic schema shapes shared by every schema-source adapter (Prisma today,
3
+ * others later). Deliberately identical in shape to what `introspection/
4
+ * parser.ts` has always produced — this file is a rename of that shape, not
5
+ * a redesign of it. `PrismaSchema`/`PrismaModel`/`PrismaField` in parser.ts
6
+ * become aliases of these.
7
+ */
8
+ export interface Field {
9
+ name: string;
10
+ type: string;
11
+ isRequired: boolean;
12
+ isList: boolean;
13
+ isUnique: boolean;
14
+ isId: boolean;
15
+ isUpdatedAt: boolean;
16
+ isCreatedAt: boolean;
17
+ hasDefault: boolean;
18
+ defaultValue?: string;
19
+ /** true si `type` correspond à un `enum` déclaré dans le même schéma. */
20
+ isEnum?: boolean;
21
+ relation?: {
22
+ name?: string;
23
+ model: string;
24
+ fields?: string[];
25
+ references?: string[];
26
+ };
27
+ documentation?: string;
28
+ }
29
+ export interface Model {
30
+ name: string;
31
+ fields: Field[];
32
+ documentation?: string;
33
+ primaryKey?: string;
34
+ isPivotTable?: boolean;
35
+ }
36
+ export interface Schema {
37
+ models: Model[];
38
+ enums: Map<string, string[]>;
39
+ provider?: string;
40
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generic schema shapes shared by every schema-source adapter (Prisma today,
3
+ * others later). Deliberately identical in shape to what `introspection/
4
+ * parser.ts` has always produced — this file is a rename of that shape, not
5
+ * a redesign of it. `PrismaSchema`/`PrismaModel`/`PrismaField` in parser.ts
6
+ * become aliases of these.
7
+ */
8
+ export {};
@@ -69,7 +69,7 @@
69
69
  ? [...model.relationGraph.edges.values()].filter(
70
70
  (e) =>
71
71
  e.model === model.name &&
72
- e.kind === 'm2m-implicit' &&
72
+ e.kind === 'm2m' &&
73
73
  !e.unsupported &&
74
74
  !hidden.includes(e.field) &&
75
75
  model.relationOptions?.has(`${e.model}.${e.field}`)
@@ -17,7 +17,7 @@
17
17
  </script>
18
18
 
19
19
  <!--
20
- Fieldset de checkboxes pour une arête m2m-implicite.
20
+ Fieldset de checkboxes pour une arête m2m.
21
21
 
22
22
  Nommage `__rel__<field>` pour les valeurs cochées et un hidden sentinelle
23
23
  `__rel_present__<field>` toujours émis : en HTML, zéro checkbox cochée
@@ -392,61 +392,114 @@ export function styles(primaryColor) {
392
392
  .ska-filters {
393
393
  margin-bottom: 1.5rem;
394
394
  display: flex;
395
- flex-wrap: wrap;
396
- gap: 1.5rem;
395
+ flex-direction: column;
396
+ gap: 0.75rem;
397
397
  }
398
398
 
399
399
  .ska-filters__group {
400
- min-width: 140px;
400
+ display: flex;
401
+ align-items: center;
402
+ flex-wrap: wrap;
403
+ gap: 0.625rem;
404
+ }
405
+
406
+ .ska-filters__group + .ska-filters__group {
407
+ padding-top: 0.75rem;
408
+ border-top: 1px solid #f1f5f9;
401
409
  }
402
410
 
403
411
  .ska-filters__title {
404
- font-size: 0.75rem;
412
+ font-size: 0.6875rem;
405
413
  font-weight: 600;
406
414
  text-transform: uppercase;
407
415
  letter-spacing: 0.05em;
408
- color: #64748b;
409
- margin-bottom: 0.5rem;
416
+ color: #94a3b8;
417
+ min-width: 88px;
418
+ flex-shrink: 0;
410
419
  }
411
420
 
412
421
  .ska-filters__list {
413
422
  list-style: none;
414
423
  display: flex;
415
- flex-direction: column;
416
- gap: 0.25rem;
424
+ flex-wrap: wrap;
425
+ gap: 0.375rem;
417
426
  }
418
427
 
419
428
  .ska-filters__link {
420
- display: block;
421
- padding: 0.25rem 0.5rem;
422
- border-radius: 0.25rem;
429
+ display: inline-flex;
430
+ align-items: center;
431
+ padding: 0.25rem 0.625rem;
432
+ border-radius: 999px;
433
+ border: 1px solid #e2e8f0;
423
434
  color: #475569;
424
435
  text-decoration: none;
425
- font-size: 0.875rem;
436
+ font-size: 0.8125rem;
437
+ line-height: 1.25rem;
438
+ white-space: nowrap;
439
+ transition: all 0.15s;
426
440
  }
427
441
 
428
442
  .ska-filters__link:hover {
429
443
  background: #f1f5f9;
444
+ border-color: #cbd5e1;
430
445
  }
431
446
 
432
447
  .ska-filters__link--active {
433
- background: #eef2ff;
434
- color: var(--ska-primary);
448
+ background: var(--ska-primary);
449
+ border-color: var(--ska-primary);
450
+ color: white;
435
451
  font-weight: 500;
436
452
  }
437
453
 
438
- .ska-filters__range {
454
+ .ska-filters__range,
455
+ .ska-filters__select {
439
456
  display: flex;
440
- flex-direction: column;
441
- gap: 0.375rem;
457
+ align-items: center;
458
+ gap: 0.5rem;
442
459
  }
443
460
 
444
461
  .ska-filters__range-input {
445
- padding: 0.375rem 0.5rem;
462
+ padding: 0.25rem 0.5rem;
446
463
  border: 1px solid #e2e8f0;
447
464
  border-radius: 0.25rem;
448
465
  font-size: 0.8125rem;
449
- width: 100%;
466
+ width: 6.5rem;
467
+ }
468
+
469
+ .ska-filters__select-input {
470
+ padding: 0.25rem 0.625rem;
471
+ border: 1px solid #e2e8f0;
472
+ border-radius: 0.25rem;
473
+ font-size: 0.8125rem;
474
+ color: #475569;
475
+ background: white;
476
+ max-width: 220px;
477
+ }
478
+
479
+ .ska-filters__chip {
480
+ display: inline-flex;
481
+ align-items: center;
482
+ gap: 0.375rem;
483
+ padding: 0.25rem 0.5rem 0.25rem 0.625rem;
484
+ border-radius: 999px;
485
+ background: #eef2ff;
486
+ color: var(--ska-primary);
487
+ font-size: 0.8125rem;
488
+ }
489
+
490
+ .ska-filters__chip a {
491
+ color: inherit;
492
+ text-decoration: none;
493
+ }
494
+
495
+ .ska-filters__chip-clear {
496
+ color: #64748b;
497
+ text-decoration: none;
498
+ line-height: 1;
499
+ }
500
+
501
+ .ska-filters__chip-clear:hover {
502
+ color: #dc2626;
450
503
  }
451
504
 
452
505
  /* Back link */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveltekit-admin",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Django-like admin panel for SvelteKit + Prisma",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",
@@ -10,6 +10,11 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "svelte": "./dist/index.js",
12
12
  "default": "./dist/index.js"
13
+ },
14
+ "./adapters/drizzle": {
15
+ "types": "./dist/server/adapters/drizzle/index.d.ts",
16
+ "svelte": "./dist/server/adapters/drizzle/index.js",
17
+ "default": "./dist/server/adapters/drizzle/index.js"
13
18
  }
14
19
  },
15
20
  "files": [
@@ -30,22 +35,38 @@
30
35
  "test:watch": "svelte-kit sync && npm run test:gen && vitest",
31
36
  "test:coverage": "svelte-kit sync && npm run test:gen && vitest run --coverage",
32
37
  "lint": "svelte-kit sync && eslint .",
33
- "format": "prettier --write ."
38
+ "format": "prettier --write .",
39
+ "changeset": "changeset",
40
+ "version-packages": "changeset version",
41
+ "release": "npm run package && changeset publish"
34
42
  },
35
43
  "peerDependencies": {
36
44
  "@prisma/client": ">=5.0.0",
37
45
  "@sveltejs/kit": ">=2.0.0",
46
+ "drizzle-orm": ">=0.32.0",
38
47
  "svelte": ">=5.0.0"
39
48
  },
49
+ "peerDependenciesMeta": {
50
+ "@prisma/client": {
51
+ "optional": true
52
+ },
53
+ "drizzle-orm": {
54
+ "optional": true
55
+ }
56
+ },
40
57
  "devDependencies": {
58
+ "@changesets/cli": "^3.0.0",
41
59
  "@eslint/js": "9",
42
60
  "@prisma/client": "^6.19.3",
43
61
  "@sveltejs/adapter-auto": "^7.0.1",
44
62
  "@sveltejs/kit": "^2.0.0",
45
63
  "@sveltejs/package": "^2.5.8",
46
64
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
65
+ "@types/better-sqlite3": "^9.6.0",
47
66
  "@types/node": "^22.0.0",
48
67
  "@vitest/coverage-v8": "^3.2.7",
68
+ "better-sqlite3": "12.9.0",
69
+ "drizzle-orm": "^0.45.2",
49
70
  "eslint": "9",
50
71
  "eslint-config-prettier": "^10.1.8",
51
72
  "eslint-plugin-svelte": "^3.22.0",
@@ -71,7 +92,7 @@
71
92
  "license": "MIT",
72
93
  "repository": {
73
94
  "type": "git",
74
- "url": "https://github.com/dotNacer/sveltekit-admin.git"
95
+ "url": "git+https://github.com/dotNacer/sveltekit-admin.git"
75
96
  },
76
97
  "homepage": "https://github.com/dotNacer/sveltekit-admin#readme",
77
98
  "bugs": {