@decaf-ts/for-fabric 0.13.6 → 0.13.8

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.
@@ -8,6 +8,45 @@ exports.writeIndexes = writeIndexes;
8
8
  exports.writeDesignDocs = writeDesignDocs;
9
9
  const for_couchdb_1 = require("@decaf-ts/for-couchdb");
10
10
  const decorator_validation_1 = require("@decaf-ts/decorator-validation");
11
+ function withDefaultQueryPkTieBreaker(indexes, m) {
12
+ const pkField = decorator_validation_1.Model.pk(m);
13
+ return indexes.map((index) => {
14
+ const fields = index?.index?.fields;
15
+ if (!index?.name ||
16
+ typeof index.name !== "string" ||
17
+ !index.name.includes("defaultQuery") ||
18
+ !Array.isArray(fields) ||
19
+ fields.length === 0) {
20
+ return index;
21
+ }
22
+ const sortedFields = fields.filter((field) => typeof field === "object" && !Array.isArray(field));
23
+ if (sortedFields.length !== fields.length) {
24
+ return index;
25
+ }
26
+ const hasPk = fields.some((field) => {
27
+ if (!field || typeof field !== "object" || Array.isArray(field)) {
28
+ return false;
29
+ }
30
+ return Object.prototype.hasOwnProperty.call(field, pkField);
31
+ });
32
+ if (hasPk) {
33
+ return index;
34
+ }
35
+ const lastField = fields[fields.length - 1];
36
+ const lastDirection = String(Object.values(lastField || {})[0] || "asc").toLowerCase();
37
+ const direction = lastDirection === "desc" ? "desc" : "asc";
38
+ return Object.assign({}, index, {
39
+ index: Object.assign({}, index.index, {
40
+ fields: [
41
+ ...fields,
42
+ {
43
+ [pkField]: direction,
44
+ },
45
+ ],
46
+ }),
47
+ });
48
+ });
49
+ }
11
50
  function ensureDirectoryExistence(filePath) {
12
51
  const fs = require("fs");
13
52
  const path = require("path");
@@ -19,7 +58,7 @@ function ensureDirectoryExistence(filePath) {
19
58
  fs.mkdirSync(dirname);
20
59
  }
21
60
  function generateModelIndexes(m) {
22
- return (0, for_couchdb_1.generateIndexes)([m]);
61
+ return withDefaultQueryPkTieBreaker((0, for_couchdb_1.generateIndexes)([m]), m);
23
62
  }
24
63
  function generateModelDesignDocs(m, accum) {
25
64
  const views = (0, for_couchdb_1.generateViews)([m]);
@@ -1 +1 @@
1
- {"version":3,"file":"generation.js","sourceRoot":"","sources":["generation.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generation.js","sourceRoot":"","sources":["generation.js"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ exports.FabricContractAdapter = void 0;
4
4
  exports.createdByOnFabricCreateUpdate = createdByOnFabricCreateUpdate;
5
5
  const for_couchdb_1 = require("@decaf-ts/for-couchdb");
6
6
  const decorator_validation_1 = require("@decaf-ts/decorator-validation");
7
+ const crypto_1 = require("crypto");
7
8
  const ContractContext_js_1 = require("./ContractContext.cjs");
8
9
  const db_decorators_1 = require("@decaf-ts/db-decorators");
9
10
  const fabric_contract_api_1 = require("fabric-contract-api");
@@ -93,6 +94,32 @@ async function createdByOnFabricCreateUpdate(context, data, key, model) {
93
94
  */
94
95
  class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
95
96
  static { this.PRIVATE_BOOKMARK_PREFIX = "__dcf_pvtbm__"; }
97
+ static stableStringify(value) {
98
+ if (value === null || typeof value !== "object") {
99
+ return JSON.stringify(value);
100
+ }
101
+ if (Array.isArray(value)) {
102
+ return `[${value
103
+ .map((v) => FabricContractAdapter.stableStringify(v))
104
+ .join(",")}]`;
105
+ }
106
+ const obj = value;
107
+ return `{${Object.keys(obj)
108
+ .sort()
109
+ .map((key) => `${JSON.stringify(key)}:${FabricContractAdapter.stableStringify(obj[key])}`)
110
+ .join(",")}}`;
111
+ }
112
+ static privateQueryHash(query) {
113
+ const relevant = {
114
+ selector: query.selector || {},
115
+ sort: query.sort || [],
116
+ fields: query.fields || undefined,
117
+ use_index: query.use_index || undefined,
118
+ };
119
+ return (0, crypto_1.createHash)("sha256")
120
+ .update(FabricContractAdapter.stableStringify(relevant))
121
+ .digest("base64url");
122
+ }
96
123
  static parseSyntheticPrivateBookmark(bookmark) {
97
124
  if (typeof bookmark !== "string")
98
125
  return undefined;
@@ -105,14 +132,18 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
105
132
  typeof parsed !== "object" ||
106
133
  typeof parsed.sortField !== "string" ||
107
134
  typeof parsed.direction !== "string" ||
108
- typeof parsed.lastId !== "string") {
135
+ typeof parsed.idField !== "string" ||
136
+ typeof parsed.lastId !== "string"
137
+ || typeof parsed.queryHash !== "string") {
109
138
  return undefined;
110
139
  }
111
140
  return {
112
141
  sortField: parsed.sortField,
113
142
  direction: parsed.direction === "desc" ? "desc" : "asc",
143
+ idField: parsed.idField,
114
144
  lastValue: parsed.lastValue,
115
145
  lastId: parsed.lastId,
146
+ queryHash: parsed.queryHash,
116
147
  };
117
148
  }
118
149
  catch {
@@ -123,35 +154,87 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
123
154
  return `${FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX}${Buffer.from(JSON.stringify(cursor)).toString("base64url")}`;
124
155
  }
125
156
  static buildPrivateCursorSelector(selector, sortField, direction, cursor) {
126
- const continuation = direction === "desc"
127
- ? {
128
- $or: [
129
- { [sortField]: { $lt: cursor.lastValue } },
130
- {
131
- $and: [
132
- { [sortField]: { $eq: cursor.lastValue } },
133
- { id: { $lt: cursor.lastId } },
134
- ],
135
- },
136
- ],
137
- }
138
- : {
139
- $or: [
140
- { [sortField]: { $gt: cursor.lastValue } },
141
- {
142
- $and: [
143
- { [sortField]: { $eq: cursor.lastValue } },
144
- { id: { $gt: cursor.lastId } },
145
- ],
146
- },
147
- ],
157
+ const cmp = direction === "desc" ? "$lt" : "$gt";
158
+ if (cursor.idField === sortField) {
159
+ const continuation = {
160
+ [sortField]: {
161
+ [cmp]: cursor.lastValue,
162
+ },
163
+ };
164
+ if (!Object.keys(selector || {}).length)
165
+ return continuation;
166
+ return {
167
+ $and: [selector, continuation],
148
168
  };
169
+ }
170
+ const continuation = {
171
+ $or: [
172
+ {
173
+ [sortField]: {
174
+ [cmp]: cursor.lastValue,
175
+ },
176
+ },
177
+ {
178
+ $and: [
179
+ {
180
+ [sortField]: {
181
+ $eq: cursor.lastValue,
182
+ },
183
+ },
184
+ {
185
+ [cursor.idField]: {
186
+ [cmp]: cursor.lastId,
187
+ },
188
+ },
189
+ ],
190
+ },
191
+ ],
192
+ };
149
193
  if (!Object.keys(selector || {}).length)
150
194
  return continuation;
151
195
  return {
152
196
  $and: [selector, continuation],
153
197
  };
154
198
  }
199
+ static normalizePrivateSort(query, idField = "id") {
200
+ if (!Array.isArray(query.sort) || query.sort.length === 0) {
201
+ throw new core_1.PagingError("Private collection pagination requires an explicit Mango sort");
202
+ }
203
+ const firstSort = Object.entries(query.sort[0] || {})[0];
204
+ if (!firstSort || typeof firstSort[0] !== "string") {
205
+ throw new core_1.PagingError("Private collection pagination requires a valid first sort field");
206
+ }
207
+ const sortField = firstSort[0];
208
+ const direction = String(firstSort[1] || "asc").toLowerCase();
209
+ if (direction !== "asc" && direction !== "desc") {
210
+ throw new core_1.PagingError(`Unsupported private pagination sort direction: ${direction}`);
211
+ }
212
+ const hasIdTieBreaker = idField === sortField ||
213
+ query.sort.some((entry) => {
214
+ const [field, dir] = Object.entries(entry || {})[0] || [];
215
+ return (field === idField && String(dir || "").toLowerCase() === direction);
216
+ });
217
+ if (!hasIdTieBreaker && idField !== sortField) {
218
+ query.sort = [...query.sort, { [idField]: direction }];
219
+ }
220
+ return {
221
+ sortField,
222
+ direction: direction,
223
+ idField,
224
+ };
225
+ }
226
+ static parsePrivateResultValue(value) {
227
+ try {
228
+ const parsed = JSON.parse(value.toString("utf8"));
229
+ if (!parsed || typeof parsed !== "object") {
230
+ throw new Error("Private query result is not an object");
231
+ }
232
+ return parsed;
233
+ }
234
+ catch (e) {
235
+ throw new db_decorators_1.SerializationError(`Failed to parse private query result while building bookmark: ${e}`);
236
+ }
237
+ }
155
238
  getClient() {
156
239
  throw new core_1.UnsupportedError("Client is not supported in Fabric contracts");
157
240
  }
@@ -545,30 +628,34 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
545
628
  case "queryResultPaginated": {
546
629
  const [stub, rawInput, limit, skip, bookmark, ...args] = argsList;
547
630
  const { log } = thisArg["logCtx"](args, prop);
631
+ if (skip !== undefined && skip !== null && Number(skip) > 0) {
632
+ throw new core_1.PagingError("Private collection pagination does not support skip/offset pagination. Use the returned bookmark instead.");
633
+ }
548
634
  const pageSize = Math.max(1, Number(limit) || 250);
549
635
  const query = { ...rawInput };
550
- const sortEntry = Array.isArray(query.sort)
551
- ? Object.entries(query.sort[0] || {})[0]
552
- : undefined;
553
- const sortField = sortEntry?.[0] || "id";
554
- const sortDirection = (sortEntry?.[1] || "asc").toLowerCase();
636
+ const queryPkField = typeof query["__pkField"] === "string" &&
637
+ query["__pkField"].trim().length
638
+ ? String(query["__pkField"])
639
+ : "id";
640
+ delete query["__pkField"];
641
+ const { sortField, direction: sortDirection, idField, } = FabricContractAdapter.normalizePrivateSort(query, queryPkField);
555
642
  const syntheticCursor = FabricContractAdapter.parseSyntheticPrivateBookmark(bookmark);
556
- const hasOpaqueBookmark = bookmark !== undefined &&
643
+ if (bookmark !== undefined &&
557
644
  bookmark !== null &&
558
645
  bookmark !== "" &&
559
- typeof bookmark === "string" &&
560
- !bookmark.startsWith(FabricContractAdapter.PRIVATE_BOOKMARK_PREFIX);
561
- log.debug(`Private paginated query input collection=${collection} limit=${limit} skip=${skip} bookmark=${bookmark} sortField=${sortField} direction=${sortDirection} opaque=${hasOpaqueBookmark} synthetic=${Boolean(syntheticCursor)}`);
562
- // Private rich queries cannot be trusted with offset-based pagination.
563
- // Synthetic mode advances using a cursor on the sorted field plus the document id.
564
- query.limit = hasOpaqueBookmark ? pageSize : pageSize + 1;
646
+ !syntheticCursor) {
647
+ throw new core_1.PagingError("Private collection pagination only supports adapter-generated synthetic bookmarks");
648
+ }
649
+ log.debug(`Private paginated query input collection=${collection} limit=${limit} skip=${skip} bookmark=${bookmark} sortField=${sortField} direction=${sortDirection} synthetic=${Boolean(syntheticCursor)}`);
565
650
  delete query.skip;
566
651
  delete query.bookmark;
652
+ query.limit = pageSize + 1;
653
+ const queryHash = FabricContractAdapter.privateQueryHash(query);
567
654
  if (syntheticCursor) {
568
- query.selector = FabricContractAdapter.buildPrivateCursorSelector(query.selector || {}, syntheticCursor.sortField || sortField, syntheticCursor.direction || sortDirection, syntheticCursor);
569
- }
570
- else if (hasOpaqueBookmark) {
571
- query.bookmark = bookmark.toString();
655
+ if (syntheticCursor.queryHash !== queryHash) {
656
+ throw new core_1.PagingError("Private collection bookmark does not match the current query");
657
+ }
658
+ query.selector = FabricContractAdapter.buildPrivateCursorSelector(query.selector || {}, syntheticCursor.sortField, syntheticCursor.direction, syntheticCursor);
572
659
  }
573
660
  log.debug(`Querying collection ${collection} for ${JSON.stringify(query)}`);
574
661
  const response = await stub.getPrivateDataQueryResult(collection, JSON.stringify(query));
@@ -607,6 +694,28 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
607
694
  await iterator.close();
608
695
  }
609
696
  log.debug(`Private paginated collection=${collection} produced ${paged.length} rows hasMore=${hasMore}`);
697
+ let nextBookmark = "";
698
+ if (hasMore && paged.length) {
699
+ const last = paged[paged.length - 1];
700
+ const lastDoc = FabricContractAdapter.parsePrivateResultValue(last.value);
701
+ if (!(sortField in lastDoc)) {
702
+ throw new core_1.PagingError(`Cannot build private pagination bookmark: sorted field "${sortField}" is missing from the last result`);
703
+ }
704
+ const lastIdValue = lastDoc[idField];
705
+ if (lastIdValue === undefined ||
706
+ lastIdValue === null) {
707
+ throw new core_1.PagingError(`Cannot build private pagination bookmark: id field "${idField}" is missing from the last result`);
708
+ }
709
+ nextBookmark =
710
+ FabricContractAdapter.buildSyntheticPrivateBookmark({
711
+ sortField,
712
+ direction: sortDirection,
713
+ idField,
714
+ lastValue: lastDoc[sortField],
715
+ lastId: String(lastIdValue),
716
+ queryHash,
717
+ });
718
+ }
610
719
  // Wrap the page in an async iterator for resultIterator()
611
720
  let idx = 0;
612
721
  const arrayIterator = {
@@ -622,32 +731,7 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
622
731
  iterator: arrayIterator,
623
732
  metadata: {
624
733
  fetchedRecordsCount: paged.length,
625
- bookmark: typeof responseMetadata.bookmark === "string" &&
626
- responseMetadata.bookmark
627
- ? responseMetadata.bookmark
628
- : hasMore && paged.length
629
- ? FabricContractAdapter.buildSyntheticPrivateBookmark({
630
- sortField,
631
- direction: sortDirection,
632
- lastValue: (() => {
633
- try {
634
- return JSON.parse(paged[paged.length - 1].value.toString("utf8"))?.[sortField];
635
- }
636
- catch {
637
- return undefined;
638
- }
639
- })(),
640
- lastId: (() => {
641
- try {
642
- const lastDoc = JSON.parse(paged[paged.length - 1].value.toString("utf8"));
643
- return String(lastDoc?.id || paged[paged.length - 1].key);
644
- }
645
- catch {
646
- return String(paged[paged.length - 1].key);
647
- }
648
- })(),
649
- })
650
- : "",
734
+ bookmark: nextBookmark,
651
735
  },
652
736
  };
653
737
  }
@@ -850,46 +934,52 @@ class FabricContractAdapter extends for_couchdb_1.CouchDBAdapter {
850
934
  const { log, ctx, ctxArgs } = this.logCtx(args, this.raw);
851
935
  const enableSegregates = !args.length || args[0] !== true;
852
936
  const fullySegregated = enableSegregates && ctx.isFullySegregated;
853
- const { skip, limit } = rawInput;
854
- const bookmark = rawInput["bookmark"];
855
- const paginationActive = Boolean(limit || skip || bookmark);
937
+ const originalInput = { ...rawInput };
938
+ const { skip, limit } = originalInput;
939
+ const bookmark = originalInput["bookmark"];
940
+ const pkField = typeof originalInput["__pkField"] === "string" &&
941
+ originalInput["__pkField"].trim().length
942
+ ? String(originalInput["__pkField"])
943
+ : "id";
944
+ const hasSkip = skip !== undefined && skip !== null && Number(skip) > 0;
945
+ const hasBookmark = bookmark !== undefined && bookmark !== null && bookmark !== "";
946
+ const paginationActive = Boolean(limit || hasSkip || hasBookmark);
947
+ const shouldPaginate = Boolean(paginationActive && (enableSegregates || hasSkip || hasBookmark));
948
+ const baseInput = { ...originalInput };
949
+ delete baseInput["skip"];
950
+ delete baseInput["bookmark"];
951
+ delete baseInput["__pkField"];
856
952
  let resp = { docs: [], bookmark: undefined };
857
- log.debug(`raw query start fullySegregated=${fullySegregated} enableSegregates=${enableSegregates} paginationActive=${paginationActive} limit=${limit} skip=${skip} bookmark=${bookmark} query=${JSON.stringify(rawInput)}`);
953
+ log.debug(`raw query start fullySegregated=${fullySegregated} enableSegregates=${enableSegregates} paginationActive=${paginationActive} limit=${limit} skip=${skip} bookmark=${bookmark} pkField=${pkField} query=${JSON.stringify(originalInput)}`);
858
954
  // Query public state only when the model is NOT fully segregated
859
955
  if (!fullySegregated) {
860
956
  let iterator;
861
- if (limit || skip) {
862
- delete rawInput["limit"];
863
- delete rawInput["skip"];
864
- log.debug(`Retrieving public paginated iterator: limit: ${limit}/ skip: ${skip}`);
865
- const response = (await this.queryResultPaginated(ctx.stub, rawInput, limit || Number.MAX_VALUE, skip?.toString(), bookmark, ...[ctx]));
957
+ if (shouldPaginate) {
958
+ const paginatedInput = { ...baseInput };
959
+ delete paginatedInput["limit"];
960
+ log.debug(`Retrieving public paginated iterator: limit: ${limit}/ skip: ${skip} bookmark=${bookmark}`);
961
+ const response = (await this.queryResultPaginated(ctx.stub, paginatedInput, limit || 250, skip?.toString(), bookmark, ...[ctx]));
866
962
  resp.bookmark = response.metadata.bookmark;
867
963
  iterator = response.iterator;
868
964
  log.debug(`Retrieved public paging iterator`);
869
- log.debug(`public paginated response bookmark=${resp.bookmark} query=${JSON.stringify(rawInput)}`);
965
+ log.debug(`public paginated response bookmark=${resp.bookmark} query=${JSON.stringify(paginatedInput)}`);
870
966
  }
871
967
  else {
872
968
  log.debug("Retrieving listing public iterator");
873
- iterator = (await this.queryResult(ctx.stub, rawInput, ctx));
969
+ iterator = (await this.queryResult(ctx.stub, { ...baseInput }, ctx));
874
970
  }
875
971
  log.debug(`Retrieved public listing iterator`);
876
972
  resp.docs = (await this.resultIterator(log, iterator));
877
973
  log.debug(`returning ${Array.isArray(resp.docs) ? resp.docs.length : 1} results`);
878
974
  }
879
975
  else {
880
- // For fully segregated models, strip pagination fields from rawInput
881
- // so the segregated query below can re-apply them cleanly
882
- if (limit || skip) {
883
- delete rawInput["limit"];
884
- delete rawInput["skip"];
885
- }
886
976
  log.debug("Skipping public state query (fully segregated model)");
887
977
  }
888
978
  const collections = enableSegregates ? ctx.getReadCollections() : undefined;
889
979
  log.debug(`read collections for raw query: ${JSON.stringify(collections || [])}`);
890
980
  if (collections && collections.length) {
891
981
  // Build a fresh input with limit/skip/bookmark restored
892
- const segregatedInput = { ...rawInput };
982
+ const segregatedInput = { ...baseInput };
893
983
  if (limit)
894
984
  segregatedInput.limit = limit;
895
985
  if (skip)