@ultimabase/soft-delete 0.0.0-stage → 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # @ultimabase/soft-delete
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0e885c4: The first release: the kernel, its adapters, transports and plugins, the testing helpers and the starter.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [0e885c4]
12
+ - ultimabase@0.1.0
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
- # Temporary Holding Version
1
+ # @ultimabase/soft-delete
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Soft delete for UltimaBase. A deleted row gets a `deletedAt` date and leaves every ordinary read; the model's trash still finds it, and restores it.
4
+
5
+ Documentation: [Soft delete](https://ultimabase.dev/docs/soft-delete).
@@ -0,0 +1,24 @@
1
+ import type { Field, Payload, PluginOf, PluginTypes, Queries, UseOf } from 'ultimabase';
2
+ export interface SoftDeleteCommands<F> {
3
+ /** The deleted rows, under the model's read rule. */
4
+ readonly trash: Pick<Queries<F>, 'findMany' | 'findFirst' | 'findUnique' | 'count'>;
5
+ /** Brings a deleted row back, under the model's update rule. */
6
+ readonly restore: (this: void, args: {
7
+ readonly where: Unique<F>;
8
+ }, options?: Parameters<Queries<F>['count']>[1]) => Promise<Payload<F, {}>>;
9
+ }
10
+ type Unique<F> = Parameters<Queries<F>['findUnique']>[0]['where'];
11
+ /** The soft delete plugin's types (D-57). */
12
+ export interface SoftDelete extends PluginTypes {
13
+ readonly name: 'softDelete';
14
+ readonly addedFields: {
15
+ readonly deletedAt: Field<Date, {
16
+ readonly optional: true;
17
+ }>;
18
+ };
19
+ readonly commands: SoftDeleteCommands<this['fields']>;
20
+ }
21
+ export declare const softDelete: UseOf<SoftDelete>;
22
+ export declare const softDeletePlugin: PluginOf<SoftDelete>;
23
+ export {};
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,KAAK,EACL,OAAO,EACP,QAAQ,EACR,WAAW,EACX,OAAO,EACP,KAAK,EACL,MAAM,YAAY,CAAA;AAInB,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACpC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,IAAI,CACnB,OAAO,CAAC,CAAC,CAAC,EACV,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,CACjD,CAAA;IACD,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,CACjB,IAAI,EAAE,IAAI,EACV,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;KAAE,EACnC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KACxC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;CAC5B;AAED,KAAK,MAAM,CAAC,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAEjE,6CAA6C;AAC7C,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC9C,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE;QACrB,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;YAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAC,CAAA;KAC5D,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;CACrD;AAoCD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,UAAU,CAA4B,CAAA;AACrE,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,UAAU,CACtB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import { definePlugin, master } from 'ultimabase';
2
+ import manifest from '../package.json' with { type: 'json' };
3
+ const softDeleteDefinition = definePlugin({
4
+ name: 'softDelete',
5
+ manifest,
6
+ addedFields: (f) => ({
7
+ // Only Master dates a row: a caller deletes through the delete rule.
8
+ deletedAt: f.dateTime({ optional: true }).with({
9
+ access: { create: () => false, update: ({ value }) => value === null },
10
+ }),
11
+ }),
12
+ hooks: {
13
+ // A command reads the trash; every other query, the rows not deleted.
14
+ beforeFind: ({ where, command }) => where({ deletedAt: command === undefined ? null : { not: null } }),
15
+ },
16
+ // Runs after the `delete` access rule and the `beforeDelete` hooks.
17
+ redirect: {
18
+ delete: ({ where, queries }) => queries.update({ where, data: { deletedAt: new Date(Date.now()) } }, { as: master }),
19
+ },
20
+ commands: ({ queries }) => ({
21
+ trash: {
22
+ findMany: queries.findMany,
23
+ findFirst: queries.findFirst,
24
+ findUnique: queries.findUnique,
25
+ count: queries.count,
26
+ },
27
+ restore: ({ where }, options) => queries.update({ where, data: { deletedAt: null } }, options),
28
+ }),
29
+ });
30
+ export const softDelete = softDeleteDefinition.use;
31
+ export const softDeletePlugin = softDeleteDefinition.plugin;
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@ultimabase/soft-delete",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Soft delete for UltimaBase: deleted rows go to a trash and can be restored.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "homepage": "https://ultimabase.dev/docs/soft-delete",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/MrEkinox/ultimabase.git",
11
+ "directory": "packages/soft-delete"
12
+ },
13
+ "engines": {
14
+ "node": ">=24.7.0"
15
+ },
16
+ "exports": {
17
+ ".": "./dist/index.js"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "src",
22
+ "!src/**/*.test.ts",
23
+ "!src/test"
24
+ ],
25
+ "scripts": {
26
+ "prepack": "rm -rf dist && yarn run -T tsc --project tsconfig.build.json"
27
+ },
28
+ "publishConfig": {
29
+ "exports": {
30
+ ".": "./dist/index.js"
31
+ }
32
+ },
33
+ "peerDependencies": {
34
+ "ultimabase": "^0.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "@ultimabase/testing": "0.1.0",
38
+ "ultimabase": "0.1.0",
39
+ "vite": "8.3.4",
40
+ "vitest": "5.0.5"
41
+ }
6
42
  }
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
1
+ // Soft delete (D-17, D-89, D-96): a deleted row gets a `deletedAt` date and
2
+ // leaves every ordinary read; `trash` reads it, `restore` brings it back.
3
+ import type {
4
+ Field,
5
+ Payload,
6
+ PluginOf,
7
+ PluginTypes,
8
+ Queries,
9
+ UseOf,
10
+ } from 'ultimabase'
11
+ import { definePlugin, master } from 'ultimabase'
12
+ import manifest from '../package.json' with { type: 'json' }
13
+
14
+ export interface SoftDeleteCommands<F> {
15
+ /** The deleted rows, under the model's read rule. */
16
+ readonly trash: Pick<
17
+ Queries<F>,
18
+ 'findMany' | 'findFirst' | 'findUnique' | 'count'
19
+ >
20
+ /** Brings a deleted row back, under the model's update rule. */
21
+ readonly restore: (
22
+ this: void,
23
+ args: { readonly where: Unique<F> },
24
+ options?: Parameters<Queries<F>['count']>[1],
25
+ ) => Promise<Payload<F, {}>>
26
+ }
27
+
28
+ type Unique<F> = Parameters<Queries<F>['findUnique']>[0]['where']
29
+
30
+ /** The soft delete plugin's types (D-57). */
31
+ export interface SoftDelete extends PluginTypes {
32
+ readonly name: 'softDelete'
33
+ readonly addedFields: {
34
+ readonly deletedAt: Field<Date, { readonly optional: true }>
35
+ }
36
+ readonly commands: SoftDeleteCommands<this['fields']>
37
+ }
38
+
39
+ const softDeleteDefinition = definePlugin<SoftDelete>({
40
+ name: 'softDelete',
41
+ manifest,
42
+ addedFields: (f) => ({
43
+ // Only Master dates a row: a caller deletes through the delete rule.
44
+ deletedAt: f.dateTime({ optional: true }).with({
45
+ access: { create: () => false, update: ({ value }) => value === null },
46
+ }),
47
+ }),
48
+ hooks: {
49
+ // A command reads the trash; every other query, the rows not deleted.
50
+ beforeFind: ({ where, command }) =>
51
+ where({ deletedAt: command === undefined ? null : { not: null } }),
52
+ },
53
+ // Runs after the `delete` access rule and the `beforeDelete` hooks.
54
+ redirect: {
55
+ delete: ({ where, queries }) =>
56
+ queries.update(
57
+ { where, data: { deletedAt: new Date(Date.now()) } },
58
+ { as: master },
59
+ ),
60
+ },
61
+ commands: ({ queries }) => ({
62
+ trash: {
63
+ findMany: queries.findMany,
64
+ findFirst: queries.findFirst,
65
+ findUnique: queries.findUnique,
66
+ count: queries.count,
67
+ },
68
+ restore: ({ where }, options) =>
69
+ queries.update({ where, data: { deletedAt: null } }, options),
70
+ }),
71
+ })
72
+
73
+ export const softDelete: UseOf<SoftDelete> = softDeleteDefinition.use
74
+ export const softDeletePlugin: PluginOf<SoftDelete> =
75
+ softDeleteDefinition.plugin