instar 1.3.555 → 1.3.556
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +15 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +23 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/feedback-factory/inbox/BlobInboxClient.d.ts +75 -0
- package/dist/feedback-factory/inbox/BlobInboxClient.d.ts.map +1 -0
- package/dist/feedback-factory/inbox/BlobInboxClient.js +121 -0
- package/dist/feedback-factory/inbox/BlobInboxClient.js.map +1 -0
- package/dist/feedback-factory/inbox/InboxDrainer.d.ts +91 -0
- package/dist/feedback-factory/inbox/InboxDrainer.d.ts.map +1 -0
- package/dist/feedback-factory/inbox/InboxDrainer.js +188 -0
- package/dist/feedback-factory/inbox/InboxDrainer.js.map +1 -0
- package/dist/feedback-factory/receiver/BlobInboxStore.d.ts +30 -0
- package/dist/feedback-factory/receiver/BlobInboxStore.d.ts.map +1 -0
- package/dist/feedback-factory/receiver/BlobInboxStore.js +36 -0
- package/dist/feedback-factory/receiver/BlobInboxStore.js.map +1 -0
- package/dist/feedback-factory/receiver/handlers.d.ts +18 -3
- package/dist/feedback-factory/receiver/handlers.d.ts.map +1 -1
- package/dist/feedback-factory/receiver/handlers.js +8 -4
- package/dist/feedback-factory/receiver/handlers.js.map +1 -1
- package/dist/feedback-factory/store/JsonlFeedbackStore.d.ts +65 -0
- package/dist/feedback-factory/store/JsonlFeedbackStore.d.ts.map +1 -0
- package/dist/feedback-factory/store/JsonlFeedbackStore.js +194 -0
- package/dist/feedback-factory/store/JsonlFeedbackStore.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +4 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +4 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +53 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +11 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +10 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +4 -0
- package/upgrades/1.3.556.md +34 -0
- package/upgrades/side-effects/feedback-receiver-persistence.md +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlobInboxClient.js","sourceRoot":"","sources":["../../../src/feedback-factory/inbox/BlobInboxClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InboxDrainer.ts — moves accepted fleet feedback from the cloud inbox (Vercel
|
|
3
|
+
* Blob, written by the canonical front) into the durable canonical FeedbackStore
|
|
4
|
+
* on the operated machine.
|
|
5
|
+
*
|
|
6
|
+
* This is the Echo-machine half of the Option-B receiving end: the front makes a
|
|
7
|
+
* report DURABLE the instant it is accepted (no operated machine in the intake
|
|
8
|
+
* critical path); this drainer ingests at its own pace, so a machine asleep /
|
|
9
|
+
* restarting / tunnel-down only DELAYS processing — it never loses a report.
|
|
10
|
+
*
|
|
11
|
+
* Delivery semantics: AT-LEAST-ONCE, made idempotent by the feedbackId dedup the
|
|
12
|
+
* receiver path already has (`store.hasFeedback`). The ordering invariant is
|
|
13
|
+
* delete-AFTER-durable-commit: a blob is removed from the inbox only after the
|
|
14
|
+
* store append has returned. A crash between commit and delete re-delivers the
|
|
15
|
+
* blob on the next tick and dedup drops it.
|
|
16
|
+
*
|
|
17
|
+
* Poison handling: a blob that cannot be parsed/validated is QUARANTINED (re-put
|
|
18
|
+
* under `quarantine/`, then deleted from the inbox) so one malformed object can
|
|
19
|
+
* never wedge the drain loop, and nothing is silently destroyed.
|
|
20
|
+
*
|
|
21
|
+
* Signal-vs-authority: this component holds NO decision authority — every
|
|
22
|
+
* accept/reject decision happened in the front's intake defense chain. The
|
|
23
|
+
* drainer is pure transport + persistence, plus read-only counters for
|
|
24
|
+
* `GET /feedback-inbox/status`.
|
|
25
|
+
*/
|
|
26
|
+
import type { BlobInboxClient } from './BlobInboxClient.js';
|
|
27
|
+
import type { FeedbackItem } from '../processor/types.js';
|
|
28
|
+
export interface InboxDrainerOptions {
|
|
29
|
+
client: BlobInboxClient;
|
|
30
|
+
store: {
|
|
31
|
+
hasFeedback(feedbackId: string): boolean;
|
|
32
|
+
addFeedback(item: FeedbackItem): void;
|
|
33
|
+
};
|
|
34
|
+
/** Inbox prefix the front writes under. */
|
|
35
|
+
prefix?: string;
|
|
36
|
+
/** Poll cadence. */
|
|
37
|
+
pollIntervalMs?: number;
|
|
38
|
+
/** Page size per list call. */
|
|
39
|
+
pageSize?: number;
|
|
40
|
+
log?: (msg: string) => void;
|
|
41
|
+
}
|
|
42
|
+
export interface InboxDrainerStatus {
|
|
43
|
+
running: boolean;
|
|
44
|
+
prefix: string;
|
|
45
|
+
pollIntervalMs: number;
|
|
46
|
+
drained: number;
|
|
47
|
+
duplicates: number;
|
|
48
|
+
quarantined: number;
|
|
49
|
+
errors: number;
|
|
50
|
+
ticks: number;
|
|
51
|
+
lastTickAt: string | null;
|
|
52
|
+
lastDrainAt: string | null;
|
|
53
|
+
lastError: string | null;
|
|
54
|
+
}
|
|
55
|
+
export declare class InboxDrainer {
|
|
56
|
+
private readonly client;
|
|
57
|
+
private readonly store;
|
|
58
|
+
private readonly prefix;
|
|
59
|
+
private readonly pollIntervalMs;
|
|
60
|
+
private readonly pageSize;
|
|
61
|
+
private readonly log;
|
|
62
|
+
private timer;
|
|
63
|
+
private ticking;
|
|
64
|
+
private drained;
|
|
65
|
+
private duplicates;
|
|
66
|
+
private quarantined;
|
|
67
|
+
private errors;
|
|
68
|
+
private ticks;
|
|
69
|
+
private lastTickAt;
|
|
70
|
+
private lastDrainAt;
|
|
71
|
+
private lastError;
|
|
72
|
+
constructor(opts: InboxDrainerOptions);
|
|
73
|
+
start(): void;
|
|
74
|
+
stop(): void;
|
|
75
|
+
/**
|
|
76
|
+
* One drain pass: list → (read → ingest → delete) per blob, page by page.
|
|
77
|
+
* Per-blob failures are isolated — one bad object never aborts the pass.
|
|
78
|
+
*/
|
|
79
|
+
drainOnce(): Promise<{
|
|
80
|
+
drained: number;
|
|
81
|
+
duplicates: number;
|
|
82
|
+
quarantined: number;
|
|
83
|
+
errors: number;
|
|
84
|
+
}>;
|
|
85
|
+
/** Returns true when the blob was REMOVED from the inbox (drained / duplicate / quarantined). */
|
|
86
|
+
private drainBlob;
|
|
87
|
+
status(): InboxDrainerStatus;
|
|
88
|
+
}
|
|
89
|
+
/** Validate the minimal shape the receiver wrote. Null = quarantine. */
|
|
90
|
+
export declare function parseInboxItem(content: string): FeedbackItem | null;
|
|
91
|
+
//# sourceMappingURL=InboxDrainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InboxDrainer.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/inbox/InboxDrainer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE;QACL,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QACzC,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;KACvC,CAAC;IACF,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAMD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;IAE5C,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,SAAS,CAAuB;gBAE5B,IAAI,EAAE,mBAAmB;IASrC,KAAK,IAAI,IAAI;IAUb,IAAI,IAAI,IAAI;IAOZ;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAqCxG,iGAAiG;YACnF,SAAS;IAoCvB,MAAM,IAAI,kBAAkB;CAe7B;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAYnE"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InboxDrainer.ts — moves accepted fleet feedback from the cloud inbox (Vercel
|
|
3
|
+
* Blob, written by the canonical front) into the durable canonical FeedbackStore
|
|
4
|
+
* on the operated machine.
|
|
5
|
+
*
|
|
6
|
+
* This is the Echo-machine half of the Option-B receiving end: the front makes a
|
|
7
|
+
* report DURABLE the instant it is accepted (no operated machine in the intake
|
|
8
|
+
* critical path); this drainer ingests at its own pace, so a machine asleep /
|
|
9
|
+
* restarting / tunnel-down only DELAYS processing — it never loses a report.
|
|
10
|
+
*
|
|
11
|
+
* Delivery semantics: AT-LEAST-ONCE, made idempotent by the feedbackId dedup the
|
|
12
|
+
* receiver path already has (`store.hasFeedback`). The ordering invariant is
|
|
13
|
+
* delete-AFTER-durable-commit: a blob is removed from the inbox only after the
|
|
14
|
+
* store append has returned. A crash between commit and delete re-delivers the
|
|
15
|
+
* blob on the next tick and dedup drops it.
|
|
16
|
+
*
|
|
17
|
+
* Poison handling: a blob that cannot be parsed/validated is QUARANTINED (re-put
|
|
18
|
+
* under `quarantine/`, then deleted from the inbox) so one malformed object can
|
|
19
|
+
* never wedge the drain loop, and nothing is silently destroyed.
|
|
20
|
+
*
|
|
21
|
+
* Signal-vs-authority: this component holds NO decision authority — every
|
|
22
|
+
* accept/reject decision happened in the front's intake defense chain. The
|
|
23
|
+
* drainer is pure transport + persistence, plus read-only counters for
|
|
24
|
+
* `GET /feedback-inbox/status`.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_PREFIX = 'inbox/';
|
|
27
|
+
const DEFAULT_POLL_MS = 60_000;
|
|
28
|
+
const DEFAULT_PAGE_SIZE = 100;
|
|
29
|
+
export class InboxDrainer {
|
|
30
|
+
client;
|
|
31
|
+
store;
|
|
32
|
+
prefix;
|
|
33
|
+
pollIntervalMs;
|
|
34
|
+
pageSize;
|
|
35
|
+
log;
|
|
36
|
+
timer = null;
|
|
37
|
+
ticking = false;
|
|
38
|
+
drained = 0;
|
|
39
|
+
duplicates = 0;
|
|
40
|
+
quarantined = 0;
|
|
41
|
+
errors = 0;
|
|
42
|
+
ticks = 0;
|
|
43
|
+
lastTickAt = null;
|
|
44
|
+
lastDrainAt = null;
|
|
45
|
+
lastError = null;
|
|
46
|
+
constructor(opts) {
|
|
47
|
+
this.client = opts.client;
|
|
48
|
+
this.store = opts.store;
|
|
49
|
+
this.prefix = opts.prefix ?? DEFAULT_PREFIX;
|
|
50
|
+
this.pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
51
|
+
this.pageSize = opts.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
52
|
+
this.log = opts.log ?? (() => { });
|
|
53
|
+
}
|
|
54
|
+
start() {
|
|
55
|
+
if (this.timer)
|
|
56
|
+
return;
|
|
57
|
+
this.timer = setInterval(() => {
|
|
58
|
+
void this.drainOnce();
|
|
59
|
+
}, this.pollIntervalMs);
|
|
60
|
+
this.timer.unref();
|
|
61
|
+
// Prime immediately so a restart doesn't wait a full interval to catch up.
|
|
62
|
+
void this.drainOnce();
|
|
63
|
+
}
|
|
64
|
+
stop() {
|
|
65
|
+
if (this.timer) {
|
|
66
|
+
clearInterval(this.timer);
|
|
67
|
+
this.timer = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One drain pass: list → (read → ingest → delete) per blob, page by page.
|
|
72
|
+
* Per-blob failures are isolated — one bad object never aborts the pass.
|
|
73
|
+
*/
|
|
74
|
+
async drainOnce() {
|
|
75
|
+
if (this.ticking)
|
|
76
|
+
return { drained: 0, duplicates: 0, quarantined: 0, errors: 0 };
|
|
77
|
+
this.ticking = true;
|
|
78
|
+
const pass = { drained: 0, duplicates: 0, quarantined: 0, errors: 0 };
|
|
79
|
+
try {
|
|
80
|
+
this.ticks++;
|
|
81
|
+
this.lastTickAt = new Date().toISOString();
|
|
82
|
+
// Re-list the FIRST page after each batch instead of cursor-walking: the
|
|
83
|
+
// drain DELETES blobs as it goes, and a cursor taken before the deletes
|
|
84
|
+
// skips entries that shifted into the consumed range. Loop until a page
|
|
85
|
+
// comes back empty or a full page makes zero progress (all blobs failing
|
|
86
|
+
// — they stay queued and retry next tick; the guard caps a pathological
|
|
87
|
+
// store at a bounded number of batches per pass).
|
|
88
|
+
let batches = 0;
|
|
89
|
+
const maxBatches = 1000;
|
|
90
|
+
while (batches++ < maxBatches) {
|
|
91
|
+
const page = await this.client.list(this.prefix, { limit: this.pageSize });
|
|
92
|
+
if (page.blobs.length === 0)
|
|
93
|
+
break;
|
|
94
|
+
let progressed = 0;
|
|
95
|
+
for (const blob of page.blobs) {
|
|
96
|
+
if (await this.drainBlob(blob, pass))
|
|
97
|
+
progressed++;
|
|
98
|
+
}
|
|
99
|
+
if (progressed === 0)
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
this.lastError = null;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// A list-level failure (network, auth) ends the pass; blobs stay queued.
|
|
106
|
+
this.errors++;
|
|
107
|
+
pass.errors++;
|
|
108
|
+
this.lastError = String(err instanceof Error ? err.message : err);
|
|
109
|
+
this.log(`[feedback-inbox] drain pass failed: ${this.lastError}`);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
this.ticking = false;
|
|
113
|
+
}
|
|
114
|
+
return pass;
|
|
115
|
+
}
|
|
116
|
+
/** Returns true when the blob was REMOVED from the inbox (drained / duplicate / quarantined). */
|
|
117
|
+
async drainBlob(blob, pass) {
|
|
118
|
+
try {
|
|
119
|
+
const content = await this.client.fetchContent(blob.url);
|
|
120
|
+
const item = parseInboxItem(content);
|
|
121
|
+
if (!item) {
|
|
122
|
+
// Poison object: preserve under quarantine/, then clear from the inbox.
|
|
123
|
+
await this.client.put(`quarantine/${blob.pathname.replace(/^.*?\//, '')}`, content);
|
|
124
|
+
await this.client.del([blob.url]);
|
|
125
|
+
this.quarantined++;
|
|
126
|
+
pass.quarantined++;
|
|
127
|
+
this.log(`[feedback-inbox] quarantined malformed inbox object ${blob.pathname}`);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (this.store.hasFeedback(item.feedbackId)) {
|
|
131
|
+
this.duplicates++;
|
|
132
|
+
pass.duplicates++;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Durable commit FIRST…
|
|
136
|
+
this.store.addFeedback(item);
|
|
137
|
+
this.drained++;
|
|
138
|
+
pass.drained++;
|
|
139
|
+
this.lastDrainAt = new Date().toISOString();
|
|
140
|
+
}
|
|
141
|
+
// …delete only after the store write returned (at-least-once + dedup).
|
|
142
|
+
await this.client.del([blob.url]);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
// Leave the blob in place — it re-delivers next tick.
|
|
147
|
+
this.errors++;
|
|
148
|
+
pass.errors++;
|
|
149
|
+
this.lastError = String(err instanceof Error ? err.message : err);
|
|
150
|
+
this.log(`[feedback-inbox] failed to drain ${blob.pathname}: ${this.lastError}`);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
status() {
|
|
155
|
+
return {
|
|
156
|
+
running: this.timer !== null,
|
|
157
|
+
prefix: this.prefix,
|
|
158
|
+
pollIntervalMs: this.pollIntervalMs,
|
|
159
|
+
drained: this.drained,
|
|
160
|
+
duplicates: this.duplicates,
|
|
161
|
+
quarantined: this.quarantined,
|
|
162
|
+
errors: this.errors,
|
|
163
|
+
ticks: this.ticks,
|
|
164
|
+
lastTickAt: this.lastTickAt,
|
|
165
|
+
lastDrainAt: this.lastDrainAt,
|
|
166
|
+
lastError: this.lastError,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/** Validate the minimal shape the receiver wrote. Null = quarantine. */
|
|
171
|
+
export function parseInboxItem(content) {
|
|
172
|
+
let parsed;
|
|
173
|
+
try {
|
|
174
|
+
parsed = JSON.parse(content);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
if (!parsed || typeof parsed !== 'object')
|
|
180
|
+
return null;
|
|
181
|
+
const row = parsed;
|
|
182
|
+
if (typeof row.feedbackId !== 'string' || row.feedbackId.length === 0)
|
|
183
|
+
return null;
|
|
184
|
+
if (typeof row.title !== 'string' || typeof row.description !== 'string')
|
|
185
|
+
return null;
|
|
186
|
+
return row;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=InboxDrainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InboxDrainer.js","sourceRoot":"","sources":["../../../src/feedback-factory/inbox/InboxDrainer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlobInboxStore.ts — the canonical front's ReceiverStore: makes an accepted
|
|
3
|
+
* report DURABLE the instant the intake defenses pass, by writing it as one JSON
|
|
4
|
+
* object into the Vercel Blob inbox (`inbox/<feedbackId>.json`, random-suffixed).
|
|
5
|
+
*
|
|
6
|
+
* This is the cloud half of the Option-B receiving end (migration spec Q2b):
|
|
7
|
+
* the front never depends on any operated machine being awake — durability is
|
|
8
|
+
* Vercel-side; the InboxDrainer on the operated machine ingests asynchronously
|
|
9
|
+
* into the canonical JsonlFeedbackStore.
|
|
10
|
+
*
|
|
11
|
+
* Dedup (`hasFeedback`) is a prefix list against the inbox. It only sees blobs
|
|
12
|
+
* not yet drained — a retransmit AFTER the drainer consumed the original falls
|
|
13
|
+
* through to `addFeedback`, and the drainer's own `store.hasFeedback` dedup drops
|
|
14
|
+
* it at ingest. End-to-end the pipeline is idempotent on feedbackId.
|
|
15
|
+
*
|
|
16
|
+
* No decision authority here: every accept/reject decision lives in
|
|
17
|
+
* handleFeedbackSubmit's ported defense chain. This class only persists.
|
|
18
|
+
*/
|
|
19
|
+
import type { BlobInboxClient } from '../inbox/BlobInboxClient.js';
|
|
20
|
+
import type { ReceiverStore } from './handlers.js';
|
|
21
|
+
import type { FeedbackItem } from '../processor/types.js';
|
|
22
|
+
export declare const INBOX_PREFIX = "inbox/";
|
|
23
|
+
export declare class BlobInboxStore implements ReceiverStore {
|
|
24
|
+
private readonly client;
|
|
25
|
+
private readonly clock;
|
|
26
|
+
constructor(client: BlobInboxClient, clock?: () => string);
|
|
27
|
+
hasFeedback(feedbackId: string): Promise<boolean>;
|
|
28
|
+
addFeedback(item: FeedbackItem): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=BlobInboxStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlobInboxStore.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/receiver/BlobInboxStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC,qBAAa,cAAe,YAAW,aAAa;IAEhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,MAAM,EAAE,eAAe,EACvB,KAAK,GAAE,MAAM,MAAuC;IAGjE,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKjD,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAIrD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlobInboxStore.ts — the canonical front's ReceiverStore: makes an accepted
|
|
3
|
+
* report DURABLE the instant the intake defenses pass, by writing it as one JSON
|
|
4
|
+
* object into the Vercel Blob inbox (`inbox/<feedbackId>.json`, random-suffixed).
|
|
5
|
+
*
|
|
6
|
+
* This is the cloud half of the Option-B receiving end (migration spec Q2b):
|
|
7
|
+
* the front never depends on any operated machine being awake — durability is
|
|
8
|
+
* Vercel-side; the InboxDrainer on the operated machine ingests asynchronously
|
|
9
|
+
* into the canonical JsonlFeedbackStore.
|
|
10
|
+
*
|
|
11
|
+
* Dedup (`hasFeedback`) is a prefix list against the inbox. It only sees blobs
|
|
12
|
+
* not yet drained — a retransmit AFTER the drainer consumed the original falls
|
|
13
|
+
* through to `addFeedback`, and the drainer's own `store.hasFeedback` dedup drops
|
|
14
|
+
* it at ingest. End-to-end the pipeline is idempotent on feedbackId.
|
|
15
|
+
*
|
|
16
|
+
* No decision authority here: every accept/reject decision lives in
|
|
17
|
+
* handleFeedbackSubmit's ported defense chain. This class only persists.
|
|
18
|
+
*/
|
|
19
|
+
export const INBOX_PREFIX = 'inbox/';
|
|
20
|
+
export class BlobInboxStore {
|
|
21
|
+
client;
|
|
22
|
+
clock;
|
|
23
|
+
constructor(client, clock = () => new Date().toISOString()) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
this.clock = clock;
|
|
26
|
+
}
|
|
27
|
+
async hasFeedback(feedbackId) {
|
|
28
|
+
const page = await this.client.list(`${INBOX_PREFIX}${feedbackId}`, { limit: 1 });
|
|
29
|
+
return page.blobs.length > 0;
|
|
30
|
+
}
|
|
31
|
+
async addFeedback(item) {
|
|
32
|
+
const row = { receivedAt: this.clock(), status: 'unprocessed', ...item };
|
|
33
|
+
await this.client.put(`${INBOX_PREFIX}${item.feedbackId}.json`, JSON.stringify(row));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=BlobInboxStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlobInboxStore.js","sourceRoot":"","sources":["../../../src/feedback-factory/receiver/BlobInboxStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC,MAAM,OAAO,cAAc;IAEN;IACA;IAFnB,YACmB,MAAuB,EACvB,QAAsB,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QADpD,WAAM,GAAN,MAAM,CAAiB;QACvB,UAAK,GAAL,KAAK,CAA+C;IACpE,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,UAAkB;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,GAAG,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAkB;QAClC,MAAM,GAAG,GAAiB,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,CAAC;QACvF,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,GAAG,IAAI,CAAC,UAAU,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACvF,CAAC;CACF"}
|
|
@@ -26,8 +26,19 @@ export interface FeedbackResponse {
|
|
|
26
26
|
headers?: Record<string, string>;
|
|
27
27
|
json: unknown;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* The narrow store surface the SUBMIT path actually uses (dedup + write). The full
|
|
31
|
+
* `FeedbackStore` satisfies it structurally; the canonical front's Blob-backed
|
|
32
|
+
* inbox store implements it asynchronously (Vercel Blob is REST). Methods may be
|
|
33
|
+
* sync or async — the handler awaits both, so decision order, status codes, and
|
|
34
|
+
* messages are unchanged for the existing synchronous stores.
|
|
35
|
+
*/
|
|
36
|
+
export interface ReceiverStore {
|
|
37
|
+
hasFeedback(feedbackId: string): boolean | Promise<boolean>;
|
|
38
|
+
addFeedback(item: Parameters<FeedbackStore['addFeedback']>[0]): void | Promise<void>;
|
|
39
|
+
}
|
|
29
40
|
export interface FeedbackHandlerDeps {
|
|
30
|
-
store:
|
|
41
|
+
store: ReceiverStore;
|
|
31
42
|
rateLimiter: RateLimiter;
|
|
32
43
|
/** Normalized webhook secret (see normalizeWebhookSecret); undefined ⇒ unsigned-but-accepted. */
|
|
33
44
|
secret: string | undefined;
|
|
@@ -36,6 +47,10 @@ export interface FeedbackHandlerDeps {
|
|
|
36
47
|
/** Injected id generator (defaults to the reference's `fb-<base36 time>-<rand>`). */
|
|
37
48
|
generateFeedbackId?: () => string;
|
|
38
49
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Port of handleSubmit. Returns the status/headers/json the canonical front sends
|
|
52
|
+
* back. Async because the canonical front's store is REST-backed (Vercel Blob);
|
|
53
|
+
* for synchronous stores the awaits are no-ops and behavior is byte-identical.
|
|
54
|
+
*/
|
|
55
|
+
export declare function handleFeedbackSubmit(req: FeedbackRequest, deps: FeedbackHandlerDeps): Promise<FeedbackResponse>;
|
|
41
56
|
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/receiver/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,iGAAiG;IACjG,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;CACnC;AAWD
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/receiver/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,iGAAiG;IACjG,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;CACnC;AAWD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsFrH"}
|
|
@@ -22,8 +22,12 @@ function header(headers, name) {
|
|
|
22
22
|
function defaultFeedbackId(now) {
|
|
23
23
|
return `fb-${now.toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Port of handleSubmit. Returns the status/headers/json the canonical front sends
|
|
27
|
+
* back. Async because the canonical front's store is REST-backed (Vercel Blob);
|
|
28
|
+
* for synchronous stores the awaits are no-ops and behavior is byte-identical.
|
|
29
|
+
*/
|
|
30
|
+
export async function handleFeedbackSubmit(req, deps) {
|
|
27
31
|
const { store, rateLimiter, secret, now } = deps;
|
|
28
32
|
const sourceIp = extractSourceIp(req.headers, req.remoteAddress);
|
|
29
33
|
// Layer 1: rate limit.
|
|
@@ -82,10 +86,10 @@ export function handleFeedbackSubmit(req, deps) {
|
|
|
82
86
|
? provided
|
|
83
87
|
: (deps.generateFeedbackId ?? (() => defaultFeedbackId(now)))();
|
|
84
88
|
// Layer 5: dedup — idempotent on a known feedbackId.
|
|
85
|
-
if (store.hasFeedback(feedbackId)) {
|
|
89
|
+
if (await store.hasFeedback(feedbackId)) {
|
|
86
90
|
return { status: 200, json: { id: feedbackId, received: true, duplicate: true } };
|
|
87
91
|
}
|
|
88
|
-
store.addFeedback({
|
|
92
|
+
await store.addFeedback({
|
|
89
93
|
feedbackId,
|
|
90
94
|
type,
|
|
91
95
|
title: title.trim().slice(0, 500),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../../src/feedback-factory/receiver/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,eAAe,EAAE,wBAAwB,EAAE,aAAa,EAAE,eAAe,EACzE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,GAEvE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../../src/feedback-factory/receiver/handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JsonlFeedbackStore.ts — the DURABLE canonical FeedbackStore for the operated
|
|
3
|
+
* instance (post-cutover authority under the migration spec's Option B: Echo's
|
|
4
|
+
* instance keeps its OWN canonical copy; Portal never opens a write door).
|
|
5
|
+
*
|
|
6
|
+
* Backing format: newline-delimited JSON, one file per entity family
|
|
7
|
+
* (`feedback.jsonl`, `clusters.jsonl`, `dispatches.jsonl`) under a caller-supplied
|
|
8
|
+
* directory. The format is DELIBERATELY the same shape `PersistedShadowImportTarget`
|
|
9
|
+
* writes (one full row per line, keyed by feedbackId/clusterId), so the proven AS-IS
|
|
10
|
+
* import artifact seeds this store directly — the cutover import output IS the
|
|
11
|
+
* canonical store, no translation step (fewer integrity risks at the one-way door).
|
|
12
|
+
*
|
|
13
|
+
* Mutation model: append-only log with LAST-WRITE-WINS on load. Every mutation
|
|
14
|
+
* appends the FULL updated row; on construction all lines are folded into a Map by
|
|
15
|
+
* id (later lines supersede earlier). A file with one row per entity — exactly what
|
|
16
|
+
* the import produces — is therefore a valid, already-compact log. When superseded
|
|
17
|
+
* lines pile up past a threshold the file is compacted at construction time via an
|
|
18
|
+
* atomic temp+rename rewrite (crash mid-compaction leaves the original intact).
|
|
19
|
+
*
|
|
20
|
+
* JSONL (not SQLite) for the same reason as PersistedShadowImportTarget: dependency-
|
|
21
|
+
* free, no native module, trivially inspectable, and append durability is a plain
|
|
22
|
+
* appendFileSync. Volume is fleet feedback — low write rate; the import's measured
|
|
23
|
+
* 148k-row artifact parses in well under a second.
|
|
24
|
+
*/
|
|
25
|
+
import type { FeedbackItem, Cluster } from '../processor/types.js';
|
|
26
|
+
import type { ReopenDecision } from '../processor/reopen.js';
|
|
27
|
+
import type { DispatchRecord } from '../dispatch/dispatch.js';
|
|
28
|
+
import type { FeedbackStore, FeedbackMetrics } from './FeedbackStore.js';
|
|
29
|
+
export declare class JsonlFeedbackStore implements FeedbackStore {
|
|
30
|
+
private readonly dir;
|
|
31
|
+
private readonly feedbackPath;
|
|
32
|
+
private readonly clustersPath;
|
|
33
|
+
private readonly dispatchesPath;
|
|
34
|
+
private feedback;
|
|
35
|
+
private clusters;
|
|
36
|
+
private dispatches;
|
|
37
|
+
private counts;
|
|
38
|
+
constructor(dir: string);
|
|
39
|
+
private loadAndMaybeCompact;
|
|
40
|
+
private appendFeedback;
|
|
41
|
+
private appendCluster;
|
|
42
|
+
getUnprocessedFeedback(): FeedbackItem[];
|
|
43
|
+
getActiveClusters(): Cluster[];
|
|
44
|
+
getCluster(clusterId: string): Cluster | undefined;
|
|
45
|
+
upsertClusterFromItem(clusterId: string, item: FeedbackItem): void;
|
|
46
|
+
mergeIntoCluster(clusterId: string, _item: FeedbackItem): void;
|
|
47
|
+
applyReopen(clusterId: string, decision: ReopenDecision): void;
|
|
48
|
+
markProcessed(feedbackId: string, clusterId: string): void;
|
|
49
|
+
hasFeedback(feedbackId: string): boolean;
|
|
50
|
+
addFeedback(item: FeedbackItem): void;
|
|
51
|
+
listDispatches(filter?: {
|
|
52
|
+
since?: string;
|
|
53
|
+
type?: string;
|
|
54
|
+
}): DispatchRecord[];
|
|
55
|
+
findDispatchByTitle(title: string): DispatchRecord | undefined;
|
|
56
|
+
createDispatch(record: DispatchRecord): void;
|
|
57
|
+
metrics(): FeedbackMetrics;
|
|
58
|
+
/** Read-only size snapshot for the status surface. */
|
|
59
|
+
sizes(): {
|
|
60
|
+
feedback: number;
|
|
61
|
+
clusters: number;
|
|
62
|
+
dispatches: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=JsonlFeedbackStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonlFeedbackStore.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/store/JsonlFeedbackStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAWzE,qBAAa,kBAAmB,YAAW,aAAa;IAU1C,OAAO,CAAC,QAAQ,CAAC,GAAG;IAThC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,MAAM,CAAwE;gBAEzD,GAAG,EAAE,MAAM;IAiBxC,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,aAAa;IAMrB,sBAAsB,IAAI,YAAY,EAAE;IAMxC,iBAAiB,IAAI,OAAO,EAAE;IAI9B,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIlD,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;IAmBlE,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAS9D,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAY9D,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAU1D,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIxC,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAMrC,cAAc,CAAC,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,cAAc,EAAE;IAQ5E,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI9D,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAM5C,OAAO,IAAI,eAAe;IAI1B,sDAAsD;IACtD,KAAK,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;CAGpE"}
|