@antelopejs/interface-database-decorators 0.1.10 → 0.1.12

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 (71) hide show
  1. package/README.md +9 -7
  2. package/dist/common.js.map +1 -1
  3. package/dist/database.js +18 -2
  4. package/dist/database.js.map +1 -1
  5. package/dist/model.js +48 -29
  6. package/dist/model.js.map +1 -1
  7. package/dist/modifiers/common.d.ts +24 -4
  8. package/dist/modifiers/common.js +104 -130
  9. package/dist/modifiers/common.js.map +1 -1
  10. package/dist/modifiers/encryption.js +14 -9
  11. package/dist/modifiers/encryption.js.map +1 -1
  12. package/dist/modifiers/hash.d.ts +46 -4
  13. package/dist/modifiers/hash.js +172 -14
  14. package/dist/modifiers/hash.js.map +1 -1
  15. package/dist/modifiers/internal/kinds.d.ts +13 -0
  16. package/dist/modifiers/internal/kinds.js +33 -0
  17. package/dist/modifiers/internal/kinds.js.map +1 -0
  18. package/dist/modifiers/internal/records.d.ts +31 -0
  19. package/dist/modifiers/internal/records.js +56 -0
  20. package/dist/modifiers/internal/records.js.map +1 -0
  21. package/dist/modifiers/localization.js +4 -2
  22. package/dist/modifiers/localization.js.map +1 -1
  23. package/dist/schema.js +1 -2
  24. package/dist/schema.js.map +1 -1
  25. package/dist/table.js +21 -6
  26. package/dist/table.js.map +1 -1
  27. package/dist/tests/codec_helpers.d.ts +6 -2
  28. package/dist/tests/codec_helpers.js +18 -14
  29. package/dist/tests/codec_helpers.js.map +1 -1
  30. package/dist/tests/common.test.js +38 -46
  31. package/dist/tests/common.test.js.map +1 -1
  32. package/dist/tests/cross_instance.test.js +27 -2
  33. package/dist/tests/cross_instance.test.js.map +1 -1
  34. package/dist/tests/database.test.js +278 -256
  35. package/dist/tests/database.test.js.map +1 -1
  36. package/dist/tests/hash_upgrade.test.d.ts +1 -0
  37. package/dist/tests/hash_upgrade.test.js +132 -0
  38. package/dist/tests/hash_upgrade.test.js.map +1 -0
  39. package/dist/tests/helpers/hash-rows.d.ts +40 -0
  40. package/dist/tests/helpers/hash-rows.js +41 -0
  41. package/dist/tests/helpers/hash-rows.js.map +1 -0
  42. package/dist/tests/helpers/other-instance.d.ts +1 -1
  43. package/dist/tests/helpers/other-instance.js +41 -9
  44. package/dist/tests/helpers/other-instance.js.map +1 -1
  45. package/dist/tests/integration.test.js +485 -485
  46. package/dist/tests/integration.test.js.map +1 -1
  47. package/dist/tests/model.test.js +293 -308
  48. package/dist/tests/model.test.js.map +1 -1
  49. package/dist/tests/modifiers/autodate.test.js +80 -86
  50. package/dist/tests/modifiers/autodate.test.js.map +1 -1
  51. package/dist/tests/modifiers/common.test.js +181 -195
  52. package/dist/tests/modifiers/common.test.js.map +1 -1
  53. package/dist/tests/modifiers/encryption.test.js +149 -155
  54. package/dist/tests/modifiers/encryption.test.js.map +1 -1
  55. package/dist/tests/modifiers/hash.test.js +386 -99
  56. package/dist/tests/modifiers/hash.test.js.map +1 -1
  57. package/dist/tests/modifiers/localization.test.js +186 -196
  58. package/dist/tests/modifiers/localization.test.js.map +1 -1
  59. package/dist/tests/partial_update.test.d.ts +1 -0
  60. package/dist/tests/partial_update.test.js +55 -0
  61. package/dist/tests/partial_update.test.js.map +1 -0
  62. package/dist/tests/relation.test.js +92 -97
  63. package/dist/tests/relation.test.js.map +1 -1
  64. package/dist/tests/schema.test.js +101 -106
  65. package/dist/tests/schema.test.js.map +1 -1
  66. package/dist/tests/table.test.js +124 -132
  67. package/dist/tests/table.test.js.map +1 -1
  68. package/docs/3.table-modifiers.md +36 -15
  69. package/package.json +14 -3
  70. package/skills/database-decorators-interface/SKILL.md +4 -2
  71. package/skills/database-decorators-interface/cases.json +71 -0
@@ -10,141 +10,133 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const chai_1 = require("chai");
13
- const common_1 = require("@antelopejs/interface-database-decorators/modifiers/common");
14
- const table_1 = require("@antelopejs/interface-database-decorators/table");
15
- const common_2 = require("@antelopejs/interface-database-decorators/common");
13
+ const common_1 = require("../modifiers/common");
14
+ const table_1 = require("../table");
15
+ const common_2 = require("../common");
16
16
  const codec_helpers_1 = require("./codec_helpers");
17
17
  describe("Table - decorators", () => {
18
- it("creates table with default primary key", async () => CreateTableWithDefaultPrimaryKeyTest());
19
- it("creates table with multiple indexes", async () => CreateTableWithMultipleIndexesTest());
20
- it("creates table with grouped indexes", async () => CreateTableWithGroupedIndexesTest());
21
- it("creates table with fixture data", async () => CreateTableWithFixtureDataTest());
22
- it("combines table with mixins", async () => CombineTableWithMixinsTest());
18
+ it("creates table with default primary key", async () => {
19
+ class TestTable extends table_1.Table {
20
+ name;
21
+ }
22
+ __decorate([
23
+ (0, table_1.Index)(),
24
+ __metadata("design:type", String)
25
+ ], TestTable.prototype, "name", void 0);
26
+ const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
27
+ (0, chai_1.expect)(metadata.primary).to.equal("_id");
28
+ (0, chai_1.expect)(metadata.indexes.name).to.deep.equal(["name"]);
29
+ });
30
+ it("creates table with multiple indexes", async () => {
31
+ class TestTable extends table_1.Table {
32
+ email;
33
+ age;
34
+ }
35
+ __decorate([
36
+ (0, table_1.Index)(),
37
+ __metadata("design:type", String)
38
+ ], TestTable.prototype, "email", void 0);
39
+ __decorate([
40
+ (0, table_1.Index)(),
41
+ __metadata("design:type", Number)
42
+ ], TestTable.prototype, "age", void 0);
43
+ const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
44
+ (0, chai_1.expect)(metadata.primary).to.equal("_id");
45
+ (0, chai_1.expect)(metadata.indexes.email).to.deep.equal(["email"]);
46
+ (0, chai_1.expect)(metadata.indexes.age).to.deep.equal(["age"]);
47
+ });
48
+ it("creates table with grouped indexes", async () => {
49
+ class TestTable extends table_1.Table {
50
+ name;
51
+ email;
52
+ age;
53
+ }
54
+ __decorate([
55
+ (0, table_1.Index)({ group: "user_search" }),
56
+ __metadata("design:type", String)
57
+ ], TestTable.prototype, "name", void 0);
58
+ __decorate([
59
+ (0, table_1.Index)({ group: "user_search" }),
60
+ __metadata("design:type", String)
61
+ ], TestTable.prototype, "email", void 0);
62
+ __decorate([
63
+ (0, table_1.Index)({ group: "age_range" }),
64
+ __metadata("design:type", Number)
65
+ ], TestTable.prototype, "age", void 0);
66
+ const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
67
+ (0, chai_1.expect)(metadata.primary).to.equal("_id");
68
+ (0, chai_1.expect)(metadata.indexes.user_search).to.deep.equal(["name", "email"]);
69
+ (0, chai_1.expect)(metadata.indexes.age_range).to.deep.equal(["age"]);
70
+ });
71
+ it("creates table with fixture data", async () => {
72
+ const testData = [
73
+ { id: "1", name: "Test User 1" },
74
+ { id: "2", name: "Test User 2" },
75
+ ];
76
+ let TestTable = class TestTable extends table_1.Table {
77
+ name;
78
+ };
79
+ TestTable = __decorate([
80
+ (0, table_1.Fixture)(() => testData)
81
+ ], TestTable);
82
+ const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
83
+ (0, chai_1.expect)(metadata.generator).to.be.a("function");
84
+ (0, chai_1.expect)(metadata.primary).to.equal("_id");
85
+ });
86
+ it("combines table with mixins", async () => {
87
+ class TestMixin {
88
+ testMethodUnunvailable() {
89
+ return "This method should be undefined";
90
+ }
91
+ [common_1.MixinSymbol] = class {
92
+ testMethod() {
93
+ return "mixin method ok";
94
+ }
95
+ };
96
+ }
97
+ class TestTable extends table_1.Table.with(TestMixin) {
98
+ name;
99
+ }
100
+ const instance = new TestTable();
101
+ (0, chai_1.expect)(instance).to.have.property("testMethod");
102
+ (0, chai_1.expect)(instance.testMethod()).to.equal("mixin method ok");
103
+ (0, chai_1.expect)(instance).to.not.have.property("testMethodUnunvailable");
104
+ (0, chai_1.expect)(instance).to.have.property("name");
105
+ (0, chai_1.expect)(instance).to.be.instanceOf(TestTable);
106
+ });
23
107
  });
24
- async function CreateTableWithDefaultPrimaryKeyTest() {
25
- class TestTable extends table_1.Table {
26
- name;
27
- }
28
- __decorate([
29
- (0, table_1.Index)(),
30
- __metadata("design:type", String)
31
- ], TestTable.prototype, "name", void 0);
32
- const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
33
- (0, chai_1.expect)(metadata.primary).to.equal("_id");
34
- (0, chai_1.expect)(metadata.indexes.name).to.deep.equal(["name"]);
35
- }
36
- async function CreateTableWithMultipleIndexesTest() {
37
- class TestTable extends table_1.Table {
38
- email;
39
- age;
40
- }
41
- __decorate([
42
- (0, table_1.Index)(),
43
- __metadata("design:type", String)
44
- ], TestTable.prototype, "email", void 0);
45
- __decorate([
46
- (0, table_1.Index)(),
47
- __metadata("design:type", Number)
48
- ], TestTable.prototype, "age", void 0);
49
- const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
50
- (0, chai_1.expect)(metadata.primary).to.equal("_id");
51
- (0, chai_1.expect)(metadata.indexes.email).to.deep.equal(["email"]);
52
- (0, chai_1.expect)(metadata.indexes.age).to.deep.equal(["age"]);
53
- }
54
- async function CreateTableWithGroupedIndexesTest() {
55
- class TestTable extends table_1.Table {
56
- name;
57
- email;
58
- age;
59
- }
60
- __decorate([
61
- (0, table_1.Index)({ group: "user_search" }),
62
- __metadata("design:type", String)
63
- ], TestTable.prototype, "name", void 0);
64
- __decorate([
65
- (0, table_1.Index)({ group: "user_search" }),
66
- __metadata("design:type", String)
67
- ], TestTable.prototype, "email", void 0);
68
- __decorate([
69
- (0, table_1.Index)({ group: "age_range" }),
70
- __metadata("design:type", Number)
71
- ], TestTable.prototype, "age", void 0);
72
- const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
73
- (0, chai_1.expect)(metadata.primary).to.equal("_id");
74
- (0, chai_1.expect)(metadata.indexes.user_search).to.deep.equal(["name", "email"]);
75
- (0, chai_1.expect)(metadata.indexes.age_range).to.deep.equal(["age"]);
76
- }
77
- async function CreateTableWithFixtureDataTest() {
78
- const testData = [
79
- { id: "1", name: "Test User 1" },
80
- { id: "2", name: "Test User 2" },
81
- ];
82
- let TestTable = class TestTable extends table_1.Table {
83
- name;
84
- };
85
- TestTable = __decorate([
86
- (0, table_1.Fixture)(() => testData)
87
- ], TestTable);
88
- const metadata = (0, common_2.getMetadata)(TestTable, common_2.DatumStaticMetadata);
89
- (0, chai_1.expect)(metadata.generator).to.be.a("function");
90
- (0, chai_1.expect)(metadata.primary).to.equal("_id");
91
- }
92
108
  describe("Table - @Field decorator", () => {
93
- it("stores a string-token type on metadata.fields", () => StoresStringTokenFieldTest());
94
- it("stores an io-ts codec on metadata.fields", () => StoresCodecFieldTest());
95
- it("stacks with @Index", () => StacksWithIndexTest());
96
- });
97
- function StoresStringTokenFieldTest() {
98
- class _T extends table_1.Table {
99
- }
100
- __decorate([
101
- (0, table_1.Field)("string"),
102
- __metadata("design:type", String)
103
- ], _T.prototype, "name", void 0);
104
- const metadata = (0, common_2.getMetadata)(_T, common_2.DatumStaticMetadata);
105
- (0, chai_1.expect)(metadata.fields.name).to.equal("string");
106
- }
107
- function StoresCodecFieldTest() {
108
- class _T extends table_1.Table {
109
- }
110
- __decorate([
111
- (0, table_1.Field)((0, codec_helpers_1.asFieldType)(codec_helpers_1.stringCodec)),
112
- __metadata("design:type", String)
113
- ], _T.prototype, "name", void 0);
114
- const metadata = (0, common_2.getMetadata)(_T, common_2.DatumStaticMetadata);
115
- (0, chai_1.expect)(metadata.fields.name).to.equal(codec_helpers_1.stringCodec);
116
- }
117
- function StacksWithIndexTest() {
118
- class _T extends table_1.Table {
119
- }
120
- __decorate([
121
- (0, table_1.Index)(),
122
- (0, table_1.Field)("string"),
123
- __metadata("design:type", String)
124
- ], _T.prototype, "email", void 0);
125
- const metadata = (0, common_2.getMetadata)(_T, common_2.DatumStaticMetadata);
126
- (0, chai_1.expect)(metadata.fields.email).to.equal("string");
127
- (0, chai_1.expect)(metadata.indexes.email).to.deep.equal(["email"]);
128
- }
129
- async function CombineTableWithMixinsTest() {
130
- class TestMixin {
131
- testMethodUnunvailable() {
132
- return "This method should be undefined";
109
+ it("stores a string-token type on metadata.fields", () => {
110
+ class _T extends table_1.Table {
133
111
  }
134
- [common_1.MixinSymbol] = class {
135
- testMethod() {
136
- return "mixin method ok";
137
- }
138
- };
139
- }
140
- class TestTable extends table_1.Table.with(TestMixin) {
141
- name;
142
- }
143
- const instance = new TestTable();
144
- (0, chai_1.expect)(instance).to.have.property("testMethod");
145
- (0, chai_1.expect)(instance.testMethod()).to.equal("mixin method ok");
146
- (0, chai_1.expect)(instance).to.not.have.property("testMethodUnunvailable");
147
- (0, chai_1.expect)(instance).to.have.property("name");
148
- (0, chai_1.expect)(instance).to.be.instanceOf(TestTable);
149
- }
112
+ __decorate([
113
+ (0, table_1.Field)("string"),
114
+ __metadata("design:type", String)
115
+ ], _T.prototype, "name", void 0);
116
+ const metadata = (0, common_2.getMetadata)(_T, common_2.DatumStaticMetadata);
117
+ (0, chai_1.expect)(metadata.fields.name).to.equal("string");
118
+ });
119
+ it("stores an io-ts codec on metadata.fields", () => {
120
+ class _T extends table_1.Table {
121
+ }
122
+ __decorate([
123
+ (0, table_1.Field)((0, codec_helpers_1.asFieldType)(codec_helpers_1.stringCodec)),
124
+ __metadata("design:type", String)
125
+ ], _T.prototype, "name", void 0);
126
+ const metadata = (0, common_2.getMetadata)(_T, common_2.DatumStaticMetadata);
127
+ (0, chai_1.expect)(metadata.fields.name).to.equal(codec_helpers_1.stringCodec);
128
+ });
129
+ it("stacks with @Index", () => {
130
+ class _T extends table_1.Table {
131
+ }
132
+ __decorate([
133
+ (0, table_1.Index)(),
134
+ (0, table_1.Field)("string"),
135
+ __metadata("design:type", String)
136
+ ], _T.prototype, "email", void 0);
137
+ const metadata = (0, common_2.getMetadata)(_T, common_2.DatumStaticMetadata);
138
+ (0, chai_1.expect)(metadata.fields.email).to.equal("string");
139
+ (0, chai_1.expect)(metadata.indexes.email).to.deep.equal(["email"]);
140
+ });
141
+ });
150
142
  //# sourceMappingURL=table.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"table.test.js","sourceRoot":"","sources":["../../src/tests/table.test.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+BAA8B;AAC9B,uFAAyF;AACzF,2EAKyD;AACzD,6EAG0D;AAE1D,mDAA2D;AAE3D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE,CACtD,oCAAoC,EAAE,CAAC,CAAC;IAC1C,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE,CACnD,kCAAkC,EAAE,CAAC,CAAC;IACxC,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE,CAClD,iCAAiC,EAAE,CAAC,CAAC;IACvC,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE,CAC/C,8BAA8B,EAAE,CAAC,CAAC;IACpC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE,CAAC,0BAA0B,EAAE,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,oCAAoC;IACjD,MAAM,SAAU,SAAQ,aAAK;QAE3B,IAAI,CAAU;KACf;IADC;QADC,IAAA,aAAK,GAAE;;2CACM;IAGhB,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,SAAS,EAAE,4BAAmB,CAAC,CAAC;IAE7D,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACzC,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,KAAK,UAAU,kCAAkC;IAC/C,MAAM,SAAU,SAAQ,aAAK;QAE3B,KAAK,CAAU;QAGf,GAAG,CAAU;KACd;IAJC;QADC,IAAA,aAAK,GAAE;;4CACO;IAGf;QADC,IAAA,aAAK,GAAE;;0CACK;IAGf,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,SAAS,EAAE,4BAAmB,CAAC,CAAC;IAE7D,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACzC,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,KAAK,UAAU,iCAAiC;IAC9C,MAAM,SAAU,SAAQ,aAAK;QAE3B,IAAI,CAAU;QAGd,KAAK,CAAU;QAGf,GAAG,CAAU;KACd;IAPC;QADC,IAAA,aAAK,EAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;;2CAClB;IAGd;QADC,IAAA,aAAK,EAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;;4CACjB;IAGf;QADC,IAAA,aAAK,EAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;;0CACjB;IAGf,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,SAAS,EAAE,4BAAmB,CAAC,CAAC;IAE7D,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACzC,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,8BAA8B;IAC3C,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE;QAChC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE;KACjC,CAAC;IAGF,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,aAAK;QAC3B,IAAI,CAAU;KACf,CAAA;IAFK,SAAS;QADd,IAAA,eAAO,EAAC,GAAG,EAAE,CAAC,QAAQ,CAAC;OAClB,SAAS,CAEd;IAED,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,SAAS,EAAE,4BAAmB,CAAC,CAAC;IAE7D,IAAA,aAAM,EAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC/C,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE,CACvD,0BAA0B,EAAE,CAAC,CAAC;IAChC,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC7E,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,SAAS,0BAA0B;IACjC,MAAM,EAAG,SAAQ,aAAK;KAGrB;IADS;QADP,IAAA,aAAK,EAAC,QAAQ,CAAC;;oCACK;IAEvB,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,EAAE,EAAE,4BAAmB,CAAC,CAAC;IACtD,IAAA,aAAM,EAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,oBAAoB;IAC3B,MAAM,EAAG,SAAQ,aAAK;KAGrB;IADS;QADP,IAAA,aAAK,EAAC,IAAA,2BAAW,EAAC,2BAAW,CAAC,CAAC;;oCACX;IAEvB,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,EAAE,EAAE,4BAAmB,CAAC,CAAC;IACtD,IAAA,aAAM,EAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,2BAAW,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,EAAG,SAAQ,aAAK;KAIrB;IADS;QAFP,IAAA,aAAK,GAAE;QACP,IAAA,aAAK,EAAC,QAAQ,CAAC;;qCACM;IAExB,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,EAAE,EAAE,4BAAmB,CAAC,CAAC;IACtD,IAAA,aAAM,EAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAA,aAAM,EAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,KAAK,UAAU,0BAA0B;IACvC,MAAM,SAAS;QACb,sBAAsB;YACpB,OAAO,iCAAiC,CAAC;QAC3C,CAAC;QAED,CAAC,oBAAW,CAAC,GAAG;YACd,UAAU;gBACR,OAAO,iBAAiB,CAAC;YAC3B,CAAC;SACF,CAAC;KACH;IAED,MAAM,SAAU,SAAQ,aAAK,CAAC,IAAI,CAAC,SAAS,CAAC;QAC3C,IAAI,CAAU;KACf;IAED,MAAM,QAAQ,GAAG,IAAI,SAAS,EAAE,CAAC;IACjC,IAAA,aAAM,EAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAChD,IAAA,aAAM,EAAE,QAAgB,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACnE,IAAA,aAAM,EAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC;IAChE,IAAA,aAAM,EAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAA,aAAM,EAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"table.test.js","sourceRoot":"","sources":["../../src/tests/table.test.ts"],"names":[],"mappings":"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"}
@@ -59,7 +59,7 @@ class UserCredentials extends Table.with(EncryptionModifier) {
59
59
 
60
60
  ### HashModifier
61
61
 
62
- The `HashModifier` stores a salted one-way hash of field values. Unlike encryption, hashing is irreversible -- you cannot retrieve the original value. Its default SHA-256 hash is fast, not a password-hardening function; use a dedicated password hashing library for production credentials.
62
+ The `HashModifier` stores a salted one-way hash of field values. Unlike encryption, hashing is irreversible -- you cannot retrieve the original value. By default it stores a scrypt hash, a memory-hard function fit for passwords.
63
63
 
64
64
  ```typescript
65
65
  import {
@@ -73,39 +73,60 @@ class User extends Table.with(HashModifier) {
73
73
  @Field("string")
74
74
  declare email: string;
75
75
 
76
- @Hashed({ algorithm: "sha256" })
76
+ @Hashed()
77
77
  @Field("string")
78
78
  declare password: string;
79
79
 
80
- verifyPassword(plainPassword: string): boolean {
81
- return this.testHash("password", plainPassword);
80
+ verifyPassword(plainPassword: string): Promise<boolean> {
81
+ return this.testHashAsync("password", plainPassword);
82
82
  }
83
83
  }
84
84
  ```
85
85
 
86
86
  #### How It Works
87
87
 
88
- - A random salt is generated and stored in the field's metadata on first write.
89
- - The value is hashed with the salt using the specified algorithm.
90
- - The `testHash` method (added by the mixin) hashes a candidate value with the stored salt and compares the result.
88
+ - Each write generates a random salt and stores `$scrypt$ln=15,r=8,p=3$<salt>$<key>` in the field. The string holds the algorithm, the cost, the salt and the hash, so the cost can change later.
89
+ - Assigning the stored hash back to the field, or calling `lock` on a loaded row, keeps the hash as it is.
90
+ - `testHash` and `testHashAsync` read the cost and the salt from the stored string, hash the candidate and compare in constant time.
91
+ - A test spends one scrypt run whatever the row holds, so the time of a refusal does not tell whether a hash exists.
92
+ - A write is synchronous, because a property setter cannot wait. Assigning a value blocks the event loop for one scrypt run, about 150 ms and 32 MiB.
91
93
 
92
94
  #### Options
93
95
 
94
- | Option | Type | Default | Description |
95
- | ----------- | -------- | ---------- | ------------------ |
96
- | `algorithm` | `string` | `"sha256"` | Hashing algorithm. |
97
-
98
- `LocalizationModifier.fallbackLocaleOf(TableClass, field)` returns a field's `fallbackLocale`, or `undefined` when the field is not localized or declares none. Generated forms use it to require a value in that locale.
96
+ | Option | Type | Default | Description |
97
+ | ----------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
98
+ | `algorithm` | `string` | none | Digest to store instead of scrypt, any name `createHash` accepts. A digest is fast to guess: use it only for non-secret fingerprints, never for credentials. Rows stay in the bare digest format. |
99
99
 
100
- #### Mixin Method
100
+ #### Mixin Methods
101
101
 
102
- The `HashModifier` mixin adds a `testHash` method to table instances:
102
+ The `HashModifier` mixin adds these methods to table instances:
103
103
 
104
104
  ```typescript
105
105
  testHash(field: string, value: unknown): boolean
106
+ testHashAsync(field: string, value: unknown): Promise<boolean>
107
+ isHashOutdated(field: string): boolean
108
+ ```
109
+
110
+ - `testHashAsync` returns whether `value` matches the stored hash. It runs scrypt on the thread pool, so use it on a server.
111
+ - `testHash` returns the same result and blocks the event loop while scrypt runs.
112
+ - `isHashOutdated` returns whether the stored hash differs from what the field writes today. It is always `false` on a field with an `algorithm`, and when nothing is stored.
113
+
114
+ #### Upgrade Stored Hashes
115
+
116
+ A row written before scrypt became the default holds a bare salted SHA-256. It still verifies, and `isHashOutdated` returns `true` for it. The same applies to a hash of an earlier cost. To upgrade a row, assign the value after a successful test, because that is the only moment the plaintext is known:
117
+
118
+ ```typescript
119
+ if (await user.testHashAsync("password", password)) {
120
+ if (user.isHashOutdated("password")) {
121
+ user.password = password;
122
+ await userModel.update(user);
123
+ }
124
+ }
106
125
  ```
107
126
 
108
- Returns `true` if `value`, when hashed, matches the stored hash for the given field.
127
+ To move a credential off `@Hashed({ algorithm: "sha256" })`, remove the option. Its rows keep verifying and are flagged for upgrade. A row written with another digest verifies only while the field declares that digest.
128
+
129
+ `LocalizationModifier.fallbackLocaleOf(TableClass, field)` returns a field's `fallbackLocale`, or `undefined` when the field is not localized or declares none. Generated forms use it to require a value in that locale.
109
130
 
110
131
  ### LocalizationModifier
111
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/interface-database-decorators",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "AntelopeJS decorators for database tables and typed data models, with encrypted, hashed and localized fields",
5
5
  "keywords": [
6
6
  "antelopejs",
@@ -127,7 +127,17 @@
127
127
  "release": "pnpm run lint && pnpm run prepack && release-it",
128
128
  "test": "pnpm run build && ajs module test .",
129
129
  "format:check": "oxfmt --check .",
130
- "knip": "knip"
130
+ "knip": "knip",
131
+ "prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
132
+ "checks": "antelopejs-checks",
133
+ "size": "antelopejs-check-size",
134
+ "ratchet": "antelopejs-ratchet",
135
+ "sync": "antelopejs-sync",
136
+ "ratchet:update": "antelopejs-ratchet --update",
137
+ "sync:check": "antelopejs-sync --check",
138
+ "typecheck": "tsc --noEmit",
139
+ "suppressions": "antelopejs-suppressions",
140
+ "suppressions:update": "antelopejs-suppressions --update"
131
141
  },
132
142
  "dependencies": {
133
143
  "randomstring": "^1.3.1",
@@ -138,7 +148,7 @@
138
148
  "@antelopejs/interface-api": "^0.0.15",
139
149
  "@antelopejs/interface-core": "^0.2.4",
140
150
  "@antelopejs/interface-database": "^0.1.11",
141
- "@antelopejs/tooling-configs": "^0.0.9",
151
+ "@antelopejs/tooling-configs": "^0.2.17",
142
152
  "@types/chai": "^4.3.20",
143
153
  "@types/mocha": "^10.0.10",
144
154
  "@types/node": "^22.19.15",
@@ -147,6 +157,7 @@
147
157
  "chai": "^4.5.0",
148
158
  "eslint-plugin-perfectionist": "^5.11.0",
149
159
  "knip": "^6.34.0",
160
+ "lefthook": "^2.2.2",
150
161
  "mongodb-memory-server-core": "^10.4.3",
151
162
  "oxfmt": "^0.66.0",
152
163
  "oxlint": "1.81.0",
@@ -110,7 +110,7 @@ class Chef extends Table.with(
110
110
  EncryptionModifier,
111
111
  LocalizationModifier,
112
112
  ) {
113
- @Hashed() declare passphrase: string; // one-way; test via instance.testHash("passphrase", value)
113
+ @Hashed() declare passphrase: string; // one-way scrypt; test via await instance.testHashAsync("passphrase", value)
114
114
  @Encrypted({ secretKey }) declare supplierCode: string; // transparent encrypt/decrypt (autolock + autounlock)
115
115
  @Localized() declare motto: string; // read AND write via instance.localize("en", ["motto"])
116
116
  @CreationTime() declare hiredAt: Date; // set on insert, stripped from updates
@@ -124,7 +124,9 @@ class Chef extends Table.with(
124
124
  is loaded once before decorators run (e.g. `import "@antelopejs/interface-database-decorators"`).
125
125
  - `Hashed`, `Encrypted`, and `Localized` require the matching modifier class in `Table.with(...)`; `CreationTime`/`UpdateTime` are event-only and need no mixin.
126
126
  - Modifier ordering matters: `attachModifier` throws if you stack a modifier after a one-way modifier (e.g. anything after `@Hashed` on the same field).
127
- - Hashed fields cannot be read back or queried directly — only equality-tested with `testHash`.
127
+ - Hashed fields cannot be read back or queried directly — only equality-tested with `testHashAsync`, or `testHash`, which blocks the event loop for one scrypt run.
128
+ - A row can hold an older hash that still verifies. After a successful test, when `instance.isHashOutdated(field)` is true, assign the tested value and save the row.
129
+ - `@Hashed({ algorithm })` stores a fast digest instead of scrypt: use it only for non-secret fingerprints, never for credentials.
128
130
  - Localized fields must be written through a localized instance (`instance.localize(locale).field = ...`);
129
131
  values assigned before `localize()` are held in floating state and silently dropped on insert/update.
130
132
  - A localized value `""` is stored as no value: reads and queries in that locale get the `fallbackLocale` value.
@@ -0,0 +1,71 @@
1
+ {
2
+ "version": 1,
3
+ "cases": [
4
+ {
5
+ "id": "table-relations",
6
+ "area": "framework",
7
+ "style": "clean",
8
+ "prompt": "How do I define relations between tables?",
9
+ "origin": "docs-assistant",
10
+ "expect": {
11
+ "skills": ["database-decorators-interface"],
12
+ "acceptable": ["database-interface"],
13
+ "mustMention": ["Relation"]
14
+ }
15
+ },
16
+ {
17
+ "id": "table-relations-casual",
18
+ "area": "framework",
19
+ "style": "casual",
20
+ "prompt": "how do i link two tables together",
21
+ "origin": "docs-assistant",
22
+ "expect": {
23
+ "skills": ["database-decorators-interface"],
24
+ "acceptable": ["database-interface"],
25
+ "mustMention": ["Relation"]
26
+ }
27
+ },
28
+ {
29
+ "id": "database-decorators-interface-aql-neighbour",
30
+ "area": "framework",
31
+ "style": "clean",
32
+ "prompt": "Write a query that returns the novels with at least 300 pages, sorted by page count descending, and watch the table for changes.",
33
+ "expect": {
34
+ "skills": ["database-interface"],
35
+ "acceptable": ["antelopejs", "ajs-consume-interface"],
36
+ "avoid": ["database-decorators-interface"]
37
+ }
38
+ },
39
+ {
40
+ "id": "database-decorators-interface-modifiers",
41
+ "area": "framework",
42
+ "style": "clean",
43
+ "prompt": "On my user table class, store the password so it can only be verified, encrypt the API key transparently, and keep created and updated timestamps.",
44
+ "expect": {
45
+ "skills": ["database-decorators-interface"],
46
+ "acceptable": [
47
+ "database-interface",
48
+ "antelopejs",
49
+ "ajs-consume-interface"
50
+ ],
51
+ "mustMention": [
52
+ "Hashed",
53
+ "Encrypted",
54
+ "HashModifier",
55
+ "EncryptionModifier"
56
+ ]
57
+ }
58
+ },
59
+ {
60
+ "id": "database-decorators-interface-fixture-casual",
61
+ "area": "framework",
62
+ "style": "casual",
63
+ "prompt": "how do i seed some default rows when the table is empty",
64
+ "expect": {
65
+ "skills": ["database-decorators-interface"],
66
+ "acceptable": ["database-interface", "antelopejs"],
67
+ "mustMention": ["Fixture", "RegisterSchema"]
68
+ }
69
+ }
70
+ ]
71
+ }