1/**
2 * Typed IndexedDB schema version definitions.
3 *
4 * This module represents one logical IndexedDB database version as a non-empty set of `IndexedDbTable` definitions.
5 * Versions are consumed by `IndexedDbDatabase.make` and `.add` to type query builders and migration transactions, so
6 * applications can describe the tables available after initialization or after each schema upgrade.
7 *
8 * Use an `IndexedDbVersion` when defining the initial stores for a browser database, adding or removing object stores,
9 * changing indexes, or moving data between differently shaped table schemas. The version value is a typed description of
10 * the target schema; creating and deleting object stores or indexes still happens explicitly inside the corresponding
11 * `IndexedDbDatabase` migration callback.
12 *
13 * IndexedDB versioning is ordered by the migration chain rather than by a number stored here. Each `.add` step becomes
14 * the next browser database version, and only migrations after the browser's current version are run. Include every table
15 * that should be queryable in each target version, avoid duplicate table names, and remember that key-path or
16 * auto-increment changes usually require creating a new object store and copying data during the upgrade transaction.
17 *
18 * @since 4.0.0
19 */
20import type { NonEmptyReadonlyArray } from "effect/Array"
21import type { Pipeable } from "effect/Pipeable"
22import { pipeArguments } from "effect/Pipeable"
23import type * as IndexedDbTable from "./IndexedDbTable.ts"
24
25const TypeId = "~@effect/platform-browser/IndexedDbVersion"
26
27/**
28 * Typed IndexedDB version definition containing the tables available in that schema version.
29 *
30 * @category models
31 * @since 4.0.0
32 */
33export interface IndexedDbVersion<
34 out Tables extends IndexedDbTable.AnyWithProps
35> extends Pipeable {
36 new(_: never): {}
37 readonly [TypeId]: typeof TypeId
38 readonly tables: ReadonlyMap<string, Tables>
39}
40
41/**
42 * Type-erased shape of an `IndexedDbVersion`.
43 *
44 * @category models
45 * @since 4.0.0
46 */
47export interface Any {
48 readonly [TypeId]: typeof TypeId
49}
50
51/**
52 * Type-erased `IndexedDbVersion` retaining version properties with broad table types.
53 *
54 * @category models
55 * @since 4.0.0
56 */
57export type AnyWithProps = IndexedDbVersion<IndexedDbTable.AnyWithProps>
58
59/**
60 * Extracts the table union from an `IndexedDbVersion`.
61 *
62 * @category utility types
63 * @since 4.0.0
64 */
65export type Tables<Db extends Any> = Db extends IndexedDbVersion<infer _Tables> ? _Tables : never
66
67/**
68 * Selects a table by name from an `IndexedDbVersion`.
69 *
70 * @category utility types
71 * @since 4.0.0
72 */
73export type TableWithName<
74 Db extends Any,
75 TableName extends string
76> = IndexedDbTable.WithName<Tables<Db>, TableName>
77
78/**
79 * Extracts the schema for a named table within an `IndexedDbVersion`.
80 *
81 * @category utility types
82 * @since 4.0.0
83 */
84export type SchemaWithName<
85 Db extends Any,
86 TableName extends string
87> = IndexedDbTable.TableSchema<IndexedDbTable.WithName<Tables<Db>, TableName>>
88
89const Proto = {
90 [TypeId]: TypeId,
91 pipe() {
92 return pipeArguments(this, arguments)
93 }
94}
95
96const makeProto = <Tables extends IndexedDbTable.AnyWithProps>(options: {
97 readonly tables: ReadonlyMap<string, Tables>
98}): IndexedDbVersion<Tables> => {
99 // oxlint-disable-next-line typescript/no-extraneous-class
100 class Version {}
101 Object.assign(Version, Proto)
102 ;(Version as any).tables = options.tables
103 return Version as any
104}
105
106/**
107 * Creates an `IndexedDbVersion` from one or more table definitions.
108 *
109 * **When to use**
110 *
111 * Use when you need a typed description of the target IndexedDB schema that a
112 * database migration will materialize.
113 *
114 * **Details**
115 *
116 * The returned version exposes a `tables` map keyed by each table's
117 * `tableName`, and its type is the union of the supplied table definitions.
118 *
119 * **Gotchas**
120 *
121 * This constructor only describes the target schema; object stores and indexes
122 * still need to be created in the corresponding `IndexedDbDatabase` migration.
123 * Duplicate table names are not rejected, and the runtime map keeps the later
124 * table for a repeated key.
125 *
126 * @see {@link IndexedDbTable.make} for creating table definitions consumed by this constructor
127 *
128 * @category constructors
129 * @since 4.0.0
130 */
131export const make = <
132 const Tables extends NonEmptyReadonlyArray<IndexedDbTable.AnyWithProps>
133>(
134 ...tables: Tables
135): IndexedDbVersion<Tables[number]> =>
136 makeProto({
137 tables: new Map(tables.map((table) => [table.tableName, table]))
138 })