@futdevpro/nts-dynamo 1.15.135 → 1.15.136

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.
@@ -1,7 +1,7 @@
1
1
  {
2
- "pid": 31045,
3
- "startedAt": "2026-08-12T05:50:51.284Z",
4
- "startedAtMs": 1786513851284,
2
+ "pid": 10907,
3
+ "startedAt": "2026-08-14T03:44:08.561Z",
4
+ "startedAtMs": 1786679048561,
5
5
  "phase": "npm-publish",
6
6
  "pipelineComplete": false,
7
7
  "serverRunning": false,
@@ -10,63 +10,63 @@
10
10
  "steps": {
11
11
  "validate-secrets": {
12
12
  "status": "ok",
13
- "startedAt": "2026-08-12T05:50:51.302Z",
13
+ "startedAt": "2026-08-14T03:44:08.862Z",
14
14
  "logLineStart": 2,
15
15
  "logLineEnd": 8,
16
- "durationSec": 2.4
16
+ "durationSec": 0.3
17
17
  },
18
18
  "discord-start": {
19
19
  "status": "ok",
20
- "startedAt": "2026-08-12T05:50:54.731Z",
20
+ "startedAt": "2026-08-14T03:44:10.157Z",
21
21
  "logLineStart": 9,
22
22
  "logLineEnd": 14,
23
- "durationSec": 5.3
23
+ "durationSec": 3.5
24
24
  },
25
25
  "pre-flight": {
26
26
  "status": "ok",
27
- "startedAt": "2026-08-12T05:51:01.033Z",
27
+ "startedAt": "2026-08-14T03:44:14.662Z",
28
28
  "logLineStart": 15,
29
29
  "logLineEnd": 17,
30
- "durationSec": 2.3
30
+ "durationSec": 0.2
31
31
  },
32
32
  "install": {
33
33
  "status": "ok",
34
- "startedAt": "2026-08-12T05:51:04.333Z",
34
+ "startedAt": "2026-08-14T03:44:15.872Z",
35
35
  "logLineStart": 18,
36
- "logLineEnd": 144,
37
- "durationSec": 66.9
36
+ "logLineEnd": 102,
37
+ "durationSec": 29.3
38
38
  },
39
39
  "check-dev-leftovers": {
40
40
  "status": "ok",
41
- "startedAt": "2026-08-12T05:52:12.241Z",
42
- "logLineStart": 145,
43
- "logLineEnd": 152,
44
- "durationSec": 7.1
41
+ "startedAt": "2026-08-14T03:44:46.167Z",
42
+ "logLineStart": 103,
43
+ "logLineEnd": 110,
44
+ "durationSec": 5.5
45
45
  },
46
46
  "build": {
47
47
  "status": "ok",
48
- "startedAt": "2026-08-12T05:52:20.365Z",
49
- "logLineStart": 153,
50
- "logLineEnd": 154,
51
- "durationSec": 29.2
48
+ "startedAt": "2026-08-14T03:44:52.653Z",
49
+ "logLineStart": 111,
50
+ "logLineEnd": 112,
51
+ "durationSec": 26.6
52
52
  },
53
53
  "test": {
54
54
  "status": "ok",
55
- "startedAt": "2026-08-12T05:52:50.574Z",
56
- "logLineStart": 155,
57
- "logLineEnd": 2956,
58
- "durationSec": 28.5,
55
+ "startedAt": "2026-08-14T03:45:20.233Z",
56
+ "logLineStart": 113,
57
+ "logLineEnd": 2856,
58
+ "durationSec": 32.1,
59
59
  "testSummary": {
60
- "totalSpecs": 1639,
60
+ "totalSpecs": 1644,
61
61
  "failures": 0,
62
- "passed": 1639,
63
- "testDurationSec": 7.28
62
+ "passed": 1644,
63
+ "testDurationSec": 11.563
64
64
  }
65
65
  },
66
66
  "npm-publish": {
67
67
  "status": "running",
68
- "startedAt": "2026-08-12T05:53:20.091Z",
69
- "logLineStart": 2957,
68
+ "startedAt": "2026-08-14T03:45:53.429Z",
69
+ "logLineStart": 2857,
70
70
  "logLineEnd": null,
71
71
  "durationSec": null
72
72
  }
@@ -0,0 +1,124 @@
1
+ # 🔴 `DyNTS_FieldEncryption_Util._transform` spread-elte a Mongoose-Documentet — a lista-endpointok burkot adtak vissza
2
+
3
+ **Dátum:** 2026-08-14 · **Kérő:** `BFR-MASTERPROMPTER-027` · **Súlyosság:** high (funkcionálisan törött lista-UI)
4
+
5
+ ---
6
+
7
+ ## 1. A hiba
8
+
9
+ ```ts
10
+ // field-encryption.util.ts — ELŐTTE
11
+ const result: any = Array.isArray(value) ? [ ...(value as any[]) ] : { ...(value as any) };
12
+ ```
13
+
14
+ Egy mongoose-**Document** adatai **nem saját mezők** — a belső `_doc`-ban ülnek. Ezért a `{ ...doc }`
15
+ a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény **már nem Document**, tehát a
16
+ `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így ezt kapja:
17
+
18
+ ```json
19
+ [{ "$__": {...}, "$isNew": false, "_doc": { "_id": "…", "name": "…" } }]
20
+ ```
21
+
22
+ `_id` a legfelső szinten **nincs** → a fogyasztó minden id-alapú művelete elhasal.
23
+
24
+ ### Élesben mért bizonyíték (`test.master-prompter.hu`, 2026-08-14)
25
+
26
+ | endpoint | státusz | első elem KULCSAI | `_id` |
27
+ |---|---|---|---|
28
+ | `GET /api/flow/flow/get/:userId/:flowId` (egyedi) | 200 | `_id`, `userId`, `ownerId`, `name`, … | ✅ |
29
+ | `GET /api/flow/flow/get-all-my/:userId` (**lista**) | 200 | **`$__`, `$isNew`, `_doc`** | ❌ **undefined** |
30
+
31
+ Kliens-oldali tünet: a lista-kártyák linkje `/editor/undefined`, a törlés
32
+ `DyNX_ApiService call "deleteFlow" ERROR: missing pathParam: "flowId"` dialógussal állt meg.
33
+
34
+ ---
35
+
36
+ ## 2. Miért CSAK a lista tört el — a mért aszimmetria
37
+
38
+ A `DyNTS_DBService` **egyedi** olvasásai már ma is `toObject()`-tel adnak vissza sima objektumot
39
+ (`getDataById`, `getDataByDependencyId`, `findByIdAndUpdate`, `findOneAndUpdate`, `createData`).
40
+ A **lista/find metódusok NYERS Documentet** adnak tovább — kimérve **11 db**:
41
+
42
+ `getDataListByIds` · `getDataListByDependencyId` · `getDataListByDependencyIds` · `getAll` ·
43
+ `getDeletedDataList` · `trueDeleteAllData` · `searchData` · `findOne` · `find` · `findWithPaging` ·
44
+ (`findLeanBatched` — ez `.lean()`-nel már sima objektumot ad)
45
+
46
+ Titkosítás **nélkül** ez ártalmatlan: a nyers Document a `res.send`-en át a `toJSON()`-nal helyesen
47
+ szerializálódik. `encrypt: true` mező esetén viszont a read-post-processor (`stringifyDataId` →
48
+ `decryptDoc` → `_transform`) **spread-eli** a Documentet, és ezzel elrontja.
49
+
50
+ ---
51
+
52
+ ## 3. A javítás
53
+
54
+ ```ts
55
+ // UTÁNA
56
+ const plain: any = this._toPlainIfDocument(value);
57
+ const result: any = Array.isArray(plain) ? [ ...(plain as any[]) ] : { ...(plain as any) };
58
+ ```
59
+
60
+ `_toPlainIfDocument` a `$__` **saját mező** + `toJSON` függvény kettősével azonosít egy Documentet
61
+ (pontosan az, ami a hibás payloadba kiszivárgott — mongoose-import nélküli, pontos diszkriminátor),
62
+ és `toJSON()`-t hív rá.
63
+
64
+ **Miért itt, és nem a 11 DB-metódusban:**
65
+ 1. Ez az **egy pont** mind a 11 utat rendezi.
66
+ 2. A 11 metódus visszatérési **típusát** megváltoztatni (Document → sima objektum) **minden modell
67
+ minden fogyasztóját** érintené. Az itteni javítás viszont **csak az `encrypt: true` modelleket**
68
+ érinti — ahol a `_transform` amúgy is **már ma is** sima (csak **rossz**) objektumot ad vissza.
69
+ ⇒ **a változás nem tud elrontani olyat, ami ma működik.**
70
+ 3. `toJSON()` (nem `toObject()`): a titkosítás bevezetése ELŐTT a nyers Document a `res.send`-en át a
71
+ `JSON.stringify` → `toJSON()` úton szerializálódott — ez adja vissza **ugyanazt a kimeneti alakot**
72
+ (schema-transzformok, virtuals, `__v` együtt).
73
+
74
+ ---
75
+
76
+ ## 4. 🔴 BLAST-RADIUS — a flotta érintettsége
77
+
78
+ A defekt akkor él, ha **mind a három** teljesül: (a) a modellnek van `encrypt: true` mezője ·
79
+ (b) a `FDP_CORE_DBCONTENT_CRYPT_KEY` be van állítva (kulcs nélkül a `decryptDoc` korán visszatér) ·
80
+ (c) az olvasás **lista-metóduson** megy.
81
+
82
+ | Projekt | Modell | Titkosított mezők | Állapot |
83
+ |---|---|---|---|
84
+ | **master-prompter** | `flow` | `flowSteps.prompt` | 🔴 **élesben igazoltan törött** |
85
+ | **master-prompter** | `flow-run` | `results`, `sourceFlow`, `flowRunValues.value` | 🔴 ugyanaz az osztály |
86
+ | **adventor** | `campaign` | `storyState`, `memory`, `epilogue` | ⚠️ ugyanaz az osztály — élesben nem mérve |
87
+ | **adventor** | `generation` | `content`, `prompt` | ⚠️ ugyanaz az osztály — élesben nem mérve |
88
+ | **fdp-token-service** | `token-purchase` | `requestIp`, `euVatNumber` (PII) | ⚠️ ugyanaz az osztály — élesben nem mérve |
89
+ | **fdp-token-service** | `manual-invoice` | `realID`, `title`, `fullName`, `companyName`, … | ⚠️ ugyanaz az osztály — élesben nem mérve |
90
+
91
+ ⇒ **6 modell, 3 projekt.** A master-prompteren kívül egyik sincs élesben visszamérve — ezeket a
92
+ projektek `nts-dynamo`-bumpja után kell ellenőrizni (a lista-endpointjaik `_id`-t adnak-e).
93
+ A `manual-invoice` és a `token-purchase` külön figyelmet érdemel: számlázási/PII adat.
94
+
95
+ > **Miért nem tűnt fel eddig:** a szerver **200**-at ad, a válasz JSON-ként **érvényes** — csak épp a
96
+ > burkot tartalmazza. A UI „üresnek"/működésképtelennek látszik, de sehol nincs hibajelzés.
97
+ > Az MP-ben azért derült ki, mert az `e2e-feature` step (a 30m→60m timeout-emelés után) **most futott
98
+ > le először végig**.
99
+
100
+ ---
101
+
102
+ ## 5. Kontroll-mérés — VALÓDI mongoose-Documenttel
103
+
104
+ A regresszió-őr **szándékosan nem mockkal** dolgozik: a hiba pont abból eredt, hogy a Document adatai
105
+ nem saját mezők — egy kézzel gyártott mock ezt a jellemzőt nem hordozza, tehát a mock-alapú teszt
106
+ **akkor is zöld maradna**, ha a hiba visszakerülne. DB-kapcsolat nem kell: a `new Model({...})`
107
+ offline is valódi Documentet ad.
108
+
109
+ | Eset | A fix nélkül |
110
+ |---|---|
111
+ | a visszafejtett eredmény sima objektum (nincs `$__`/`$isNew`/`_doc`) | ❌ `Expected [ '$__', '_doc' ] not to contain '$__'` |
112
+ | az `_id` megmarad | ❌ `az _id elveszett a transzform során: Expected undefined to be defined` |
113
+ | a JSON-szerializált alak a valódi adatot adja | ❌ `… not to contain '_doc'` |
114
+ | Document-**tömb** (lista-alak) minden eleme sima objektum, `_id`-vel | ❌ `… not to contain '_doc'` |
115
+ | **összesen** | ❌ **4 FAILED** → javítva: ✅ **1644 specs, 0 failures** |
116
+
117
+ A mezők helyes visszafejtését is asszertáljuk Documenten (top-level + beágyazott tömb-elem).
118
+
119
+ ---
120
+
121
+ ## 6. Adopció
122
+
123
+ `@futdevpro/nts-dynamo` bump minden érintett szerver-projektben (master-prompter, adventor,
124
+ fdp-token-service), majd a lista-endpointok visszamérése (`_id` jelen van-e a válaszban).
@@ -42,10 +42,44 @@ export declare class DyNTS_FieldEncryption_Util {
42
42
  * (a read ne törjön el) — az envelope-értékek visszafejtetlenül maradnak (a hívó látja + a hiba loggolt).
43
43
  */
44
44
  static decryptDoc<T>(data: T, properties?: DyFM_DataProperties<any>): T;
45
+ /**
46
+ * Egy mongoose-**Document** sima objektummá alakítása (más érték változatlanul jön vissza).
47
+ *
48
+ * @description 🔴 **MIÉRT KELL** (BFR-MASTERPROMPTER-027, 2026-08-14): a `_transform` sekély spreaddel
49
+ * klónoz. Egy mongoose-Documentnek viszont az adatok NEM saját mezői — azok a belső `_doc`-ban
50
+ * ülnek —, ezért a `{ ...doc }` a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény
51
+ * **már nem Document**, tehát a `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így
52
+ * `{"$__":…,"$isNew":…,"_doc":{…}}`-t kap `_id` nélkül.
53
+ *
54
+ * **Élesben mérve** (`test.master-prompter.hu`, 2026-08-14): a `flow` lista-endpoint válaszának
55
+ * első elemén a kulcsok `["$__","$isNew","_doc"]`, `_id: undefined` → a kliens-oldali linkek
56
+ * `/editor/undefined`-ra mutattak, a törlés `missing pathParam: "flowId"`-vel állt meg. Az EGYEDI
57
+ * get ugyanakkor ép volt — mert a `DyNTS_DBService` az egyedi olvasásoknál **már `toObject()`-et**
58
+ * hív (`db.service.ts` getDataById / getDataByDependencyId / findByIdAndUpdate / findOneAndUpdate),
59
+ * a **lista-metódusok (11 db) viszont NYERS Documentet adnak tovább**. Titkosítás nélküli
60
+ * modelleknél ez ártalmatlan (a `res.send` a `toJSON()`-on át szerializál), `encrypt:true` mező
61
+ * esetén viszont a spread elrontja.
62
+ *
63
+ * **Miért ITT javítjuk, és nem a 11 DB-metódusban:** (1) ez az EGY pont mind a 11 utat rendezi;
64
+ * (2) a 11 metódus visszatérési TÍPUSÁT megváltoztatni (Document → sima objektum) minden modell
65
+ * minden fogyasztóját érintené — az itteni javítás viszont **csak az `encrypt:true` modelleket**
66
+ * érinti, ahol a `_transform` amúgy is **már ma is** sima (csak ROSSZ) objektumot ad vissza.
67
+ * Vagyis a változás nem tud elrontani olyat, ami ma működik.
68
+ *
69
+ * `toJSON()`-t használunk (nem `toObject()`-et): a titkosítás bevezetése ELŐTT a nyers Document a
70
+ * `res.send`-en át a `JSON.stringify` → `toJSON()` úton szerializálódott, tehát ez adja vissza
71
+ * **bitre ugyanazt a kimeneti alakot** (schema-transzformok, virtuals, `__v`-kezelés együtt).
72
+ *
73
+ * @param value tetszőleges érték
74
+ * @returns a Document sima alakja, vagy az eredeti érték
75
+ */
76
+ private static _toPlainIfDocument;
45
77
  /**
46
78
  * Rekurzív per-field walk: az `encrypt:true` mezőket titkosítja/visszafejti, a `subObjectParams`-os
47
79
  * mezőkbe (objektum VAGY objektum-tömb) leszáll. Immutábilis: minden érintett konténert sekélyen
48
80
  * klónoz (az eredetit nem mutálja); a nem-érintett ágakat referencia szerint megosztja (read-only).
81
+ *
82
+ * ⚠️ A klónozás ELŐTT a mongoose-Documentek sima objektummá alakulnak — lásd {@link _toPlainIfDocument}.
49
83
  */
50
84
  private static _transform;
51
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"field-encryption.util.d.ts","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAEpB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA0B;IACrC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAkC;IACjE,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAA0C;IAEjF,+EAA+E;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAA2C;IAE/E,mEAAmE;IACnE,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB,4CAA4C;IAC5C,MAAM,CAAC,aAAa,IAAI,MAAM;IAK9B;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,OAAO;IA8BzE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAmBvE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAavE;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAkD1B"}
1
+ {"version":3,"file":"field-encryption.util.d.ts","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAEpB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA0B;IACrC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAkC;IACjE,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAA0C;IAEjF,+EAA+E;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAA2C;IAE/E,mEAAmE;IACnE,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB,4CAA4C;IAC5C,MAAM,CAAC,aAAa,IAAI,MAAM;IAK9B;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,OAAO;IA8BzE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAmBvE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAavE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAejC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAmD1B"}
@@ -99,16 +99,61 @@ class DyNTS_FieldEncryption_Util {
99
99
  }
100
100
  return this._transform(data, properties, 'decrypt', key, this.getKeyVersion());
101
101
  }
102
+ /**
103
+ * Egy mongoose-**Document** sima objektummá alakítása (más érték változatlanul jön vissza).
104
+ *
105
+ * @description 🔴 **MIÉRT KELL** (BFR-MASTERPROMPTER-027, 2026-08-14): a `_transform` sekély spreaddel
106
+ * klónoz. Egy mongoose-Documentnek viszont az adatok NEM saját mezői — azok a belső `_doc`-ban
107
+ * ülnek —, ezért a `{ ...doc }` a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény
108
+ * **már nem Document**, tehát a `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így
109
+ * `{"$__":…,"$isNew":…,"_doc":{…}}`-t kap `_id` nélkül.
110
+ *
111
+ * **Élesben mérve** (`test.master-prompter.hu`, 2026-08-14): a `flow` lista-endpoint válaszának
112
+ * első elemén a kulcsok `["$__","$isNew","_doc"]`, `_id: undefined` → a kliens-oldali linkek
113
+ * `/editor/undefined`-ra mutattak, a törlés `missing pathParam: "flowId"`-vel állt meg. Az EGYEDI
114
+ * get ugyanakkor ép volt — mert a `DyNTS_DBService` az egyedi olvasásoknál **már `toObject()`-et**
115
+ * hív (`db.service.ts` getDataById / getDataByDependencyId / findByIdAndUpdate / findOneAndUpdate),
116
+ * a **lista-metódusok (11 db) viszont NYERS Documentet adnak tovább**. Titkosítás nélküli
117
+ * modelleknél ez ártalmatlan (a `res.send` a `toJSON()`-on át szerializál), `encrypt:true` mező
118
+ * esetén viszont a spread elrontja.
119
+ *
120
+ * **Miért ITT javítjuk, és nem a 11 DB-metódusban:** (1) ez az EGY pont mind a 11 utat rendezi;
121
+ * (2) a 11 metódus visszatérési TÍPUSÁT megváltoztatni (Document → sima objektum) minden modell
122
+ * minden fogyasztóját érintené — az itteni javítás viszont **csak az `encrypt:true` modelleket**
123
+ * érinti, ahol a `_transform` amúgy is **már ma is** sima (csak ROSSZ) objektumot ad vissza.
124
+ * Vagyis a változás nem tud elrontani olyat, ami ma működik.
125
+ *
126
+ * `toJSON()`-t használunk (nem `toObject()`-et): a titkosítás bevezetése ELŐTT a nyers Document a
127
+ * `res.send`-en át a `JSON.stringify` → `toJSON()` úton szerializálódott, tehát ez adja vissza
128
+ * **bitre ugyanazt a kimeneti alakot** (schema-transzformok, virtuals, `__v`-kezelés együtt).
129
+ *
130
+ * @param value tetszőleges érték
131
+ * @returns a Document sima alakja, vagy az eredeti érték
132
+ */
133
+ static _toPlainIfDocument(value) {
134
+ // A `$__` a mongoose-Document SAJÁT mezője — pontosan ez szivárgott ki a hibás payloadba,
135
+ // ezért ez a legpontosabb (és mongoose-import nélküli) diszkriminátor.
136
+ if (value != null
137
+ && typeof value === 'object'
138
+ && typeof value.toJSON === 'function'
139
+ && Object.prototype.hasOwnProperty.call(value, '$__')) {
140
+ return value.toJSON();
141
+ }
142
+ return value;
143
+ }
102
144
  /**
103
145
  * Rekurzív per-field walk: az `encrypt:true` mezőket titkosítja/visszafejti, a `subObjectParams`-os
104
146
  * mezőkbe (objektum VAGY objektum-tömb) leszáll. Immutábilis: minden érintett konténert sekélyen
105
147
  * klónoz (az eredetit nem mutálja); a nem-érintett ágakat referencia szerint megosztja (read-only).
148
+ *
149
+ * ⚠️ A klónozás ELŐTT a mongoose-Documentek sima objektummá alakulnak — lásd {@link _toPlainIfDocument}.
106
150
  */
107
151
  static _transform(value, properties, mode, key, keyVersion) {
108
152
  if (value == null || typeof value !== 'object') {
109
153
  return value;
110
154
  }
111
- const result = Array.isArray(value) ? [...value] : { ...value }; // ccap-review-disable-line no-any-type
155
+ const plain = this._toPlainIfDocument(value); // ccap-review-disable-line no-any-type
156
+ const result = Array.isArray(plain) ? [...plain] : { ...plain }; // ccap-review-disable-line no-any-type
112
157
  for (const propKey in properties) {
113
158
  const prop = properties[propKey]; // ccap-review-disable-line no-any-type
114
159
  if (!prop) {
@@ -1 +1 @@
1
- {"version":3,"file":"field-encryption.util.js","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field-encryption.util.js","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futdevpro/nts-dynamo",
3
- "version": "01.15.135",
3
+ "version": "01.15.136",
4
4
  "description": "Dynamic NodeTS (NodeJS-Typescript), MongoDB Backend System Framework by Future Development Program Ltd.",
5
5
  "DyBu_settings": {
6
6
  "packageType": "server-package",
@@ -378,7 +378,7 @@
378
378
  "@anthropic-ai/sdk": "~0.39.0",
379
379
  "@modelcontextprotocol/sdk": "~1.29.0",
380
380
  "better-sqlite3": "~12.4.1",
381
- "@futdevpro/fsm-dynamo": "1.16.37",
381
+ "@futdevpro/fsm-dynamo": "1.16.38",
382
382
  "@types/express": "4.17.21",
383
383
  "@types/geoip-lite": "~1.4.1",
384
384
  "@types/node": "~24.1.0",
@@ -412,7 +412,7 @@
412
412
  }
413
413
  },
414
414
  "devDependencies": {
415
- "@futdevpro/dynamo-eslint": "1.15.22",
415
+ "@futdevpro/dynamo-eslint": "1.15.25",
416
416
  "@discordjs/opus": "^0.10.0",
417
417
  "@discordjs/voice": "^0.18.0",
418
418
  "@types/jasmine": "~4.3.5",
@@ -1,3 +1,5 @@
1
+ import { Schema, model } from 'mongoose';
2
+
1
3
  import { DyFM_DataProperties } from '@futdevpro/fsm-dynamo';
2
4
  import { DyFM_Crypto } from '@futdevpro/fsm-dynamo/crypto';
3
5
 
@@ -106,4 +108,88 @@ describe('| DyNTS_FieldEncryption_Util', () => {
106
108
  const dec: any = DyNTS_FieldEncryption_Util.decryptDoc(enc, encProps);
107
109
  expect(dec.secret).toBe(enc.secret); // envelope marad, nincs throw
108
110
  });
111
+
112
+ // ─── 🔴 MONGOOSE-DOCUMENT (BFR-MASTERPROMPTER-027) ─────────────────────────────────────────
113
+ //
114
+ // A regresszio-or SZANDEKOSAN VALODI mongoose-Documenttel dolgozik (nem mockkal): a hiba pont abbol
115
+ // eredt, hogy egy Document adatai NEM sajat mezok (a `_doc`-ban ulnek), ezert a `{ ...doc }` a belso
116
+ // burkot masolja. Egy kezzel gyartott mock ezt a jellemzot nem hordozza — vagyis a mock-alapu teszt
117
+ // AKKOR IS zold maradna, ha a hiba visszakerulne.
118
+ //
119
+ // DB-kapcsolat NEM kell: a `new Model({...})` offline is valodi Documentet ad.
120
+ describe('| mongoose-Document bemenet (BFR-MASTERPROMPTER-027)', () => {
121
+ const stepSchema: Schema = new Schema({ prompt: String, label: String }, { _id: false });
122
+ const flowSchema: Schema = new Schema({ name: String, secret: String, steps: [ stepSchema ] });
123
+ const FlowModel = model('EncUtilSpecFlow', flowSchema);
124
+
125
+ function newDocument(): any { // ccap-review-disable-line no-any-type
126
+ return new FlowModel({
127
+ name: 'my flow',
128
+ secret: DyFM_Crypto.encryptDbValue('titok', KEY, 1),
129
+ steps: [ { prompt: DyFM_Crypto.encryptDbValue('lepes-prompt', KEY, 1), label: 'A' } ],
130
+ });
131
+ }
132
+
133
+ it('| a visszafejtett eredmeny SIMA objektum — NINCS benne $__ / $isNew / _doc', () => {
134
+ const doc: any = newDocument(); // ccap-review-disable-line no-any-type
135
+
136
+ const result: any = DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps); // ccap-review-disable-line no-any-type
137
+
138
+ expect(Object.keys(result)).not.toContain('$__');
139
+ expect(Object.keys(result)).not.toContain('$isNew');
140
+ expect(Object.keys(result)).not.toContain('_doc');
141
+ });
142
+
143
+ /** ⚠️ EZ a fazis lenyegi kontroll-assertje: a kliens ezt az `_id`-t nem kapta meg. */
144
+ it('| az `_id` MEGMARAD (a kliens enelkul nem tud torolni/megnyitni)', () => {
145
+ const doc: any = newDocument(); // ccap-review-disable-line no-any-type
146
+
147
+ const result: any = DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps); // ccap-review-disable-line no-any-type
148
+
149
+ expect(result._id).withContext('az _id elveszett a transzform soran').toBeDefined();
150
+ expect(`${result._id}`).toBe(`${doc._id}`);
151
+ });
152
+
153
+ it('| az adat-mezok es a visszafejtes helyesek Documenten is', () => {
154
+ const doc: any = newDocument(); // ccap-review-disable-line no-any-type
155
+
156
+ const result: any = DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps); // ccap-review-disable-line no-any-type
157
+
158
+ expect(result.name).toBe('my flow');
159
+ expect(result.secret).withContext('a top-level enc-mezo nem fejtodott vissza').toBe('titok');
160
+ expect(result.steps[0].prompt).withContext('a beagyazott enc-mezo nem fejtodott vissza')
161
+ .toBe('lepes-prompt');
162
+ expect(result.steps[0].label).toBe('A');
163
+ });
164
+
165
+ /**
166
+ * A JSON-korut is merjuk: a hiba VEGSO tunete az volt, hogy a `res.send` a burkot szerializalta.
167
+ * Ez az assert azt a kimenetet nezi, amit a kliens TENYLEGESEN megkap.
168
+ */
169
+ it('| a JSON-szerializalt alak a valodi adatot adja (a kliens ezt kapja)', () => {
170
+ const doc: any = newDocument(); // ccap-review-disable-line no-any-type
171
+
172
+ const serialized: Record<string, unknown> =
173
+ JSON.parse(JSON.stringify(DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps)));
174
+
175
+ expect(serialized['_id']).toBeDefined();
176
+ expect(serialized['name']).toBe('my flow');
177
+ expect(Object.keys(serialized)).not.toContain('_doc');
178
+ });
179
+
180
+ it('| Document-TOMB (lista-endpoint alak) minden eleme sima objektum, _id-vel', () => {
181
+ const list: any[] = [ newDocument(), newDocument() ]; // ccap-review-disable-line no-any-type
182
+
183
+ const results: any[] = list.map( // ccap-review-disable-line no-any-type
184
+ (d: any): any => DyNTS_FieldEncryption_Util.decryptDoc(d, encProps), // ccap-review-disable-line no-any-type
185
+ );
186
+
187
+ results.forEach((r: any): void => { // ccap-review-disable-line no-any-type
188
+ expect(r._id).toBeDefined();
189
+ expect(Object.keys(r)).not.toContain('_doc');
190
+ expect(r.secret).toBe('titok');
191
+ });
192
+ });
193
+ });
194
+
109
195
  });
@@ -115,10 +115,58 @@ export class DyNTS_FieldEncryption_Util {
115
115
  return this._transform(data, properties as DyFM_DataProperties<any>, 'decrypt', key, this.getKeyVersion());
116
116
  }
117
117
 
118
+ /**
119
+ * Egy mongoose-**Document** sima objektummá alakítása (más érték változatlanul jön vissza).
120
+ *
121
+ * @description 🔴 **MIÉRT KELL** (BFR-MASTERPROMPTER-027, 2026-08-14): a `_transform` sekély spreaddel
122
+ * klónoz. Egy mongoose-Documentnek viszont az adatok NEM saját mezői — azok a belső `_doc`-ban
123
+ * ülnek —, ezért a `{ ...doc }` a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény
124
+ * **már nem Document**, tehát a `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így
125
+ * `{"$__":…,"$isNew":…,"_doc":{…}}`-t kap `_id` nélkül.
126
+ *
127
+ * **Élesben mérve** (`test.master-prompter.hu`, 2026-08-14): a `flow` lista-endpoint válaszának
128
+ * első elemén a kulcsok `["$__","$isNew","_doc"]`, `_id: undefined` → a kliens-oldali linkek
129
+ * `/editor/undefined`-ra mutattak, a törlés `missing pathParam: "flowId"`-vel állt meg. Az EGYEDI
130
+ * get ugyanakkor ép volt — mert a `DyNTS_DBService` az egyedi olvasásoknál **már `toObject()`-et**
131
+ * hív (`db.service.ts` getDataById / getDataByDependencyId / findByIdAndUpdate / findOneAndUpdate),
132
+ * a **lista-metódusok (11 db) viszont NYERS Documentet adnak tovább**. Titkosítás nélküli
133
+ * modelleknél ez ártalmatlan (a `res.send` a `toJSON()`-on át szerializál), `encrypt:true` mező
134
+ * esetén viszont a spread elrontja.
135
+ *
136
+ * **Miért ITT javítjuk, és nem a 11 DB-metódusban:** (1) ez az EGY pont mind a 11 utat rendezi;
137
+ * (2) a 11 metódus visszatérési TÍPUSÁT megváltoztatni (Document → sima objektum) minden modell
138
+ * minden fogyasztóját érintené — az itteni javítás viszont **csak az `encrypt:true` modelleket**
139
+ * érinti, ahol a `_transform` amúgy is **már ma is** sima (csak ROSSZ) objektumot ad vissza.
140
+ * Vagyis a változás nem tud elrontani olyat, ami ma működik.
141
+ *
142
+ * `toJSON()`-t használunk (nem `toObject()`-et): a titkosítás bevezetése ELŐTT a nyers Document a
143
+ * `res.send`-en át a `JSON.stringify` → `toJSON()` úton szerializálódott, tehát ez adja vissza
144
+ * **bitre ugyanazt a kimeneti alakot** (schema-transzformok, virtuals, `__v`-kezelés együtt).
145
+ *
146
+ * @param value tetszőleges érték
147
+ * @returns a Document sima alakja, vagy az eredeti érték
148
+ */
149
+ private static _toPlainIfDocument(value: any): any { // ccap-review-disable-line no-any-type
150
+ // A `$__` a mongoose-Document SAJÁT mezője — pontosan ez szivárgott ki a hibás payloadba,
151
+ // ezért ez a legpontosabb (és mongoose-import nélküli) diszkriminátor.
152
+ if (
153
+ value != null
154
+ && typeof value === 'object'
155
+ && typeof value.toJSON === 'function'
156
+ && Object.prototype.hasOwnProperty.call(value, '$__')
157
+ ) {
158
+ return value.toJSON();
159
+ }
160
+
161
+ return value;
162
+ }
163
+
118
164
  /**
119
165
  * Rekurzív per-field walk: az `encrypt:true` mezőket titkosítja/visszafejti, a `subObjectParams`-os
120
166
  * mezőkbe (objektum VAGY objektum-tömb) leszáll. Immutábilis: minden érintett konténert sekélyen
121
167
  * klónoz (az eredetit nem mutálja); a nem-érintett ágakat referencia szerint megosztja (read-only).
168
+ *
169
+ * ⚠️ A klónozás ELŐTT a mongoose-Documentek sima objektummá alakulnak — lásd {@link _toPlainIfDocument}.
122
170
  */
123
171
  private static _transform<T>(
124
172
  value: T,
@@ -131,7 +179,8 @@ export class DyNTS_FieldEncryption_Util {
131
179
  return value;
132
180
  }
133
181
 
134
- const result: any = Array.isArray(value) ? [ ...(value as any[]) ] : { ...(value as any) }; // ccap-review-disable-line no-any-type
182
+ const plain: any = this._toPlainIfDocument(value); // ccap-review-disable-line no-any-type
183
+ const result: any = Array.isArray(plain) ? [ ...(plain as any[]) ] : { ...(plain as any) }; // ccap-review-disable-line no-any-type
135
184
 
136
185
  for (const propKey in properties) {
137
186
  const prop: any = properties[propKey]; // ccap-review-disable-line no-any-type