261 lines
7.5 KiB
TypeScript
261 lines
7.5 KiB
TypeScript
/**
|
|
* Typed object-store descriptors for the browser IndexedDB integration.
|
|
*
|
|
* An {@link IndexedDbTable} is the schema-backed description of one IndexedDB
|
|
* object store. It carries the store name, row schema, key path, index key
|
|
* paths, auto-increment mode, and transaction durability used by database
|
|
* versions, migrations, and typed queries. The `make` constructor also derives
|
|
* the read, array, and auto-increment write schemas used by the query builder.
|
|
*
|
|
* @see {@link make} for constructing table descriptors.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
import { type Pipeable, pipeArguments } from "effect/Pipeable"
|
|
import * as Schema from "effect/Schema"
|
|
import * as Struct from "effect/Struct"
|
|
import type { NoInfer } from "effect/Types"
|
|
import * as IndexedDb from "./IndexedDb.ts"
|
|
import type * as IndexedDbQueryBuilder from "./IndexedDbQueryBuilder.ts"
|
|
|
|
const TypeId = "~@effect/platform-browser/IndexedDbTable"
|
|
|
|
/**
|
|
* Typed IndexedDB table definition containing its name, schema, key path, indexes, auto-increment setting, and transaction durability.
|
|
*
|
|
* @category interface
|
|
* @since 4.0.0
|
|
*/
|
|
export interface IndexedDbTable<
|
|
out Name extends string,
|
|
out TableSchema extends AnySchemaStruct,
|
|
out Indexes extends Record<
|
|
string,
|
|
IndexedDbQueryBuilder.KeyPath<TableSchema>
|
|
>,
|
|
out KeyPath extends Readonly<IDBValidKey | undefined>,
|
|
out AutoIncrement extends boolean
|
|
> extends Pipeable {
|
|
new(_: never): {}
|
|
readonly [TypeId]: typeof TypeId
|
|
readonly tableName: Name
|
|
readonly tableSchema: TableSchema
|
|
readonly readSchema: Schema.Top
|
|
readonly autoincrementSchema: Schema.Top
|
|
readonly arraySchema: Schema.Top
|
|
readonly keyPath: KeyPath
|
|
readonly indexes: Indexes
|
|
readonly autoIncrement: AutoIncrement
|
|
readonly durability: IDBTransactionDurability
|
|
}
|
|
|
|
/**
|
|
* Schema constraint for table schemas that expose struct fields.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type AnySchemaStruct = Schema.Top & {
|
|
readonly fields: Schema.Struct.Fields
|
|
}
|
|
|
|
/**
|
|
* Type-erased shape of an `IndexedDbTable` used when table type parameters are not needed.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export interface Any {
|
|
readonly [TypeId]: typeof TypeId
|
|
readonly keyPath: any
|
|
readonly tableName: string
|
|
readonly tableSchema: Schema.Top
|
|
readonly readSchema: Schema.Top
|
|
readonly autoincrementSchema: Schema.Top
|
|
readonly arraySchema: Schema.Top
|
|
readonly autoIncrement: boolean
|
|
readonly indexes: any
|
|
}
|
|
|
|
/**
|
|
* Type-erased `IndexedDbTable` retaining the table interface properties with broad type parameters.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type AnyWithProps = IndexedDbTable<
|
|
string,
|
|
AnySchemaStruct,
|
|
any,
|
|
any,
|
|
boolean
|
|
>
|
|
|
|
/**
|
|
* Extracts the table name type from an `IndexedDbTable`.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type TableName<Table extends Any> = Table["tableName"]
|
|
/**
|
|
* Extracts the key-path type from an `IndexedDbTable`.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type KeyPath<Table extends Any> = Table["keyPath"]
|
|
|
|
/**
|
|
* Extracts the auto-increment flag type from an `IndexedDbTable`.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type AutoIncrement<Table extends Any> = Table["autoIncrement"]
|
|
|
|
/**
|
|
* Extracts the schema type from an `IndexedDbTable`.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type TableSchema<Table extends Any> = Table["tableSchema"]
|
|
/**
|
|
* Extracts the decoding or encoding service requirements needed by an `IndexedDbTable` schema.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type Context<Table extends Any> =
|
|
| Table["tableSchema"]["DecodingServices"]
|
|
| Table["tableSchema"]["EncodingServices"]
|
|
|
|
/**
|
|
* Extracts the encoded row type from an `IndexedDbTable` schema.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type Encoded<Table extends Any> = Table["tableSchema"]["Encoded"]
|
|
|
|
/**
|
|
* Extracts the index definition map from an `IndexedDbTable`.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type Indexes<Table extends Any> = Table["indexes"]
|
|
|
|
/**
|
|
* Selects the table with the given name from a union of `IndexedDbTable` types.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type WithName<Table extends Any, TableName extends string> = Extract<
|
|
Table,
|
|
{ readonly tableName: TableName }
|
|
>
|
|
|
|
const Proto = {
|
|
[TypeId]: TypeId,
|
|
pipe() {
|
|
return pipeArguments(this, arguments)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a typed IndexedDB table definition from its name, schema, optional key path, indexes, auto-increment flag, and durability.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to define a typed object-store descriptor for inclusion in an
|
|
* `IndexedDbVersion` and for migration or query APIs.
|
|
*
|
|
* **Details**
|
|
*
|
|
* `autoIncrement` defaults to `false` and `durability` defaults to `"relaxed"`.
|
|
* Tables without a key path get a read schema that includes an out-of-line
|
|
* `key`, while auto-increment tables use a write schema where the generated key
|
|
* may be omitted.
|
|
*
|
|
* **Gotchas**
|
|
*
|
|
* Tables without a key path cannot define a `key` field in their row schema.
|
|
* Key paths and index paths must point to encoded fields whose values are valid
|
|
* IndexedDB keys, and declared indexes still need to be created during
|
|
* database migrations.
|
|
*
|
|
* @see `IndexedDbVersion.make` for grouping table definitions into a schema version
|
|
*
|
|
* @category constructors
|
|
* @since 4.0.0
|
|
*/
|
|
export const make = <
|
|
const Name extends string,
|
|
TableSchema extends AnySchemaStruct,
|
|
const Indexes extends Record<
|
|
string,
|
|
IndexedDbQueryBuilder.KeyPath<TableSchema>
|
|
>,
|
|
const KeyPath extends
|
|
| (AutoIncrement extends true ? IndexedDbQueryBuilder.KeyPathNumber<NoInfer<TableSchema>>
|
|
: IndexedDbQueryBuilder.KeyPath<NoInfer<TableSchema>>)
|
|
| undefined = undefined,
|
|
const AutoIncrement extends boolean = false
|
|
>(options: {
|
|
readonly name: Name
|
|
readonly schema: [KeyPath] extends [undefined]
|
|
? "key" extends keyof TableSchema["fields"] ? "Cannot have a 'key' field when keyPath is undefined"
|
|
: TableSchema
|
|
: TableSchema
|
|
readonly keyPath?: KeyPath
|
|
readonly indexes?: Indexes | undefined
|
|
readonly autoIncrement?: IsValidAutoIncrementKeyPath<
|
|
TableSchema,
|
|
KeyPath
|
|
> extends true ? AutoIncrement | undefined
|
|
: never
|
|
readonly durability?: IDBTransactionDurability | undefined
|
|
}): IndexedDbTable<
|
|
Name,
|
|
TableSchema,
|
|
Indexes,
|
|
Extract<KeyPath, Readonly<IDBValidKey | undefined>>,
|
|
AutoIncrement
|
|
> => {
|
|
// oxlint-disable-next-line typescript/no-extraneous-class
|
|
class Table {}
|
|
Object.assign(Table, Proto)
|
|
const readSchema = options.keyPath === undefined
|
|
? Schema.Struct({
|
|
...(options.schema as Schema.Struct<{}>).fields,
|
|
key: IndexedDb.IDBValidKey
|
|
})
|
|
: options.schema
|
|
;(Table as any).tableName = options.name
|
|
;(Table as any).tableSchema = options.schema
|
|
;(Table as any).readSchema = readSchema
|
|
;(Table as any).arraySchema = Schema.Array(readSchema as any)
|
|
;(Table as any).autoincrementSchema = options.autoIncrement
|
|
? Schema.Struct(Struct.omit((options.schema as Schema.Struct<{}>).fields, [options.keyPath!] as any))
|
|
: options.schema
|
|
;(Table as any).keyPath = options.keyPath
|
|
;(Table as any).indexes = options.indexes
|
|
;(Table as any).autoIncrement = options.autoIncrement === true
|
|
;(Table as any).durability = options.durability ?? "relaxed"
|
|
return Table as any
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// internal
|
|
// -----------------------------------------------------------------------------
|
|
|
|
type IsValidAutoIncrementKeyPath<
|
|
TableSchema extends AnySchemaStruct,
|
|
KeyPath
|
|
> = KeyPath extends keyof TableSchema["Encoded"] ? TableSchema["Encoded"][KeyPath] extends number ? true
|
|
: false
|
|
: false
|