@volter/twin-sentry 0.1.0 → 0.1.1

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.
@@ -11,6 +11,7 @@
11
11
  // Delivery is an INJECTED seam (the auth-boundary): tests pass a fake deliverer, live runs
12
12
  // pass the HTTP deliverer. verify() in the manifest stays fully offline.
13
13
  import { applyTwinWrite, projectResources, nodeBuiltin } from '@volter/world-core';
14
+ import { appDestination, appFetch } from '@volter/world-core/app-route';
14
15
  import { worldEgressRefusal } from '@volter/world-core/network-policy';
15
16
  const SERVICE = 'sentry';
16
17
  function nodeCrypto() {
@@ -68,10 +69,10 @@ export function listAlertRules(projectId, root) {
68
69
  }
69
70
  // ── Webhook emission on issue lifecycle ──────────────────────────────────────────
70
71
  const httpDelivery = async (url, payload, headers) => {
71
- if (worldEgressRefusal(url) !== null)
72
- return; // the World's egress rule: refused like an unreachable endpoint
72
+ if (!appDestination(url) && worldEgressRefusal(url) !== null)
73
+ return; // the World's egress rule, the application's own hostnames reaching it inside the World: refused like an unreachable endpoint
73
74
  try {
74
- await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', ...headers }, body: payload });
75
+ await appFetch(url, { method: 'POST', headers: { 'content-type': 'application/json', ...headers }, body: payload });
75
76
  }
76
77
  catch {
77
78
  /* fire-and-forget, like Sentry */
@@ -14,7 +14,7 @@
14
14
  // query param, or embedded in the envelope header's `dsn`. The twin validates that a
15
15
  // project with that public key exists (faithful 401 otherwise). The store/envelope path
16
16
  // is unauthenticated by the SECRET key (that's the modern DSN model — public key only).
17
- import { applyTwinWrite, projectResources } from '@volter/world-core';
17
+ import { applyTwinWrite, projectResources, worldNow } from '@volter/world-core';
18
18
  const SERVICE = 'sentry';
19
19
  // ── DSN public key extraction (auth) ─────────────────────────────────────────────
20
20
  // The SDK sends the public key in one of three places. We accept all three, mirroring
@@ -192,7 +192,7 @@ function genEventId(provided) {
192
192
  * Pure local writes via the kernel — no network. `occurredAt` keeps it deterministic.
193
193
  */
194
194
  export async function ingestEvent(key, event, opts = {}) {
195
- const occurredAt = opts.occurredAt ?? new Date().toISOString();
195
+ const occurredAt = opts.occurredAt ?? worldNow();
196
196
  const eventTs = toIso(event.timestamp, occurredAt);
197
197
  const eventId = genEventId(event.event_id);
198
198
  const issueId = issueIdFor(key.projectId, event);
@@ -320,7 +320,7 @@ export function envelopeSessions(env) {
320
320
  * lifecycle, the final status wins). This is fully reproducible offline — crash-free counts
321
321
  * are exact tallies over the persisted sessions (NOT a fabricated metrics engine). */
322
322
  export async function persistSessions(projectId, sessions, opts = {}) {
323
- const occurredAt = opts.occurredAt ?? new Date().toISOString();
323
+ const occurredAt = opts.occurredAt ?? worldNow();
324
324
  for (const s of sessions) {
325
325
  if (!s.sid)
326
326
  continue;
@@ -336,7 +336,7 @@ export async function persistSessions(projectId, sessions, opts = {}) {
336
336
  }
337
337
  /** Persist an event's attachments (metadata only) as `event_attachment` resources. */
338
338
  export async function persistEventAttachments(eventId, projectId, attachments, opts = {}) {
339
- const occurredAt = opts.occurredAt ?? new Date().toISOString();
339
+ const occurredAt = opts.occurredAt ?? worldNow();
340
340
  let i = rows('event_attachment', opts.root).length;
341
341
  for (const a of attachments) {
342
342
  i += 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/twin-sentry",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Local Sentry twin — a faithful, stateful local Sentry error-monitoring API your real `@sentry/node` SDK talks to unmodified. Ingest captureException events into grouped issues, browse the Web API + dashboard mirror, simulate, and fork. Built on @volter/world-core.",
5
5
  "keywords": [
6
6
  "twin",
@@ -57,10 +57,10 @@
57
57
  "react-dom": "^19.2.7"
58
58
  },
59
59
  "peerDependencies": {
60
- "@volter/world-core": "2.0.0"
60
+ "@volter/world-core": "2.0.1"
61
61
  },
62
62
  "devDependencies": {
63
- "@volter/world-core": "2.0.0",
63
+ "@volter/world-core": "2.0.1",
64
64
  "@volter/world-tooling": "0.1.0",
65
65
  "@sentry/node": "^10.64.0",
66
66
  "@types/bun": "^1.2.20",
@@ -12,6 +12,7 @@
12
12
  // pass the HTTP deliverer. verify() in the manifest stays fully offline.
13
13
  import { applyTwinWrite, projectResources, nodeBuiltin } from '@volter/world-core';
14
14
  import type { TwinResource } from '@volter/world-core';
15
+ import { appDestination, appFetch } from '@volter/world-core/app-route';
15
16
  import { worldEgressRefusal } from '@volter/world-core/network-policy';
16
17
 
17
18
  const SERVICE = 'sentry';
@@ -106,9 +107,9 @@ export function listAlertRules(projectId: string | undefined, root?: string): Tw
106
107
 
107
108
  // ── Webhook emission on issue lifecycle ──────────────────────────────────────────
108
109
  const httpDelivery: SentryWebhookDelivery = async (url, payload, headers) => {
109
- if (worldEgressRefusal(url) !== null) return; // the World's egress rule: refused like an unreachable endpoint
110
+ if (!appDestination(url) && worldEgressRefusal(url) !== null) return; // the World's egress rule, the application's own hostnames reaching it inside the World: refused like an unreachable endpoint
110
111
  try {
111
- await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', ...headers }, body: payload });
112
+ await appFetch(url, { method: 'POST', headers: { 'content-type': 'application/json', ...headers }, body: payload });
112
113
  } catch {
113
114
  /* fire-and-forget, like Sentry */
114
115
  }
@@ -14,7 +14,7 @@
14
14
  // query param, or embedded in the envelope header's `dsn`. The twin validates that a
15
15
  // project with that public key exists (faithful 401 otherwise). The store/envelope path
16
16
  // is unauthenticated by the SECRET key (that's the modern DSN model — public key only).
17
- import { applyTwinWrite, projectResources } from '@volter/world-core';
17
+ import { applyTwinWrite, projectResources, worldNow } from '@volter/world-core';
18
18
  import type { TwinResource } from '@volter/world-core';
19
19
 
20
20
  const SERVICE = 'sentry';
@@ -245,7 +245,7 @@ export async function ingestEvent(
245
245
  event: SentryEventPayload,
246
246
  opts: { root?: string; occurredAt?: string } = {},
247
247
  ): Promise<IngestOutcome> {
248
- const occurredAt = opts.occurredAt ?? new Date().toISOString();
248
+ const occurredAt = opts.occurredAt ?? worldNow();
249
249
  const eventTs = toIso(event.timestamp, occurredAt);
250
250
  const eventId = genEventId(event.event_id);
251
251
  const issueId = issueIdFor(key.projectId, event);
@@ -396,7 +396,7 @@ export async function persistSessions(
396
396
  sessions: EnvelopeSession[],
397
397
  opts: { root?: string; occurredAt?: string } = {},
398
398
  ): Promise<void> {
399
- const occurredAt = opts.occurredAt ?? new Date().toISOString();
399
+ const occurredAt = opts.occurredAt ?? worldNow();
400
400
  for (const s of sessions) {
401
401
  if (!s.sid) continue;
402
402
  await applyTwinWrite(SERVICE, {
@@ -417,7 +417,7 @@ export async function persistEventAttachments(
417
417
  attachments: EnvelopeAttachment[],
418
418
  opts: { root?: string; occurredAt?: string } = {},
419
419
  ): Promise<void> {
420
- const occurredAt = opts.occurredAt ?? new Date().toISOString();
420
+ const occurredAt = opts.occurredAt ?? worldNow();
421
421
  let i = rows('event_attachment', opts.root).length;
422
422
  for (const a of attachments) {
423
423
  i += 1;