@decaf-ts/for-fabric 0.1.119 → 0.2.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 (145) hide show
  1. package/dist/for-fabric.cjs +1 -1
  2. package/dist/for-fabric.cjs.map +1 -1
  3. package/dist/for-fabric.js +1 -1
  4. package/dist/for-fabric.js.map +1 -1
  5. package/lib/client/FabricClientAdapter.cjs +4 -7
  6. package/lib/client/FabricClientAdapter.js.map +1 -1
  7. package/lib/client/services/FabricIdentityService.cjs +3 -2
  8. package/lib/client/services/FabricIdentityService.js.map +1 -1
  9. package/lib/contract/OtherMarketContract.cjs +30 -0
  10. package/lib/contract/OtherMarketContract.d.ts +5 -0
  11. package/lib/contract/OtherMarketContract.js.map +1 -0
  12. package/lib/contract/OtherProductStrengthContract.cjs +30 -0
  13. package/lib/contract/OtherProductStrengthContract.d.ts +5 -0
  14. package/lib/contract/OtherProductStrengthContract.js.map +1 -0
  15. package/lib/contract/index.cjs +4 -0
  16. package/lib/contract/index.js.map +1 -1
  17. package/lib/contract/models/Audit.js.map +1 -1
  18. package/lib/contract/models/OtherAudit.cjs +105 -0
  19. package/lib/contract/models/OtherAudit.d.ts +13 -0
  20. package/lib/contract/models/OtherAudit.js.map +1 -0
  21. package/lib/contract/models/OtherMarket.cjs +78 -0
  22. package/lib/contract/models/OtherMarket.d.ts +12 -0
  23. package/lib/contract/models/OtherMarket.js.map +1 -0
  24. package/lib/contract/models/OtherProductShared.cjs +15 -4
  25. package/lib/contract/models/OtherProductShared.d.ts +4 -0
  26. package/lib/contract/models/OtherProductShared.js.map +1 -1
  27. package/lib/contract/models/OtherProductStrength.cjs +81 -0
  28. package/lib/contract/models/OtherProductStrength.d.ts +11 -0
  29. package/lib/contract/models/OtherProductStrength.js.map +1 -0
  30. package/lib/contract/models/Product.cjs +1 -1
  31. package/lib/contract/models/Product.js.map +1 -1
  32. package/lib/contract/models/ProductStrength.js.map +1 -1
  33. package/lib/contract/models/decorators-private.cjs +88 -0
  34. package/lib/contract/models/decorators-private.d.ts +16 -0
  35. package/lib/contract/models/decorators-private.js.map +1 -0
  36. package/lib/contract/models/decorators.cjs +31 -2
  37. package/lib/contract/models/decorators.d.ts +6 -1
  38. package/lib/contract/models/decorators.js.map +1 -1
  39. package/lib/contracts/ContractAdapter.cjs +259 -409
  40. package/lib/contracts/ContractAdapter.d.ts +5 -29
  41. package/lib/contracts/ContractAdapter.js.map +1 -1
  42. package/lib/contracts/ContractContext.cjs +41 -13
  43. package/lib/contracts/ContractContext.d.ts +13 -8
  44. package/lib/contracts/ContractContext.js.map +1 -1
  45. package/lib/contracts/FabricContractRepository.cjs +70 -0
  46. package/lib/contracts/FabricContractRepository.d.ts +6 -0
  47. package/lib/contracts/FabricContractRepository.js.map +1 -1
  48. package/lib/contracts/FabricContractSequence.cjs +7 -23
  49. package/lib/contracts/FabricContractSequence.js.map +1 -1
  50. package/lib/contracts/FabricContractStatement.cjs +136 -1
  51. package/lib/contracts/FabricContractStatement.d.ts +5 -2
  52. package/lib/contracts/FabricContractStatement.js.map +1 -1
  53. package/lib/contracts/crud/crud-contract.cjs +42 -17
  54. package/lib/contracts/crud/crud-contract.d.ts +6 -5
  55. package/lib/contracts/crud/crud-contract.js.map +1 -1
  56. package/lib/contracts/types.d.ts +9 -2
  57. package/lib/esm/client/FabricClientAdapter.js +4 -7
  58. package/lib/esm/client/FabricClientAdapter.js.map +1 -1
  59. package/lib/esm/client/services/FabricIdentityService.js +3 -2
  60. package/lib/esm/client/services/FabricIdentityService.js.map +1 -1
  61. package/lib/esm/contract/OtherMarketContract.d.ts +5 -0
  62. package/lib/esm/contract/OtherMarketContract.js +27 -0
  63. package/lib/esm/contract/OtherMarketContract.js.map +1 -0
  64. package/lib/esm/contract/OtherProductStrengthContract.d.ts +5 -0
  65. package/lib/esm/contract/OtherProductStrengthContract.js +27 -0
  66. package/lib/esm/contract/OtherProductStrengthContract.js.map +1 -0
  67. package/lib/esm/contract/index.js +4 -0
  68. package/lib/esm/contract/index.js.map +1 -1
  69. package/lib/esm/contract/models/Audit.js +1 -1
  70. package/lib/esm/contract/models/Audit.js.map +1 -1
  71. package/lib/esm/contract/models/OtherAudit.d.ts +13 -0
  72. package/lib/esm/contract/models/OtherAudit.js +102 -0
  73. package/lib/esm/contract/models/OtherAudit.js.map +1 -0
  74. package/lib/esm/contract/models/OtherMarket.d.ts +12 -0
  75. package/lib/esm/contract/models/OtherMarket.js +75 -0
  76. package/lib/esm/contract/models/OtherMarket.js.map +1 -0
  77. package/lib/esm/contract/models/OtherProductShared.d.ts +4 -0
  78. package/lib/esm/contract/models/OtherProductShared.js +16 -5
  79. package/lib/esm/contract/models/OtherProductShared.js.map +1 -1
  80. package/lib/esm/contract/models/OtherProductStrength.d.ts +11 -0
  81. package/lib/esm/contract/models/OtherProductStrength.js +78 -0
  82. package/lib/esm/contract/models/OtherProductStrength.js.map +1 -0
  83. package/lib/esm/contract/models/Product.js +1 -1
  84. package/lib/esm/contract/models/Product.js.map +1 -1
  85. package/lib/esm/contract/models/ProductStrength.js +1 -1
  86. package/lib/esm/contract/models/ProductStrength.js.map +1 -1
  87. package/lib/esm/contract/models/decorators-private.d.ts +16 -0
  88. package/lib/esm/contract/models/decorators-private.js +81 -0
  89. package/lib/esm/contract/models/decorators-private.js.map +1 -0
  90. package/lib/esm/contract/models/decorators.d.ts +6 -1
  91. package/lib/esm/contract/models/decorators.js +30 -2
  92. package/lib/esm/contract/models/decorators.js.map +1 -1
  93. package/lib/esm/contracts/ContractAdapter.d.ts +5 -29
  94. package/lib/esm/contracts/ContractAdapter.js +262 -412
  95. package/lib/esm/contracts/ContractAdapter.js.map +1 -1
  96. package/lib/esm/contracts/ContractContext.d.ts +13 -8
  97. package/lib/esm/contracts/ContractContext.js +41 -13
  98. package/lib/esm/contracts/ContractContext.js.map +1 -1
  99. package/lib/esm/contracts/FabricContractRepository.d.ts +6 -0
  100. package/lib/esm/contracts/FabricContractRepository.js +70 -0
  101. package/lib/esm/contracts/FabricContractRepository.js.map +1 -1
  102. package/lib/esm/contracts/FabricContractSequence.js +9 -25
  103. package/lib/esm/contracts/FabricContractSequence.js.map +1 -1
  104. package/lib/esm/contracts/FabricContractStatement.d.ts +5 -2
  105. package/lib/esm/contracts/FabricContractStatement.js +135 -0
  106. package/lib/esm/contracts/FabricContractStatement.js.map +1 -1
  107. package/lib/esm/contracts/crud/crud-contract.d.ts +6 -5
  108. package/lib/esm/contracts/crud/crud-contract.js +42 -17
  109. package/lib/esm/contracts/crud/crud-contract.js.map +1 -1
  110. package/lib/esm/contracts/types.d.ts +9 -2
  111. package/lib/esm/shared/decorators.d.ts +8 -0
  112. package/lib/esm/shared/decorators.js +90 -83
  113. package/lib/esm/shared/decorators.js.map +1 -1
  114. package/lib/esm/shared/overrides/overrides.js +7 -6
  115. package/lib/esm/shared/overrides/overrides.js.map +1 -1
  116. package/lib/esm/shared/types.d.ts +3 -2
  117. package/lib/esm/version.d.ts +1 -1
  118. package/lib/esm/version.js +1 -1
  119. package/lib/shared/decorators.cjs +91 -82
  120. package/lib/shared/decorators.d.ts +8 -0
  121. package/lib/shared/decorators.js.map +1 -1
  122. package/lib/shared/overrides/overrides.cjs +7 -6
  123. package/lib/shared/overrides/overrides.js.map +1 -1
  124. package/lib/shared/types.d.ts +3 -2
  125. package/lib/version.cjs +1 -1
  126. package/lib/version.d.ts +1 -1
  127. package/package.json +3 -3
  128. package/lib/contracts/ContractPrivateDataAdapter.cjs +0 -426
  129. package/lib/contracts/ContractPrivateDataAdapter.d.ts +0 -0
  130. package/lib/contracts/ContractPrivateDataAdapter.js.map +0 -1
  131. package/lib/contracts/FabricConstruction.cjs +0 -441
  132. package/lib/contracts/FabricConstruction.d.ts +0 -305
  133. package/lib/contracts/FabricConstruction.js.map +0 -1
  134. package/lib/contracts/private-data.cjs +0 -204
  135. package/lib/contracts/private-data.d.ts +0 -0
  136. package/lib/contracts/private-data.js.map +0 -1
  137. package/lib/esm/contracts/ContractPrivateDataAdapter.d.ts +0 -0
  138. package/lib/esm/contracts/ContractPrivateDataAdapter.js +0 -426
  139. package/lib/esm/contracts/ContractPrivateDataAdapter.js.map +0 -1
  140. package/lib/esm/contracts/FabricConstruction.d.ts +0 -305
  141. package/lib/esm/contracts/FabricConstruction.js +0 -433
  142. package/lib/esm/contracts/FabricConstruction.js.map +0 -1
  143. package/lib/esm/contracts/private-data.d.ts +0 -0
  144. package/lib/esm/contracts/private-data.js +0 -204
  145. package/lib/esm/contracts/private-data.js.map +0 -1
@@ -4,16 +4,15 @@ import { FabricContractContext } from "./ContractContext.js";
4
4
  import { BadRequestError, ConflictError, InternalError, NotFoundError, onCreate, onCreateUpdate, SerializationError, } from "@decaf-ts/db-decorators";
5
5
  import { Object as FabricObject, Property, Property as FabricProperty, } from "fabric-contract-api";
6
6
  import { Logging } from "@decaf-ts/logging";
7
- import { PersistenceKeys, UnsupportedError, Adapter, QueryError, PagingError, MigrationError, ObserverError, AuthorizationError, ForbiddenError, ConnectionError, } from "@decaf-ts/core";
7
+ import { PersistenceKeys, UnsupportedError, Adapter, QueryError, PagingError, MigrationError, ObserverError, AuthorizationError, ForbiddenError, ConnectionError, TransactionOperationKeys, } from "@decaf-ts/core";
8
8
  import { FabricContractRepository } from "./FabricContractRepository.js";
9
9
  import { FabricStatement } from "./FabricContractStatement.js";
10
10
  import { FabricContractSequence } from "./FabricContractSequence.js";
11
- import { FabricFlavour, FabricModelKeys } from "./../shared/constants.js";
11
+ import { FabricFlavour } from "./../shared/constants.js";
12
12
  import { SimpleDeterministicSerializer } from "./../shared/SimpleDeterministicSerializer.js";
13
- import { Decoration, Metadata, propMetadata, } from "@decaf-ts/decoration";
13
+ import { apply, Decoration, Metadata, propMetadata, } from "@decaf-ts/decoration";
14
14
  import { FabricContractPaginator } from "./FabricContractPaginator.js";
15
15
  import { MissingContextError } from "./../shared/errors.js";
16
- const MIRROR_SKIP_FLAG_PREFIX = "mirror:skip:";
17
16
  /**
18
17
  * @description Sets the creator or updater field in a model based on the user in the context
19
18
  * @summary Callback function used in decorators to automatically set the created_by or updated_by fields
@@ -96,12 +95,6 @@ export class FabricContractAdapter extends CouchDBAdapter {
96
95
  */
97
96
  static { this.textDecoder = new TextDecoder("utf8"); }
98
97
  static { this.serializer = new SimpleDeterministicSerializer(); }
99
- setSequenceSegregation(seqName, fullySegregated, collections) {
100
- this._sequenceSegregation.set(seqName, { fullySegregated, collections });
101
- }
102
- getSequenceSegregation(seqName) {
103
- return this._sequenceSegregation.get(seqName);
104
- }
105
98
  /**
106
99
  * @description Context constructor for this adapter
107
100
  * @summary Overrides the base Context constructor with FabricContractContext
@@ -132,17 +125,18 @@ export class FabricContractAdapter extends CouchDBAdapter {
132
125
  */
133
126
  constructor(scope, alias) {
134
127
  super(scope, FabricFlavour, alias);
135
- /**
136
- * @description Stores segregation metadata per sequence name
137
- * @summary Needed because the Sequence creates its own context (via logCtx),
138
- * losing flags set by extractSegregatedCollections on the handler context.
139
- * The adapter persists across operations, making it a reliable store.
140
- */
141
- this._sequenceSegregation = new Map();
142
128
  }
143
129
  for(config, ...args) {
144
130
  return super.for(config, ...args);
145
131
  }
132
+ getModelDefaults(clazz) {
133
+ const m = new clazz();
134
+ return (Metadata.properties(clazz) || []).reduce((acc, p) => {
135
+ if (typeof m[p] !== "undefined")
136
+ acc[p] = m[p];
137
+ return acc;
138
+ }, {});
139
+ }
146
140
  /**
147
141
  * @description Creates a record in the state database
148
142
  * @summary Serializes a model and stores it in the Fabric state database
@@ -160,6 +154,10 @@ export class FabricContractAdapter extends CouchDBAdapter {
160
154
  const tableName = Model.tableName(clazz);
161
155
  const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
162
156
  const isMirror = ctx.getOrUndefined("mirror");
157
+ const segregated = isMirror
158
+ ? ctx.getOrUndefined("segregated")
159
+ : undefined;
160
+ const fullySegregated = ctx.isFullySegregated && !segregated;
163
161
  if (!isMirror) {
164
162
  let existing;
165
163
  try {
@@ -175,30 +173,45 @@ export class FabricContractAdapter extends CouchDBAdapter {
175
173
  throw new ConflictError(`record with id ${id} in table ${tableName} already exists`);
176
174
  }
177
175
  try {
178
- const sanitizedModel = this.normalizeForPublic(clazz, model, ctx);
179
176
  log.info(`adding entry to ${tableName} table with pk ${id}`);
180
- if (ctx.isFullySegregated) {
181
- await this.flushSegregatedWrites(ctx, composedKey);
182
- return sanitizedModel;
177
+ if (segregated) {
178
+ // Mirror write: route the full model to the mirror collection via forPrivate
179
+ model = await this.forPrivate(segregated).putState(composedKey, model, ctx);
180
+ }
181
+ else {
182
+ const defaults = this.getModelDefaults(clazz);
183
+ // handle public data if not fully segregated
184
+ if (!fullySegregated) {
185
+ if (Object.keys(model).filter((k) => {
186
+ if (k === CouchDBKeys.TABLE)
187
+ return false;
188
+ return !(defaults &&
189
+ k in defaults &&
190
+ defaults[k] === model[k]);
191
+ }).length)
192
+ model = await this.putState(composedKey, model, ctx);
193
+ }
194
+ // handle segregated writes
195
+ const data = ctx.getFromChildren("segregatedData");
196
+ if (data) {
197
+ for (const collection in data) {
198
+ Object.assign(model, await this.forPrivate(collection).putState(composedKey, data[collection][id], ctx));
199
+ }
200
+ }
183
201
  }
184
- model = await this.putState(composedKey, sanitizedModel, ctx);
185
202
  }
186
203
  catch (e) {
187
204
  throw this.parseError(e);
188
205
  }
189
206
  return model;
190
207
  }
191
- normalizeForPublic(clazz, model, ctx) {
192
- if (model &&
193
- typeof model === "object" &&
194
- Object.prototype.hasOwnProperty.call(model, CouchDBKeys.TABLE)) {
195
- return model;
196
- }
197
- const instance = Model.isModel(model)
198
- ? model
199
- : Model.build(model, clazz.name);
200
- const prepared = this.prepare(instance, ctx);
201
- return prepared.record;
208
+ async createAll(tableName, id, model, ...args) {
209
+ if (id.length !== model.length)
210
+ throw new InternalError("Ids and models must have the same length");
211
+ const { log, ctxArgs } = this.logCtx(args, this.createAll);
212
+ const tableLabel = Model.tableName(tableName);
213
+ log.debug(`Creating ${id.length} entries ${tableLabel} table`);
214
+ return Promise.all(id.map((i, count) => this.create(tableName, i, model[count], ...ctxArgs)));
202
215
  }
203
216
  /**
204
217
  * @description Reads a record from the state database
@@ -215,22 +228,18 @@ export class FabricContractAdapter extends CouchDBAdapter {
215
228
  const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
216
229
  let model;
217
230
  try {
218
- model = await this.readState(composedKey, ctx);
231
+ model = ctx.isFullySegregated
232
+ ? {}
233
+ : await this.readState(composedKey, ctx);
219
234
  }
220
235
  catch (e) {
221
- const fabricCtx = ctx;
222
- if (e instanceof NotFoundError &&
223
- (fabricCtx.isFullySegregated ||
224
- fabricCtx.getReadCollections().length ||
225
- !!ctx.getOrUndefined("segregated") ||
226
- !!ctx.getOrUndefined("mirror"))) {
227
- model = {};
228
- }
229
- else {
230
- throw this.parseError(e);
231
- }
236
+ throw this.parseError(e);
237
+ }
238
+ const collections = ctx.getReadCollections();
239
+ if (collections && collections.length) {
240
+ for (const col of collections)
241
+ Object.assign(model, await this.forPrivate(col).readState(composedKey, ctx));
232
242
  }
233
- model = await this.mergeSegregatedReads(ctx, composedKey, model);
234
243
  return model;
235
244
  }
236
245
  /**
@@ -246,16 +255,38 @@ export class FabricContractAdapter extends CouchDBAdapter {
246
255
  async update(clazz, id, model, ...args) {
247
256
  const { ctx, log } = this.logCtx(args, this.update);
248
257
  this.enforceMirrorAuthorization(clazz, ctx);
258
+ log.info(`in ADAPTER update with args ${args}`);
249
259
  const tableName = Model.tableName(clazz);
250
260
  const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
261
+ const isMirror = ctx.getOrUndefined("mirror");
262
+ const segregated = isMirror
263
+ ? ctx.getOrUndefined("segregated")
264
+ : undefined;
251
265
  try {
252
- log.verbose(`updating entry to ${tableName} table with pk ${id}`);
253
- const sanitizedModel = this.normalizeForPublic(clazz, model, ctx);
254
- if (ctx.isFullySegregated) {
255
- await this.flushSegregatedWrites(ctx, composedKey);
256
- return sanitizedModel;
266
+ log.info(`updating entry in ${tableName} table with pk ${id}`);
267
+ if (segregated) {
268
+ // Mirror/segregated write: route the full model to the private collection
269
+ model = await this.forPrivate(segregated).putState(composedKey, model, ctx);
270
+ }
271
+ else {
272
+ const defaults = this.getModelDefaults(clazz);
273
+ // handle public data
274
+ if (Object.keys(model).filter((k) => {
275
+ if (k === CouchDBKeys.TABLE)
276
+ return false;
277
+ return !(defaults &&
278
+ k in defaults &&
279
+ defaults[k] === model[k]);
280
+ }).length)
281
+ model = await this.putState(composedKey, model, ctx);
282
+ // handle segregated writes
283
+ const data = ctx.getFromChildren("segregatedData");
284
+ if (data) {
285
+ for (const collection in data) {
286
+ Object.assign(model, await this.forPrivate(collection).putState(composedKey, data[collection][id], ctx));
287
+ }
288
+ }
257
289
  }
258
- model = await this.putState(composedKey, sanitizedModel, ctx);
259
290
  }
260
291
  catch (e) {
261
292
  throw this.parseError(e);
@@ -271,40 +302,48 @@ export class FabricContractAdapter extends CouchDBAdapter {
271
302
  * @return {Promise<Record<string, any>>} Promise resolving to the deleted record
272
303
  */
273
304
  async delete(clazz, id, ...args) {
274
- const { ctx, log, ctxArgs } = this.logCtx(args, this.delete);
305
+ const { ctx } = this.logCtx(args, this.delete);
275
306
  this.enforceMirrorAuthorization(clazz, ctx);
276
307
  const tableName = Model.tableName(clazz);
277
- const pkProp = Model.pk(clazz);
308
+ const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
278
309
  const isMirror = ctx.getOrUndefined("mirror");
310
+ const segregated = isMirror
311
+ ? ctx.getOrUndefined("segregated")
312
+ : undefined;
279
313
  let model;
280
- let shouldDeletePublicState = false;
281
- try {
282
- const composedKey = ctx.stub.createCompositeKey(tableName, [String(id)]);
283
- if (!isMirror) {
284
- model = await this.read(clazz, id, ...ctxArgs);
285
- shouldDeletePublicState = this.hasPublicState(model, clazz);
286
- }
287
- else {
288
- model = { [pkProp]: id };
314
+ if (segregated) {
315
+ // Mirror/segregated delete: route through forPrivate
316
+ try {
317
+ model = await this.forPrivate(segregated).readState(composedKey, ctx);
318
+ await this.forPrivate(segregated).deleteState(composedKey, ctx);
289
319
  }
290
- log.verbose(`deleting entry with pk ${id} from ${tableName} table`);
291
- if (shouldDeletePublicState) {
292
- await this.deleteState(composedKey, ctx);
320
+ catch (e) {
321
+ throw this.parseError(e);
293
322
  }
294
- await this.deleteSegregatedCollections(ctx, composedKey);
295
323
  }
296
- catch (e) {
297
- throw this.parseError(e);
324
+ else {
325
+ try {
326
+ model = ctx.isFullySegregated
327
+ ? {}
328
+ : await this.readState(composedKey, ctx);
329
+ if (!ctx.isFullySegregated)
330
+ await this.deleteState(composedKey, ctx);
331
+ }
332
+ catch (e) {
333
+ throw this.parseError(e);
334
+ }
335
+ const collections = ctx.getReadCollections();
336
+ if (collections && collections.length) {
337
+ for (const col of collections) {
338
+ Object.assign(model, await this.forPrivate(col).readState(composedKey, ctx));
339
+ await this.forPrivate(col).deleteState(composedKey, ctx);
340
+ }
341
+ }
298
342
  }
299
343
  return model;
300
344
  }
301
345
  async deleteState(id, context) {
302
- const { ctx } = this.logCtx([context], this.deleteState);
303
- const collection = ctx.getOrUndefined("segregated");
304
- if (collection)
305
- await ctx.stub.deletePrivateData(collection, id);
306
- else
307
- await ctx.stub.deleteState(id);
346
+ await context.stub.deleteState(id);
308
347
  }
309
348
  forPrivate(collection) {
310
349
  const toOverride = [
@@ -349,55 +388,62 @@ export class FabricContractAdapter extends CouchDBAdapter {
349
388
  }
350
389
  case "queryResult": {
351
390
  const [stub, rawInput] = argsList;
352
- return stub.getPrivateDataQueryResult(collection, rawInput);
391
+ return stub.getPrivateDataQueryResult(collection, JSON.stringify(rawInput));
353
392
  }
354
393
  case "queryResultPaginated": {
355
- const [stub, rawInput, limit, skip] = argsList;
356
- const iterator = await stub.getPrivateDataQueryResult(collection, rawInput);
394
+ const [stub, rawInput, limit, skip, bookmark] = argsList;
395
+ const iterator = await stub.getPrivateDataQueryResult(collection, JSON.stringify(rawInput));
357
396
  const results = [];
358
397
  let count = 0;
359
- let reachedBookmark = skip ? false : true;
398
+ const skipKey = bookmark || skip;
399
+ let reachedBookmark = skipKey ? false : true;
360
400
  let lastKey = null;
361
401
  while (true) {
362
402
  const res = await iterator.next();
363
403
  if (res.value && res.value.value.toString()) {
364
404
  const recordKey = res.value.key;
365
405
  const recordValue = res.value.value.toString("utf8");
366
- // If we have a skip, skip until we reach it
406
+ // If we have a bookmark/skip, skip until we pass it
367
407
  if (!reachedBookmark) {
368
- if (recordKey === skip?.toString()) {
408
+ if (recordKey === skipKey?.toString()) {
369
409
  reachedBookmark = true;
370
410
  }
371
411
  continue;
372
412
  }
373
413
  results.push({
374
- Key: recordKey,
375
- Record: JSON.parse(recordValue),
414
+ key: recordKey,
415
+ value: Buffer.from(recordValue),
376
416
  });
377
417
  lastKey = recordKey;
378
418
  count++;
379
419
  if (count >= limit) {
380
420
  await iterator.close();
381
- return {
382
- iterator: results,
383
- metadata: {
384
- fetchedRecordsCount: results.length,
385
- bookmark: lastKey,
386
- },
387
- };
421
+ break;
388
422
  }
389
423
  }
390
424
  if (res.done) {
391
425
  await iterator.close();
392
- return {
393
- iterator: results,
394
- metadata: {
395
- fetchedRecordsCount: results.length,
396
- bookmark: "",
397
- },
398
- };
426
+ break;
399
427
  }
400
428
  }
429
+ // Wrap results in an async iterator compatible with resultIterator()
430
+ let idx = 0;
431
+ const arrayIterator = {
432
+ async next() {
433
+ if (idx < results.length) {
434
+ return { value: results[idx++], done: false };
435
+ }
436
+ return { value: undefined, done: true };
437
+ },
438
+ async close() { },
439
+ };
440
+ return {
441
+ iterator: arrayIterator,
442
+ metadata: {
443
+ fetchedRecordsCount: results.length,
444
+ bookmark: count >= limit ? lastKey || "" : "",
445
+ },
446
+ };
401
447
  }
402
448
  default:
403
449
  throw new InternalError(`Unsupported method override ${String(prop)}`);
@@ -409,156 +455,22 @@ export class FabricContractAdapter extends CouchDBAdapter {
409
455
  }
410
456
  async putState(id, model, ctx) {
411
457
  let data;
412
- const { log } = this.logCtx([ctx], this.putState);
413
458
  try {
414
459
  data = Buffer.from(FabricContractAdapter.serializer.serialize(model, false));
415
460
  }
416
461
  catch (e) {
417
462
  throw new SerializationError(`Failed to serialize record with id ${id}: ${e}`);
418
463
  }
419
- const collection = ctx.getOrUndefined("segregated");
420
- if (collection) {
421
- if (ctx.getOrUndefined("mirror")) {
422
- const cacheKey = `segregatedRecord:${collection}:${id.toString()}`;
423
- let existingRecord = ctx.cache.get(cacheKey);
424
- ctx.cache.remove(cacheKey);
425
- if (!existingRecord) {
426
- const existing = await ctx.stub.getPrivateData(collection, id.toString());
427
- if (existing) {
428
- try {
429
- existingRecord = FabricContractAdapter.serializer.deserialize(existing.toString());
430
- }
431
- catch {
432
- existingRecord = undefined;
433
- }
434
- }
435
- }
436
- if (existingRecord) {
437
- const merged = Object.assign({}, existingRecord, model);
438
- data = Buffer.from(FabricContractAdapter.serializer.serialize(merged, false));
439
- }
440
- }
441
- await ctx.stub.putPrivateData(collection, id.toString(), data);
442
- }
443
- else {
444
- await ctx.stub.putState(id.toString(), data);
445
- await this.flushSegregatedWrites(ctx, id);
446
- }
447
- log.silly(`state stored${collection ? ` in ${collection} collection` : ""} under id ${id}`);
448
- return model;
449
- }
450
- async flushSegregatedWrites(ctx, compositeKey) {
451
- const writeCollections = ctx.getWriteCollections();
452
- if (!writeCollections.length)
453
- return;
454
- const writes = ctx._segregateWrite;
455
- if (!writes)
456
- return;
457
- for (const collection of writeCollections) {
458
- const entries = writes[collection];
459
- if (!entries || !entries.length)
460
- continue;
461
- const record = this.buildSegregatedRecord(entries, collection);
462
- if (!record || !Object.keys(record).length)
463
- continue;
464
- const cacheKey = `segregatedRecord:${collection}:${compositeKey}`;
465
- ctx.cache.put(cacheKey, record);
466
- const privateCtx = ctx.override({
467
- segregated: collection,
468
- });
469
- await this.putState(compositeKey, record, privateCtx);
470
- }
471
- ctx.cache.remove("segregateWrite");
472
- }
473
- buildSegregatedRecord(entries, collection) {
474
- if (!entries || !entries.length)
475
- return undefined;
476
- const firstModel = entries.find((entry) => !!entry.model)?.model;
477
- if (!firstModel)
478
- return undefined;
479
- const clazz = firstModel.constructor;
480
- const pkProp = Model.pk(clazz);
481
- const keys = new Set();
482
- entries.forEach((entry) => entry.keys.forEach((key) => keys.add(key)));
483
- const metadataFields = this.getFieldsForCollection(firstModel, collection);
484
- metadataFields.forEach((key) => keys.add(key));
485
- if (pkProp)
486
- keys.add(pkProp);
487
- const record = {};
488
- keys.forEach((key) => {
489
- const value = entries
490
- .map((entry) => entry.model[key])
491
- .find((val) => typeof val !== "undefined");
492
- if (typeof value === "undefined")
493
- return;
494
- const mappedProp = Model.columnName(clazz, key);
495
- if (this.isReserved(mappedProp))
496
- throw new InternalError(`Property name ${mappedProp} is reserved`);
497
- record[mappedProp] =
498
- value instanceof Date ? new Date(value) : value;
499
- });
500
- return record;
501
- }
502
- async mergeSegregatedReads(ctx, id, model) {
503
- // Use getReadCollections which reads from the private _segregateRead property
504
- const reads = ctx.getReadCollections();
505
- if (!reads?.length)
506
- return model;
507
- for (const collection of reads) {
508
- const skipFlagKey = `${MIRROR_SKIP_FLAG_PREFIX}${collection}`;
509
- if (ctx.getOrUndefined(skipFlagKey))
510
- continue;
511
- const privateRecord = await this.readPrivateRecord(ctx, collection, id);
512
- if (!privateRecord)
513
- continue;
514
- Object.entries(privateRecord).forEach(([key, value]) => {
515
- if (typeof value === "undefined")
516
- return;
517
- model[key] = value;
518
- });
519
- }
464
+ await ctx.stub.putState(id.toString(), data);
520
465
  return model;
521
466
  }
522
- async readPrivateRecord(ctx, collection, id) {
523
- const data = await ctx.stub.getPrivateData(collection, id);
524
- if (!data)
525
- return undefined;
526
- const text = data.toString();
527
- if (!text)
528
- return undefined;
529
- try {
530
- return FabricContractAdapter.serializer.deserialize(text);
531
- }
532
- catch {
533
- return undefined;
534
- }
535
- }
536
- async deleteSegregatedCollections(ctx, id) {
537
- // Use getReadCollections which reads from the private _segregateRead property
538
- const reads = ctx.getReadCollections();
539
- if (!reads?.length)
540
- return;
541
- for (const collection of reads) {
542
- try {
543
- await ctx.stub.deletePrivateData(collection, id);
544
- }
545
- catch {
546
- // ignore missing private data
547
- }
548
- }
549
- }
550
467
  async readState(id, ctx) {
551
468
  let result;
552
469
  const { log } = this.logCtx([ctx], this.readState);
553
- let res;
554
- const collection = ctx.get("segregated");
555
- if (collection)
556
- res = (await ctx.stub.getPrivateData(collection, id.toString())).toString();
557
- else
558
- res = (await ctx.stub.getState(id.toString())).toString();
470
+ const res = (await ctx.stub.getState(id.toString())).toString();
559
471
  if (!res)
560
- throw new NotFoundError(`Record with id ${id}${collection ? ` in ${collection} collection` : ""} not found`);
561
- log.silly(`state retrieved from${collection ? ` ${collection} collection` : ""} under id ${id}`);
472
+ throw new NotFoundError(`Record with id ${id} not found`);
473
+ log.silly(`state retrieved under id ${id}`);
562
474
  try {
563
475
  result = FabricContractAdapter.serializer.deserialize(res.toString());
564
476
  }
@@ -567,84 +479,15 @@ export class FabricContractAdapter extends CouchDBAdapter {
567
479
  }
568
480
  return result;
569
481
  }
570
- async queryResult(stub, rawInput, ...args) {
571
- const { ctx } = this.logCtx(args, this.queryResult);
572
- let res;
573
- const collection = ctx.get("segregated");
574
- if (collection)
575
- res = await ctx.stub.getPrivateDataQueryResult(collection, JSON.stringify(rawInput));
576
- else
577
- res = await stub.getQueryResult(JSON.stringify(rawInput));
578
- return res;
579
- }
580
- createRowsIterator(rows) {
581
- let index = 0;
582
- return {
583
- // @ts-expect-error typeing of iterator?
584
- async next() {
585
- if (index < rows.length) {
586
- const row = rows[index++];
587
- return {
588
- value: { key: row.key, value: row.value },
589
- done: false,
590
- };
591
- }
592
- return { done: true };
593
- },
594
- async close() {
595
- // noop
596
- },
597
- };
598
- }
599
- async queryResultPaginated(stub, rawInput, limit = 250, page, bookmark, ...args) {
600
- const { ctx } = this.logCtx(args, this.readState);
601
- let res;
602
- const collection = ctx.get("segregated");
603
- if (collection) {
604
- const clonedInput = JSON.parse(JSON.stringify(rawInput));
605
- clonedInput.selector = clonedInput.selector || {};
606
- if (bookmark)
607
- clonedInput.selector._id = { $gt: bookmark.toString() };
608
- const limitValue = typeof limit === "number" && limit > 0 ? limit : Number.MAX_VALUE;
609
- const iterator = await stub.getPrivateDataQueryResult(collection, JSON.stringify(clonedInput));
610
- const rows = [];
611
- let lastKey = "";
612
- while (rows.length < limitValue) {
613
- const resRow = await iterator.next();
614
- if (resRow.done)
615
- break;
616
- if (resRow.value && resRow.value.value) {
617
- rows.push({
618
- key: resRow.value.key,
619
- value: resRow.value.value,
620
- });
621
- lastKey = resRow.value.key;
622
- }
623
- }
624
- await iterator.close();
625
- res = {
626
- iterator: this.createRowsIterator(rows),
627
- metadata: {
628
- fetchedRecordsCount: rows.length,
629
- bookmark: rows.length ? lastKey : "",
630
- },
631
- };
632
- }
633
- else
634
- res = await stub.getQueryResultWithPagination(JSON.stringify(rawInput), limit, bookmark?.toString());
635
- return res;
482
+ async queryResult(stub, rawInput,
483
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
484
+ ...args) {
485
+ return stub.getQueryResult(JSON.stringify(rawInput));
636
486
  }
637
- mergeModels(results) {
638
- const extract = (model) => Object.entries(model).reduce((accum, [key, val]) => {
639
- if (typeof val !== "undefined")
640
- accum[key] = val;
641
- return accum;
642
- }, {});
643
- let finalModel = results.pop();
644
- for (const res of results) {
645
- finalModel = Object.assign({}, extract(finalModel), extract(res));
646
- }
647
- return finalModel;
487
+ async queryResultPaginated(stub, rawInput, limit = 250, page, bookmark,
488
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
489
+ ...args) {
490
+ return stub.getQueryResultWithPagination(JSON.stringify(rawInput), limit, bookmark?.toString());
648
491
  }
649
492
  /**
650
493
  * @description Decodes binary data to string
@@ -669,6 +512,7 @@ export class FabricContractAdapter extends CouchDBAdapter {
669
512
  let baseFlags = Object.assign({
670
513
  segregated: false,
671
514
  rebuildWithTransient: false,
515
+ fullySegregated: false,
672
516
  }, flags);
673
517
  const stubFromFlags = flags.stub || flags.stub;
674
518
  const identityFromFlags = flags.identity ||
@@ -793,15 +637,16 @@ export class FabricContractAdapter extends CouchDBAdapter {
793
637
  * FabricContractAdapter-->>Caller: results
794
638
  */
795
639
  async raw(rawInput, docsOnly = true, ...args) {
796
- const { log, ctx } = this.logCtx(args, this.raw);
640
+ const { log, ctx, ctxArgs } = this.logCtx(args, this.raw);
641
+ const enableSegregates = !args.length || args[0] !== true;
797
642
  const { skip, limit } = rawInput;
798
643
  let iterator;
799
- const resp = { docs: [], bookmark: undefined };
644
+ let resp = { docs: [], bookmark: undefined };
800
645
  if (limit || skip) {
801
646
  delete rawInput["limit"];
802
647
  delete rawInput["skip"];
803
648
  log.debug(`Retrieving paginated iterator: limit: ${limit}/ skip: ${skip}`);
804
- const response = (await this.queryResultPaginated(ctx.stub, rawInput, limit || Number.MAX_VALUE, skip?.toString(), rawInput["bookmark"], ctx));
649
+ const response = (await this.queryResultPaginated(ctx.stub, rawInput, limit || Number.MAX_VALUE, skip?.toString(), rawInput["bookmark"], ...[ctx]));
805
650
  resp.bookmark = response.metadata.bookmark;
806
651
  iterator = response.iterator;
807
652
  }
@@ -812,8 +657,30 @@ export class FabricContractAdapter extends CouchDBAdapter {
812
657
  log.debug("Iterator acquired");
813
658
  resp.docs = (await this.resultIterator(log, iterator));
814
659
  log.debug(`returning ${Array.isArray(resp.docs) ? resp.docs.length : 1} results`);
815
- if (docsOnly)
660
+ const collections = enableSegregates ? ctx.getReadCollections() : undefined;
661
+ const segregated = [];
662
+ if (collections && collections.length) {
663
+ // Restore limit/skip for segregated queries (they were removed above)
664
+ const segregatedInput = { ...rawInput };
665
+ if (limit)
666
+ segregatedInput.limit = limit;
667
+ if (skip)
668
+ segregatedInput.skip = skip;
669
+ for (const collection of collections) {
670
+ segregated.push(await this.forPrivate(collection).raw(segregatedInput, false, true, ...ctxArgs));
671
+ }
672
+ // choose the response with the most results
673
+ resp = segregated.reduce((acc, curr) => {
674
+ if (!acc)
675
+ return curr;
676
+ if (curr.docs && curr.docs.length >= acc?.docs.length)
677
+ return curr;
678
+ return acc;
679
+ }, resp);
680
+ }
681
+ if (docsOnly) {
816
682
  return resp.docs;
683
+ }
817
684
  return resp;
818
685
  }
819
686
  async view(
@@ -830,14 +697,6 @@ export class FabricContractAdapter extends CouchDBAdapter {
830
697
  Statement() {
831
698
  return new FabricStatement(this);
832
699
  }
833
- async createAll(tableName, id, model, ...args) {
834
- if (id.length !== model.length)
835
- throw new InternalError("Ids and models must have the same length");
836
- const { log, ctxArgs } = this.logCtx(args, this.createAll);
837
- const tableLabel = Model.tableName(tableName);
838
- log.debug(`Creating ${id.length} entries ${tableLabel} table`);
839
- return Promise.all(id.map((i, count) => this.create(tableName, i, model[count], ...ctxArgs)));
840
- }
841
700
  async updateAll(tableName, id, model, ...args) {
842
701
  if (id.length !== model.length)
843
702
  throw new InternalError("Ids and models must have the same length");
@@ -854,115 +713,98 @@ export class FabricContractAdapter extends CouchDBAdapter {
854
713
  */
855
714
  prepare(model, ...args) {
856
715
  const { log, ctx } = this.logCtx(args, this.prepare);
716
+ const split = Model.segregate(model);
857
717
  const tableName = Model.tableName(model.constructor);
858
718
  const pk = Model.pk(model.constructor);
859
- const collection = ctx.getOrUndefined("segregated");
719
+ const id = model[pk];
860
720
  const isMirror = ctx.getOrUndefined("mirror");
861
- let entries;
862
- if (collection && isMirror) {
863
- // MIRROR mode: keep ALL properties (full model copy)
864
- entries = Object.keys(model)
865
- .map((key) => [key, model[key]])
866
- .filter(([, val]) => typeof val !== "undefined");
867
- }
868
- else if (collection) {
869
- // SEGREGATED mode: keep only properties decorated for this collection + pk
870
- const collectionFields = this.getFieldsForCollection(model, collection);
871
- entries = [...new Set([pk, ...collectionFields])]
872
- .map((key) => [key, model[key]])
873
- .filter(([, val]) => typeof val !== "undefined");
874
- }
875
- else {
876
- // NORMAL mode: strip transient (current behavior)
877
- const split = Model.segregate(model);
878
- entries = Object.entries(split.model).filter(([, val]) => typeof val !== "undefined");
879
- }
880
- const record = entries.reduce((accum, [key, val]) => {
881
- const mappedProp = Model.columnName(model, key);
882
- if (this.isReserved(mappedProp))
883
- throw new InternalError(`Property name ${mappedProp} is reserved`);
884
- val = val instanceof Date ? new Date(val) : val;
885
- accum[mappedProp] = val;
886
- return accum;
887
- }, {});
888
- // Add table identifier
889
- record[CouchDBKeys.TABLE] = tableName;
721
+ const mapToRecord = function (obj, keysOverride) {
722
+ if (keysOverride)
723
+ keysOverride = [...new Set([...keysOverride, pk])];
724
+ const result = Object.entries(obj).reduce((accum, [key, val]) => {
725
+ if (typeof val === "undefined")
726
+ return accum;
727
+ if (keysOverride && !keysOverride.includes(key))
728
+ return accum;
729
+ const mappedProp = Model.columnName(model, key);
730
+ if (this.isReserved(mappedProp))
731
+ throw new InternalError(`Property name ${mappedProp} is reserved`);
732
+ val = val instanceof Date ? new Date(val) : val;
733
+ accum[mappedProp] = val;
734
+ return accum;
735
+ }, {});
736
+ if (Object.keys(result).filter((k) => Boolean(result[k])).length) {
737
+ // Add table identifier
738
+ result[CouchDBKeys.TABLE] = tableName;
739
+ }
740
+ return result;
741
+ }.bind(this);
890
742
  log.silly(`Preparing record for ${tableName} table with pk ${model[pk]}`);
743
+ const segregatedWriteKeys = ctx.getSegregatedWrites();
744
+ const segregatedWrites = {};
745
+ if (segregatedWriteKeys) {
746
+ for (const collection in segregatedWriteKeys) {
747
+ segregatedWrites[collection] = segregatedWrites[collection] || {};
748
+ segregatedWrites[collection][id] = mapToRecord(ctx.getOrUndefined("forceSegregateWrite")
749
+ ? split.model
750
+ : split.transient, segregatedWriteKeys[collection]);
751
+ }
752
+ }
753
+ // In mirror mode, the record should contain ALL model properties (full copy)
754
+ const record = isMirror ? mapToRecord(model) : mapToRecord(split.model);
891
755
  return {
892
756
  record,
893
757
  id: model[pk],
894
- transient: collection ? undefined : Model.segregate(model).transient,
758
+ transient: !isMirror && split.transient && Object.keys(split.transient).length
759
+ ? mapToRecord(split.transient)
760
+ : undefined,
761
+ segregated: isMirror ? undefined : segregatedWrites,
895
762
  };
896
763
  }
897
- /**
898
- * @description Gets the property names that belong to a specific private/shared collection
899
- * @summary Looks up per-property metadata set by @privateData/@sharedData decorators
900
- * to determine which properties are decorated for the given collection name.
901
- */
902
- getFieldsForCollection(model, collection) {
903
- const constr = model.constructor;
904
- const properties = Metadata.validatableProperties(constr);
905
- if (!properties)
906
- return [];
907
- return properties.filter((prop) => {
908
- const privateKey = Metadata.key(FabricModelKeys.PRIVATE, prop);
909
- const privateMeta = Metadata.get(constr, privateKey);
910
- if (privateMeta?.collections?.includes(collection))
911
- return true;
912
- const sharedKey = Metadata.key(FabricModelKeys.SHARED, prop);
913
- const sharedMeta = Metadata.get(constr, sharedKey);
914
- if (sharedMeta?.collections?.includes(collection))
915
- return true;
916
- return false;
917
- });
918
- }
919
764
  revert(obj, clazz, id, transient, ...args) {
920
765
  const { log, ctx } = this.logCtx(args, this.revert);
921
766
  const ob = {};
922
767
  const pk = Model.pk(clazz);
768
+ const pkProps = Model.pkProps(clazz);
769
+ if (pkProps?.type === Number && typeof id === "string") {
770
+ id = Number(id);
771
+ }
923
772
  ob[pk] = id;
924
- const m = (typeof clazz === "string" ? Model.build(ob, clazz) : new clazz(ob));
925
- log.silly(`Rebuilding model ${m.constructor.name} id ${id}`);
926
- const result = Object.keys(m).reduce((accum, key) => {
927
- accum[key] =
928
- obj[Model.columnName(accum, key)];
929
- return accum;
930
- }, m);
931
- if (transient &&
932
- this.shouldRebuildWithTransient(ctx, ctx.getOrUndefined("operation"))) {
933
- log.debug(`re-adding transient properties: ${Object.keys(transient).join(", ")}`);
934
- Object.entries(transient).forEach(([key, val]) => {
935
- if (key in result && result[key] !== undefined)
936
- log.warn(`overwriting existing ${key}. if this is not a default value, this may pose a problem`);
937
- result[key] = val;
938
- });
773
+ log.silly(`Rebuilding model ${clazz.name} id ${id}`);
774
+ function mapToModel(r) {
775
+ const m = (typeof clazz === "string" ? Model.build(ob, clazz) : new clazz(ob));
776
+ const attributes = Model.getAttributes(clazz);
777
+ const keys = attributes.length ? attributes : Object.keys(m);
778
+ return keys
779
+ .filter((k) => k !== pk)
780
+ .reduce((accum, key) => {
781
+ accum[key] =
782
+ r[Model.columnName(accum, key)];
783
+ return accum;
784
+ }, m);
785
+ }
786
+ let result = mapToModel(obj);
787
+ if (transient && !this.shouldRebuildWithTransient(ctx)) {
788
+ log.debug(`filtering transient properties: ${Object.keys(transient).join(", ")}`);
789
+ result = Object.entries(result).reduce((acc, [key, v]) => {
790
+ if (key === pk || !(key in transient)) {
791
+ acc[key] = v;
792
+ }
793
+ return acc;
794
+ }, new clazz());
939
795
  }
940
796
  return result;
941
797
  }
942
- shouldRebuildWithTransient(ctx, operation) {
798
+ shouldRebuildWithTransient(ctx) {
943
799
  if (!ctx)
944
800
  return false;
945
801
  if (ctx.getOrUndefined("rebuildWithTransient"))
946
802
  return true;
803
+ const operation = ctx.getOrUndefined("operation");
947
804
  if (!operation)
948
- return false;
805
+ return true;
949
806
  const op = operation.toString().toLowerCase();
950
- return (op.includes("read") ||
951
- op.includes("find") ||
952
- op.includes("query") ||
953
- op.includes("statement") ||
954
- op.includes("page"));
955
- }
956
- hasPublicState(model, clazz) {
957
- if (!model)
958
- return false;
959
- const instance = Model.isModel(model)
960
- ? model
961
- : Model.build(model, clazz.name);
962
- const split = Model.segregate(instance);
963
- const pkProp = Model.pk(clazz);
964
- return Object.keys(split.model).some((key) => key !== pkProp &&
965
- typeof split.model[key] !== "undefined");
807
+ return !TransactionOperationKeys.map((k) => k.toLowerCase()).includes(op);
966
808
  }
967
809
  getContextMsp(context) {
968
810
  const identity = context.get("identity");
@@ -1095,11 +937,19 @@ export class FabricContractAdapter extends CouchDBAdapter {
1095
937
  super.decoration();
1096
938
  Decoration.flavouredAs(FabricFlavour)
1097
939
  .for(PersistenceKeys.CREATED_BY)
1098
- .define(onCreate(createdByOnFabricCreateUpdate), propMetadata(PersistenceKeys.CREATED_BY, {}))
940
+ .define({
941
+ decorator: function createdBy() {
942
+ return apply(onCreate(createdByOnFabricCreateUpdate), propMetadata(PersistenceKeys.CREATED_BY, {}));
943
+ },
944
+ })
1099
945
  .apply();
1100
946
  Decoration.flavouredAs(FabricFlavour)
1101
947
  .for(PersistenceKeys.UPDATED_BY)
1102
- .define(onCreateUpdate(createdByOnFabricCreateUpdate), propMetadata(PersistenceKeys.UPDATED_BY, {}))
948
+ .define({
949
+ decorator: function createdBy() {
950
+ return apply(onCreateUpdate(createdByOnFabricCreateUpdate), propMetadata(PersistenceKeys.UPDATED_BY, {}));
951
+ },
952
+ })
1103
953
  .apply();
1104
954
  Decoration.flavouredAs(FabricFlavour)
1105
955
  .for(PersistenceKeys.COLUMN)