@warpgogol/werkstatt-shared 0.13.0 → 0.14.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 (63) hide show
  1. package/package.json +21 -69
  2. package/src/integration/funnel.ts +78 -1
  3. package/src/integration/index.ts +19 -2
  4. package/src/integration/lagebild-ingress.ts +169 -0
  5. package/src/integration/orchestration.ts +7 -13
  6. package/src/integration/port-barrel.ts +9 -28
  7. package/src/integration/tests/funnel-bridge.test.ts +7 -7
  8. package/src/integration/tests/lagebild-ingress.test.ts +118 -0
  9. package/src/observability/typed-refs.ts +1 -1
  10. package/src/ontology/analytics/matomo-fleet.registry.yaml +1 -1
  11. package/src/ontology/archetypes/index.json +12 -9
  12. package/src/ontology/archetypes/index.yaml +26 -10
  13. package/src/ontology/blueprints/website-local.yaml +11 -11
  14. package/src/ontology/blueprints/website-service.yaml +20 -20
  15. package/src/ontology/capabilities/lead.submit.yaml +2 -2
  16. package/src/ontology/enums.ts +2 -2
  17. package/src/ontology/external-surfaces/jsonld-types.yaml +14 -14
  18. package/src/ontology/external-surfaces/url-schema.yaml +1 -1
  19. package/src/ontology/index.ts +2 -0
  20. package/src/ontology/schemas/index.ts +7 -2
  21. package/src/ontology/schemas/system/integrations.ts +1 -1
  22. package/src/ontology/schemas/system/manifest.ts +38 -1
  23. package/src/ontology/schemas/system.ts +7 -2
  24. package/src/ontology/shared-section-props/common.ts +11 -0
  25. package/src/ontology/shared-section-props/visual-header.ts +1 -1
  26. package/src/share/agent/api-catalog.ts +5 -0
  27. package/src/share/agent/ard-catalog.ts +14 -0
  28. package/src/share/agent/index.ts +2 -0
  29. package/src/share/agent/manifest.ts +25 -2
  30. package/src/share/agent/mcp-card.ts +4 -0
  31. package/src/share/agent/openapi.ts +78 -0
  32. package/src/share/agent/search.test.ts +188 -0
  33. package/src/share/agent/search.ts +164 -0
  34. package/src/share/env.d.ts +0 -4
  35. package/src/share/schemas/effects.ts +16 -0
  36. package/src/share/schemas/media.ts +4 -0
  37. package/src/share/schemas/section-shell.ts +2 -1
  38. package/src/share/scripts/external-link-qr.ts +9 -1
  39. package/src/share/scripts/gsap-counter.ts +2 -0
  40. package/src/share/scripts/inline-number-animation.ts +2 -0
  41. package/src/share/scripts/live-photos.ts +2 -0
  42. package/src/share/scripts/video-player.test.ts +210 -0
  43. package/src/share/scripts/video-player.ts +73 -13
  44. package/src/share/semantic/extract.ts +25 -1
  45. package/src/share/semantic/tests/split-sentences.test.ts +57 -0
  46. package/src/share/tests/manifest.test.ts +51 -0
  47. package/src/share/tests/openapi.test.ts +30 -0
  48. package/src/share/types/qrcode-browser.d.ts +18 -0
  49. package/src/share/utility-registry.yaml +2 -2
  50. package/src/integration/crm-buffer.ts +0 -491
  51. package/src/integration-adapter-supabase-crm/adapter.ts +0 -437
  52. package/src/integration-adapter-supabase-crm/client.ts +0 -605
  53. package/src/integration-adapter-supabase-crm/index.ts +0 -28
  54. package/src/integration-adapter-supabase-crm/pipedrive-sync-target.ts +0 -482
  55. package/src/integration-adapter-supabase-crm/supabase-rest.ts +0 -89
  56. package/src/integration-adapter-supabase-crm/tenant-registry.ts +0 -334
  57. package/src/integration-adapter-supabase-crm/tests/change-balance.pbt.test.ts +0 -83
  58. package/src/integration-adapter-supabase-crm/tests/funnel-persistence.test.ts +0 -409
  59. package/src/integration-adapter-supabase-crm/tests/lifecycle-sync.test.ts +0 -288
  60. package/src/integration-adapter-supabase-crm/tests/stage-map.test.ts +0 -80
  61. package/src/integration-adapter-supabase-crm/vitest.config.ts +0 -8
  62. package/src/integration-adapter-supabase-crm/worker.ts +0 -223
  63. package/src/ontology/archetypes/components/currency-selector.yaml +0 -19
@@ -1,605 +0,0 @@
1
- /*
2
- <MODULE_CONTRACT>
3
- <purpose>Supabase REST client implementation of CrmBufferClient. Uses the Supabase REST API
4
- (PostgREST) directly over fetch — no Supabase SDK dependency. Secrets (URL + anon/service key)
5
- are injected by the caller; this module never imports astro:env. All queries set the
6
- app.current_tenant config so RLS policies enforce tenant isolation.</purpose>
7
- <non-goals>
8
- <item>Do not import the Supabase SDK (@supabase/supabase-js) — raw fetch only.</item>
9
- <item>Do not read astro:env — the caller injects supabaseUrl and supabaseKey.</item>
10
- </non-goals>
11
- </MODULE_CONTRACT>
12
- <CHANGE_SUMMARY>
13
- <item>Lagebild MVP: initial Supabase REST client.</item>
14
- <item>Split low-level Supabase REST transport into supabase-rest.ts to keep the client focused on CRM buffer semantics.</item>
15
- </CHANGE_SUMMARY>
16
- */
17
-
18
- import type {
19
- BufferConsentEvent,
20
- BufferContact,
21
- BufferDeal,
22
- BufferFunnelEvent,
23
- BufferInvoice,
24
- BufferOrganization,
25
- BufferStageTransition,
26
- BufferSubscription,
27
- BufferUpsertResult,
28
- CrmBufferClient,
29
- CrmBufferReader,
30
- CrmBufferWriter,
31
- DealPipedriveIdPatch,
32
- OutboxWriteResult,
33
- SyncOutboxOp,
34
- SyncOutboxRow,
35
- SyncOutboxStatus,
36
- } from "@warpgogol/werkstatt-shared/integration/crm-buffer";
37
- import { rest, type FetchImpl, type SupabaseClientConfig } from "./supabase-rest.ts";
38
-
39
- export type { SupabaseClientConfig } from "./supabase-rest.ts";
40
-
41
- /**
42
- * Fetch-based CrmBufferClient implementation backed by the Supabase REST API.
43
- * Tenant isolation is enforced via the x-set-config RLS mechanism.
44
- */
45
- export class SupabaseCrmBufferClient implements CrmBufferWriter, CrmBufferReader {
46
- private readonly config: SupabaseClientConfig;
47
- private readonly fetchImpl: FetchImpl;
48
-
49
- constructor(config: SupabaseClientConfig, fetchImpl: FetchImpl = fetch) {
50
- this.config = config;
51
- this.fetchImpl = fetchImpl;
52
- }
53
-
54
- async upsertContact(
55
- tenantId: string,
56
- data: Omit<BufferContact, "id" | "tenant_id" | "created_at" | "updated_at">,
57
- ): Promise<BufferUpsertResult> {
58
- const cfg = { ...this.config, tenantId };
59
- const row = { tenant_id: tenantId, ...data };
60
- // ON CONFLICT (tenant_id, uchat_contact_id) DO UPDATE
61
- const result = (await rest(
62
- cfg,
63
- "POST",
64
- "buffer_contacts",
65
- {
66
- body: row,
67
- prefer: "return=representation,resolution=merge-duplicates",
68
- },
69
- this.fetchImpl,
70
- )) as Array<BufferContact & { _was_created?: boolean }>;
71
- const created = Array.isArray(result) ? result[0] : (result as BufferContact);
72
- return { id: created.id, created: !data.uchat_contact_id };
73
- }
74
-
75
- async upsertOrganization(
76
- tenantId: string,
77
- data: Omit<BufferOrganization, "id" | "tenant_id" | "created_at" | "updated_at">,
78
- ): Promise<BufferUpsertResult> {
79
- const cfg = { ...this.config, tenantId };
80
- const row = { tenant_id: tenantId, ...data };
81
- // ON CONFLICT (tenant_id, legal_name|name) DO UPDATE — resolve-or-create.
82
- const result = (await rest(
83
- cfg,
84
- "POST",
85
- "buffer_organizations",
86
- {
87
- body: row,
88
- prefer: "return=representation,resolution=merge-duplicates",
89
- },
90
- this.fetchImpl,
91
- )) as Array<BufferOrganization>;
92
- const org = Array.isArray(result) ? result[0] : (result as BufferOrganization);
93
- return { id: org.id, created: org.created_at === org.updated_at };
94
- }
95
-
96
- async findOrganizationByStripeCustomer(
97
- tenantId: string,
98
- stripeCustomerId: string,
99
- ): Promise<{ id: string } | null> {
100
- const cfg = { ...this.config, tenantId };
101
- const rows = (await rest(
102
- cfg,
103
- "GET",
104
- "buffer_organizations",
105
- {
106
- params: {
107
- tenant_id: `eq.${tenantId}`,
108
- stripe_customer_id: `eq.${stripeCustomerId}`,
109
- select: "id",
110
- limit: "1",
111
- },
112
- },
113
- this.fetchImpl,
114
- )) as Array<{ id: string }>;
115
- return Array.isArray(rows) && rows[0]?.id ? { id: rows[0].id } : null;
116
- }
117
-
118
- async upsertDeal(
119
- tenantId: string,
120
- data: Omit<BufferDeal, "id" | "tenant_id" | "created_at" | "updated_at">,
121
- ): Promise<BufferUpsertResult> {
122
- const cfg = { ...this.config, tenantId };
123
- const row = { tenant_id: tenantId, ...data };
124
- const result = (await rest(
125
- cfg,
126
- "POST",
127
- "buffer_deals",
128
- {
129
- body: row,
130
- prefer: "return=representation,resolution=merge-duplicates",
131
- },
132
- this.fetchImpl,
133
- )) as Array<BufferDeal>;
134
- const deal = Array.isArray(result) ? result[0] : (result as BufferDeal);
135
- return { id: deal.id, created: deal.created_at === deal.updated_at };
136
- }
137
-
138
- async appendStageTransition(
139
- tenantId: string,
140
- data: Omit<BufferStageTransition, "id" | "tenant_id" | "created_at">,
141
- ): Promise<string> {
142
- const cfg = { ...this.config, tenantId };
143
- const row = { tenant_id: tenantId, ...data };
144
- const result = (await rest(
145
- cfg,
146
- "POST",
147
- "buffer_stage_transitions",
148
- {
149
- body: row,
150
- prefer: "return=representation",
151
- },
152
- this.fetchImpl,
153
- )) as Array<BufferStageTransition>;
154
- const tr = Array.isArray(result) ? result[0] : (result as BufferStageTransition);
155
- return tr.id;
156
- }
157
-
158
- async appendFunnelEvent(
159
- tenantId: string,
160
- data: Omit<BufferFunnelEvent, "id" | "tenant_id" | "created_at">,
161
- ): Promise<string> {
162
- const cfg = { ...this.config, tenantId };
163
- const row = { tenant_id: tenantId, ...data };
164
- // Idempotent by (tenant_id, idempotency_key): a UChat webhook retry must not
165
- // create a second row. ignore-duplicates returns [] on conflict.
166
- const result = (await rest(
167
- cfg,
168
- "POST",
169
- "buffer_funnel_events",
170
- {
171
- body: row,
172
- prefer: "return=representation,resolution=ignore-duplicates",
173
- },
174
- this.fetchImpl,
175
- )) as Array<BufferFunnelEvent>;
176
- const inserted = Array.isArray(result) ? result[0] : (result as BufferFunnelEvent | null);
177
- if (inserted?.id) return inserted.id;
178
- // Conflict (duplicate idempotency_key) — return the existing row id.
179
- const existing = (await rest(
180
- cfg,
181
- "GET",
182
- "buffer_funnel_events",
183
- {
184
- params: {
185
- tenant_id: `eq.${tenantId}`,
186
- idempotency_key: `eq.${data.idempotency_key}`,
187
- select: "id",
188
- limit: "1",
189
- },
190
- },
191
- this.fetchImpl,
192
- )) as Array<{ id: string }>;
193
- return Array.isArray(existing) && existing[0]?.id ? existing[0].id : "";
194
- }
195
-
196
- async appendConsentEvent(
197
- tenantId: string,
198
- data: Omit<BufferConsentEvent, "id" | "tenant_id" | "created_at">,
199
- ): Promise<string> {
200
- const cfg = { ...this.config, tenantId };
201
- const row = { tenant_id: tenantId, ...data };
202
- // Append-only: every consent acknowledgement is an immutable row.
203
- const result = (await rest(
204
- cfg,
205
- "POST",
206
- "buffer_consent_events",
207
- {
208
- body: row,
209
- prefer: "return=representation",
210
- },
211
- this.fetchImpl,
212
- )) as Array<BufferConsentEvent>;
213
- const ev = Array.isArray(result) ? result[0] : (result as BufferConsentEvent);
214
- return ev.id;
215
- }
216
-
217
- async upsertSubscription(
218
- tenantId: string,
219
- data: Omit<
220
- BufferSubscription,
221
- "id" | "tenant_id" | "created_at" | "updated_at" | "included_changes_balance"
222
- > & { included_changes_balance?: number },
223
- ): Promise<BufferUpsertResult> {
224
- const cfg = { ...this.config, tenantId };
225
- const row = { tenant_id: tenantId, ...data };
226
- // ON CONFLICT (tenant_id, stripe_subscription_id) DO UPDATE. Omitting
227
- // included_changes_balance preserves the current value (PostgREST updates only sent columns).
228
- const result = (await rest(
229
- cfg,
230
- "POST",
231
- "buffer_subscriptions",
232
- {
233
- body: row,
234
- prefer: "return=representation,resolution=merge-duplicates",
235
- },
236
- this.fetchImpl,
237
- )) as Array<BufferSubscription>;
238
- const sub = Array.isArray(result) ? result[0] : (result as BufferSubscription);
239
- return { id: sub.id, created: sub.created_at === sub.updated_at };
240
- }
241
-
242
- async findSubscriptionByStripeId(
243
- tenantId: string,
244
- stripeSubscriptionId: string,
245
- ): Promise<{
246
- id: string;
247
- included_changes_per_cycle: number;
248
- included_changes_balance: number;
249
- } | null> {
250
- const cfg = { ...this.config, tenantId };
251
- const rows = (await rest(
252
- cfg,
253
- "GET",
254
- "buffer_subscriptions",
255
- {
256
- params: {
257
- tenant_id: `eq.${tenantId}`,
258
- stripe_subscription_id: `eq.${stripeSubscriptionId}`,
259
- select: "id,included_changes_per_cycle,included_changes_balance",
260
- limit: "1",
261
- },
262
- },
263
- this.fetchImpl,
264
- )) as Array<{
265
- id: string;
266
- included_changes_per_cycle: number;
267
- included_changes_balance: number;
268
- }>;
269
- return Array.isArray(rows) && rows[0]?.id ? rows[0] : null;
270
- }
271
-
272
- async findActiveSubscriptionByOrganization(
273
- tenantId: string,
274
- organizationId: string,
275
- ): Promise<{ id: string; included_changes_balance: number } | null> {
276
- const cfg = { ...this.config, tenantId };
277
- const rows = (await rest(
278
- cfg,
279
- "GET",
280
- "buffer_subscriptions",
281
- {
282
- params: {
283
- tenant_id: `eq.${tenantId}`,
284
- organization_id: `eq.${organizationId}`,
285
- status: "eq.active",
286
- order: "created_at.desc",
287
- select: "id,included_changes_balance",
288
- limit: "1",
289
- },
290
- },
291
- this.fetchImpl,
292
- )) as Array<{ id: string; included_changes_balance: number }>;
293
- return Array.isArray(rows) && rows[0]?.id ? rows[0] : null;
294
- }
295
-
296
- async appendInvoice(
297
- tenantId: string,
298
- data: Omit<BufferInvoice, "id" | "tenant_id" | "created_at">,
299
- ): Promise<string> {
300
- const cfg = { ...this.config, tenantId };
301
- const row = { tenant_id: tenantId, ...data };
302
- // Idempotent by (tenant_id, stripe_invoice_id): ignore-duplicates returns [] on conflict.
303
- const result = (await rest(
304
- cfg,
305
- "POST",
306
- "buffer_invoices",
307
- {
308
- body: row,
309
- prefer: "return=representation,resolution=ignore-duplicates",
310
- },
311
- this.fetchImpl,
312
- )) as Array<BufferInvoice>;
313
- const inserted = Array.isArray(result) ? result[0] : (result as BufferInvoice | null);
314
- if (inserted?.id) return inserted.id;
315
- const existing = (await rest(
316
- cfg,
317
- "GET",
318
- "buffer_invoices",
319
- {
320
- params: {
321
- tenant_id: `eq.${tenantId}`,
322
- stripe_invoice_id: `eq.${data.stripe_invoice_id}`,
323
- select: "id",
324
- limit: "1",
325
- },
326
- },
327
- this.fetchImpl,
328
- )) as Array<{ id: string }>;
329
- return Array.isArray(existing) && existing[0]?.id ? existing[0].id : "";
330
- }
331
-
332
- async adjustChangeBalance(
333
- tenantId: string,
334
- subscriptionId: string,
335
- delta: number,
336
- ): Promise<number> {
337
- const cfg = { ...this.config, tenantId };
338
- // Read-modify-write. The shared sync worker drains per tenant serially, so
339
- // contention is low; a Postgres RPC would harden this against concurrency later.
340
- const rows = (await rest(
341
- cfg,
342
- "GET",
343
- "buffer_subscriptions",
344
- {
345
- params: { id: `eq.${subscriptionId}`, select: "included_changes_balance", limit: "1" },
346
- },
347
- this.fetchImpl,
348
- )) as Array<{ included_changes_balance: number }>;
349
- const current = Array.isArray(rows) && rows[0] ? rows[0].included_changes_balance : 0;
350
- const next = current + delta;
351
- await rest(
352
- cfg,
353
- "PATCH",
354
- "buffer_subscriptions",
355
- {
356
- params: { id: `eq.${subscriptionId}` },
357
- body: { included_changes_balance: next },
358
- },
359
- this.fetchImpl,
360
- );
361
- return next;
362
- }
363
-
364
- async writeOutbox(
365
- tenantId: string,
366
- tasks: Array<{
367
- op: SyncOutboxOp;
368
- payload: Record<string, unknown>;
369
- maxRetries?: number;
370
- }>,
371
- ): Promise<OutboxWriteResult> {
372
- if (tasks.length === 0) return { ids: [] };
373
- const cfg = { ...this.config, tenantId };
374
- const rows = tasks.map((t) => ({
375
- tenant_id: tenantId,
376
- op: t.op,
377
- payload: t.payload,
378
- max_retries: t.maxRetries ?? 5,
379
- status: "pending",
380
- scheduled_at: new Date().toISOString(),
381
- }));
382
- // ON CONFLICT (tenant_id, op, payload_hash, status) DO NOTHING
383
- const result = (await rest(
384
- cfg,
385
- "POST",
386
- "sync_outbox",
387
- {
388
- body: rows,
389
- prefer: "return=representation,resolution=ignore-duplicates",
390
- },
391
- this.fetchImpl,
392
- )) as Array<SyncOutboxRow>;
393
- const inserted = Array.isArray(result) ? result : [];
394
- return { ids: inserted.map((r) => r.id) };
395
- }
396
-
397
- async readPendingOutbox(tenantId: string, limit = 100): Promise<SyncOutboxRow[]> {
398
- const cfg = { ...this.config, tenantId };
399
- const result = (await rest(
400
- cfg,
401
- "GET",
402
- "sync_outbox",
403
- {
404
- params: {
405
- status: "eq.pending",
406
- order: "scheduled_at.asc",
407
- limit: String(limit),
408
- },
409
- },
410
- this.fetchImpl,
411
- )) as SyncOutboxRow[];
412
- return Array.isArray(result) ? result : [];
413
- }
414
-
415
- async updateOutboxStatus(
416
- id: string,
417
- status: SyncOutboxStatus,
418
- opts?: { lastError?: string; retryCount?: number },
419
- ): Promise<void> {
420
- const cfg = this.config;
421
- await rest(
422
- cfg,
423
- "PATCH",
424
- "sync_outbox",
425
- {
426
- params: { id: `eq.${id}` },
427
- body: {
428
- status,
429
- ...(status === "processing" ? { processing_at: new Date().toISOString() } : {}),
430
- ...(status === "done" || status === "failed" || status === "dead"
431
- ? { resolved_at: new Date().toISOString() }
432
- : {}),
433
- ...(opts?.lastError !== undefined ? { last_error: opts.lastError } : {}),
434
- ...(opts?.retryCount !== undefined ? { retry_count: opts.retryCount } : {}),
435
- },
436
- },
437
- this.fetchImpl,
438
- );
439
- }
440
-
441
- // --- CrmBufferReader: single-row reads ---
442
-
443
- async getContact(tenantId: string, contactId: string): Promise<BufferContact | null> {
444
- const cfg = { ...this.config, tenantId };
445
- const rows = (await rest(
446
- cfg,
447
- "GET",
448
- "buffer_contacts",
449
- {
450
- params: { id: `eq.${contactId}`, select: "*", limit: "1" },
451
- },
452
- this.fetchImpl,
453
- )) as Array<BufferContact>;
454
- return Array.isArray(rows) && rows[0] ? rows[0] : null;
455
- }
456
-
457
- async getDeal(tenantId: string, dealId: string): Promise<BufferDeal | null> {
458
- const cfg = { ...this.config, tenantId };
459
- const rows = (await rest(
460
- cfg,
461
- "GET",
462
- "buffer_deals",
463
- {
464
- params: { id: `eq.${dealId}`, select: "*", limit: "1" },
465
- },
466
- this.fetchImpl,
467
- )) as Array<BufferDeal>;
468
- return Array.isArray(rows) && rows[0] ? rows[0] : null;
469
- }
470
-
471
- async getOrganization(
472
- tenantId: string,
473
- organizationId: string,
474
- ): Promise<BufferOrganization | null> {
475
- const cfg = { ...this.config, tenantId };
476
- const rows = (await rest(
477
- cfg,
478
- "GET",
479
- "buffer_organizations",
480
- {
481
- params: { id: `eq.${organizationId}`, select: "*", limit: "1" },
482
- },
483
- this.fetchImpl,
484
- )) as Array<BufferOrganization>;
485
- return Array.isArray(rows) && rows[0] ? rows[0] : null;
486
- }
487
-
488
- // --- CrmBufferReader: patch back destination IDs ---
489
-
490
- async patchContactPipedriveId(
491
- tenantId: string,
492
- contactId: string,
493
- pipedrivePersonId: number,
494
- ): Promise<void> {
495
- const cfg = { ...this.config, tenantId };
496
- await rest(
497
- cfg,
498
- "PATCH",
499
- "buffer_contacts",
500
- {
501
- params: { id: `eq.${contactId}` },
502
- body: { pipedrive_person_id: pipedrivePersonId },
503
- },
504
- this.fetchImpl,
505
- );
506
- }
507
-
508
- async patchOrganizationPipedriveId(
509
- tenantId: string,
510
- organizationId: string,
511
- pipedriveOrgId: number,
512
- ): Promise<void> {
513
- const cfg = { ...this.config, tenantId };
514
- await rest(
515
- cfg,
516
- "PATCH",
517
- "buffer_organizations",
518
- {
519
- params: { id: `eq.${organizationId}` },
520
- body: { pipedrive_org_id: pipedriveOrgId },
521
- },
522
- this.fetchImpl,
523
- );
524
- }
525
-
526
- async patchDealPipedriveIds(
527
- tenantId: string,
528
- dealId: string,
529
- patch: DealPipedriveIdPatch,
530
- ): Promise<void> {
531
- const cfg = { ...this.config, tenantId };
532
- await rest(
533
- cfg,
534
- "PATCH",
535
- "buffer_deals",
536
- {
537
- params: { id: `eq.${dealId}` },
538
- body: patch,
539
- },
540
- this.fetchImpl,
541
- );
542
- }
543
-
544
- // --- RFC-0386: lifecycle sync reads ---
545
-
546
- async getSubscription(
547
- tenantId: string,
548
- subscriptionId: string,
549
- ): Promise<BufferSubscription | null> {
550
- const cfg = { ...this.config, tenantId };
551
- const rows = (await rest(
552
- cfg,
553
- "GET",
554
- "buffer_subscriptions",
555
- {
556
- params: { id: `eq.${subscriptionId}`, select: "*", limit: "1" },
557
- },
558
- this.fetchImpl,
559
- )) as Array<BufferSubscription>;
560
- return Array.isArray(rows) && rows[0] ? rows[0] : null;
561
- }
562
-
563
- async getInvoice(tenantId: string, invoiceId: string): Promise<BufferInvoice | null> {
564
- const cfg = { ...this.config, tenantId };
565
- const rows = (await rest(
566
- cfg,
567
- "GET",
568
- "buffer_invoices",
569
- {
570
- params: { id: `eq.${invoiceId}`, select: "*", limit: "1" },
571
- },
572
- this.fetchImpl,
573
- )) as Array<BufferInvoice>;
574
- return Array.isArray(rows) && rows[0] ? rows[0] : null;
575
- }
576
-
577
- async patchSubscriptionPipedriveDealId(
578
- tenantId: string,
579
- subscriptionId: string,
580
- pipedriveDealId: number,
581
- ): Promise<void> {
582
- const cfg = { ...this.config, tenantId };
583
- await rest(
584
- cfg,
585
- "PATCH",
586
- "buffer_subscriptions",
587
- {
588
- params: { id: `eq.${subscriptionId}` },
589
- body: { pipedrive_deal_id: pipedriveDealId },
590
- },
591
- this.fetchImpl,
592
- );
593
- }
594
- }
595
-
596
- /**
597
- * Factory: create a CrmBufferClient backed by Supabase REST for the given tenant.
598
- * Inject the returned client into the adapter or the sync worker.
599
- */
600
- export function createSupabaseCrmBufferClient(
601
- config: SupabaseClientConfig,
602
- fetchImpl: FetchImpl = fetch,
603
- ): CrmBufferClient {
604
- return new SupabaseCrmBufferClient(config, fetchImpl);
605
- }
@@ -1,28 +0,0 @@
1
- /*
2
- <MODULE_CONTRACT>
3
- <purpose>@warpgogol/werkstatt-shared/integration-adapter-supabase-crm public entry point. Exports the DestinationAdapter
4
- (supabaseBufferDestinationAdapter), the required secret names, and the Supabase client factory.
5
- Re-exports CrmBuffer types from @warpgogol/werkstatt-shared/integration/crm-buffer for convenience.</purpose>
6
- <non-goals>
7
- <item>Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file.</item>
8
- </non-goals>
9
- </MODULE_CONTRACT>
10
- <CHANGE_SUMMARY>
11
- <item>Lagebild MVP: initial public entry point.</item>
12
- </CHANGE_SUMMARY>
13
- */
14
-
15
- export { supabaseBufferDestinationAdapter, SUPABASE_BUFFER_SECRETS } from "./adapter.ts";
16
- export { createSupabaseCrmBufferClient, SupabaseCrmBufferClient } from "./client.ts";
17
- export type { SupabaseClientConfig } from "./client.ts";
18
- export {
19
- createSyncTarget,
20
- PipedriveSyncTarget,
21
- resolvePipedriveStageUpdate,
22
- STAGE_MAP,
23
- } from "./pipedrive-sync-target.ts";
24
- export type {
25
- CrmSyncTarget,
26
- SyncTargetCredentials,
27
- PipedriveCredentials,
28
- } from "./pipedrive-sync-target.ts";