@propertysimple/client 0.1.0 → 0.2.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 (41) hide show
  1. package/API.md +27 -73
  2. package/README.md +200 -57
  3. package/client/src/index.d.ts +257 -2422
  4. package/client/src/index.js +6 -69
  5. package/contracts/src/index.d.ts +0 -7
  6. package/contracts/src/index.js +0 -7
  7. package/contracts/src/openapi.js +1 -1
  8. package/contracts/src/operations.d.ts +950 -4678
  9. package/contracts/src/operations.js +6 -36
  10. package/contracts/src/opportunity-events.d.ts +2 -1
  11. package/contracts/src/opportunity-events.js +6 -1
  12. package/contracts/src/property-features.d.ts +2 -2
  13. package/contracts/src/property-features.js +62 -19
  14. package/contracts/src/routes.d.ts +28 -305
  15. package/contracts/src/routes.js +82 -354
  16. package/contracts/src/schemas.d.ts +1114 -1355
  17. package/contracts/src/schemas.js +45 -223
  18. package/contracts/src/search.d.ts +1366 -253
  19. package/contracts/src/search.js +117 -2
  20. package/openapi.json +19288 -38596
  21. package/package.json +1 -9
  22. package/client/src/directory-sync.d.ts +0 -123
  23. package/client/src/directory-sync.js +0 -404
  24. package/client/src/managed-sync.d.ts +0 -129
  25. package/client/src/managed-sync.js +0 -410
  26. package/contracts/src/directory-execution.d.ts +0 -320
  27. package/contracts/src/directory-execution.js +0 -122
  28. package/contracts/src/directory-identity.d.ts +0 -1255
  29. package/contracts/src/directory-identity.js +0 -129
  30. package/contracts/src/directory-sales.d.ts +0 -333
  31. package/contracts/src/directory-sales.js +0 -102
  32. package/contracts/src/directory.d.ts +0 -1088
  33. package/contracts/src/directory.js +0 -179
  34. package/contracts/src/intelligence.d.ts +0 -366
  35. package/contracts/src/intelligence.js +0 -157
  36. package/contracts/src/professional-intelligence.d.ts +0 -256
  37. package/contracts/src/professional-intelligence.js +0 -90
  38. package/contracts/src/publication.d.ts +0 -44
  39. package/contracts/src/publication.js +0 -42
  40. package/contracts/src/smart-feed.d.ts +0 -3731
  41. package/contracts/src/smart-feed.js +0 -109
@@ -4,7 +4,6 @@ import { LocationSchema } from './location.js';
4
4
  import { redactListingAddress } from './location-serving.js';
5
5
  import { SharedListingFields, SourceFieldsSchema, ProfessionalContactsSchema, } from './catalog-fields.js';
6
6
  export * from './catalog-fields.js';
7
- export * from './intelligence.js';
8
7
  /** Wire types are intentionally independent of database rows and provider payloads. */
9
8
  export const Id = z.string().min(1).max(256);
10
9
  export const Timestamp = z.iso.datetime({ offset: true });
@@ -313,8 +312,6 @@ const ListingQueryFields = DatasetPageQuery.extend({
313
312
  address_q: z.string().trim().min(2).max(500).optional(),
314
313
  market_status: MarketStatus.optional(),
315
314
  agent_id: Id.optional(),
316
- party_id: Id.optional(),
317
- inventory_basis: z.literal('listing_representation').optional(),
318
315
  mls_number: z.string().max(128).optional(),
319
316
  modified_since: Timestamp.optional(),
320
317
  source_modified_since: Timestamp.optional(),
@@ -327,7 +324,6 @@ const ListingQueryFields = DatasetPageQuery.extend({
327
324
  .regex(/^\d+(?:\.\d{1,2})?$/)
328
325
  .optional(),
329
326
  min_bedrooms: z.coerce.number().min(0).max(100).optional(),
330
- features: z.array(z.string().min(1).max(80)).max(20).optional(),
331
327
  bbox: z
332
328
  .string()
333
329
  .max(128)
@@ -346,11 +342,7 @@ const ListingQueryFields = DatasetPageQuery.extend({
346
342
  }, 'Use west,south,east,north within geographic bounds, without crossing the antimeridian.')
347
343
  .optional(),
348
344
  });
349
- const explicitPartyInventory = (query) => Boolean(query.party_id) === Boolean(query.inventory_basis);
350
- const partyInventoryMessage = {
351
- message: 'party_id and inventory_basis=listing_representation must be supplied together.',
352
- };
353
- export const ListingQuery = ListingQueryFields.refine(explicitPartyInventory, partyInventoryMessage);
345
+ export const ListingQuery = ListingQueryFields;
354
346
  export const IdentityQuery = z.strictObject({ source: Id, key: Id });
355
347
  export const IdentitySchema = z.strictObject({
356
348
  source: Id,
@@ -358,7 +350,40 @@ export const IdentitySchema = z.strictObject({
358
350
  listing_id: Id,
359
351
  episode_id: Id,
360
352
  });
353
+ /** /v1/listing-changes. Without a cursor, `start` picks where the stream begins:
354
+ * `now` (from this moment) or `all` (every listing first, then changes). */
355
+ export const ListingChangeQuery = DatasetPageQuery.extend({
356
+ start: z.enum(['now', 'all']).default('now'),
357
+ include_contacts: z.union([z.boolean(), z.enum(['true', 'false'])]).optional(),
358
+ });
359
+ /** One listing's current state. `visible` carries the listing as /v1/listings
360
+ * renders it; `hidden` (present but not displayable) and `removed` (absent from a
361
+ * complete enumeration) carry only the identity. Contacts follow the
362
+ * /contacts rule and appear only when requested with contacts:read. */
363
+ export const ListingChangeSchema = z.strictObject({
364
+ listing_id: Id,
365
+ episode_id: Id,
366
+ revision: z.string(),
367
+ source_identities: z.array(z.strictObject({ source: Id, key: Id })),
368
+ status: z.enum(['visible', 'hidden', 'removed']),
369
+ listing: ListingSchema.nullable(),
370
+ contacts: ListingContactsSchema.nullable().optional(),
371
+ });
361
372
  export const AgentIdentitySchema = z.strictObject({ source: Id, key: Id, agent_id: Id });
373
+ /** Suppression evidence only; never consent, identity matching or contact freshness. */
374
+ export const AgentEmailSharingSchema = z
375
+ .strictObject({
376
+ source: Id,
377
+ key: Id,
378
+ agent_id: Id,
379
+ email: z.email().max(320).nullable(),
380
+ sharing_count: z.number().int().min(1).max(10_000_000).nullable(),
381
+ observed_at: Timestamp,
382
+ basis: z.literal('stored_source_agent_email'),
383
+ coverage: z.literal('known_source_agents'),
384
+ matching: z.literal('exact_stored_email'),
385
+ })
386
+ .refine((value) => (value.email === null) === (value.sharing_count === null));
362
387
  export function page(item) {
363
388
  return z.strictObject({
364
389
  data: z.array(item),
@@ -367,11 +392,19 @@ export function page(item) {
367
392
  meta: z.record(z.string(), Json),
368
393
  });
369
394
  }
370
- /** Operational source health is live metadata; immutable listing snapshots keep their original observation times. */
395
+ /** Operational source health is live metadata; immutable listing snapshots keep their original observation times.
396
+ * Each time is the capture start of a completed, non-dry import run. */
371
397
  export const CatalogSourceFreshnessSchema = z.strictObject({
372
398
  source: Id,
399
+ /** The newest completed run of any kind: change-scan freshness. */
373
400
  last_completed_enumeration_at: Timestamp.nullable(),
401
+ /** Withdrawal coverage: the newest completed nightly full or removal sweep. Both are
402
+ * complete key enumerations that resolve absence, so a listing present now was in
403
+ * that key list or was seen after it. */
374
404
  last_full_enumeration_at: Timestamp.nullable(),
405
+ /** The newest completed nightly full, which also re-fetches every listing. Sweeps do
406
+ * not move it, so a failed full stays visible here while sweeps keep coverage. */
407
+ last_full_refresh_at: Timestamp.nullable().optional(),
375
408
  state: z.enum(['fresh', 'stale', 'never_completed']),
376
409
  freshness_window_seconds: z.number().int().positive(),
377
410
  /** Continuous sources separate change-scan freshness from withdrawal coverage. */
@@ -406,12 +439,10 @@ export const ListingMapQuery = ListingQueryFields.omit({
406
439
  limit: true,
407
440
  sort: true,
408
441
  include_total: true,
409
- })
410
- .extend({
442
+ }).extend({
411
443
  bbox: ListingQueryFields.shape.bbox.unwrap(),
412
444
  limit: z.coerce.number().int().min(1).max(1000).default(500),
413
- })
414
- .refine(explicitPartyInventory, partyInventoryMessage);
445
+ });
415
446
  export const ListingMapSchema = z.strictObject({
416
447
  data: z.array(z.strictObject({
417
448
  id: Id,
@@ -441,27 +472,6 @@ export const SimilarListingsSchema = z.strictObject({
441
472
  candidate_count: z.number().int().nonnegative(),
442
473
  candidates_truncated: z.boolean(),
443
474
  });
444
- export const SnapshotRequest = z.strictObject({ dataset_id: Id });
445
- export const SnapshotSchema = z.strictObject({
446
- id: Id,
447
- dataset_id: Id,
448
- status: z.enum(['building', 'ready', 'failed', 'expired']),
449
- schema_version: z.string(),
450
- snapshot_sequence: z.string(),
451
- replay_cursor: z.string(),
452
- created_at: Timestamp,
453
- expires_at: Timestamp,
454
- count: z.number().int().nonnegative().optional(),
455
- });
456
- export const CatalogChangeSchema = z.strictObject({
457
- id: Id,
458
- sequence: z.string(),
459
- listing_id: Id,
460
- revision: z.string(),
461
- operation: z.enum(['upsert', 'remove']),
462
- listing: ListingSchema.nullable(),
463
- published_at: Timestamp,
464
- });
465
475
  export const DomainEventSchema = z.strictObject({
466
476
  id: Id,
467
477
  sequence: z.string(),
@@ -472,194 +482,6 @@ export const DomainEventSchema = z.strictObject({
472
482
  observed_at: Timestamp,
473
483
  evidence: z.record(z.string(), Json),
474
484
  });
475
- export const ClassificationDefinitionSchema = z.strictObject({
476
- taxonomy_version: z.string(),
477
- attributes: z.array(z.strictObject({
478
- id: Id,
479
- description: z.string(),
480
- type: z.enum(['boolean', 'string', 'number']),
481
- values: z.array(z.string()).optional(),
482
- })),
483
- });
484
- export const ActionProfileSchema = z.strictObject({
485
- id: Id,
486
- family: Id,
487
- purpose: z.string(),
488
- owner_product: Id,
489
- channel: z.enum(['email', 'social', 'ads']),
490
- target_unit: z.enum(['contact_actions', 'content_actions', 'ad_candidates']),
491
- timezone: z.string(),
492
- policy_revision: z.string(),
493
- required_context: z.array(z.string()),
494
- enabled: z.boolean(),
495
- });
496
- export const OpportunityQuery = PageQuery.extend({
497
- profile_id: Id,
498
- account_id: Id.optional(),
499
- agent_id: Id.optional(),
500
- });
501
- export const OpportunitySchema = z.strictObject({
502
- id: Id,
503
- profile_id: Id,
504
- listing_id: Id,
505
- episode_id: Id,
506
- recipient_id: Id,
507
- intent_id: Id,
508
- primary_angle: z.string(),
509
- trigger_event_ids: z.array(Id),
510
- supporting_evidence_refs: z.array(z.string()),
511
- reason_codes: z.array(z.string()),
512
- priority_index: z.number(),
513
- valid_until: Timestamp,
514
- decision_revision: z.string(),
515
- });
516
- export const PlanRequest = z.strictObject({
517
- profile_id: Id,
518
- business_date: DateOnly,
519
- audience_id: Id,
520
- target: z.strictObject({
521
- count: z.number().int().min(1).max(100000),
522
- unit: z.enum(['contact_actions', 'content_actions', 'ad_candidates']),
523
- }),
524
- run_slot: z
525
- .string()
526
- .min(1)
527
- .max(64)
528
- .regex(/^[a-z0-9_-]+$/),
529
- });
530
- export const PlanSchema = z.strictObject({
531
- id: Id,
532
- profile_id: Id,
533
- business_date: DateOnly,
534
- timezone: z.string(),
535
- status: z.enum(['queued', 'building', 'ready', 'failed', 'cancelled', 'expired']),
536
- requested_count: z.number().int().nonnegative(),
537
- selected_count: z.number().int().nonnegative(),
538
- unique_recipients: z.number().int().nonnegative(),
539
- reserve_count: z.number().int().nonnegative(),
540
- shortfall: z.number().int().nonnegative(),
541
- completeness: z.enum(['pending', 'complete', 'partial']),
542
- decision_mode: z.enum(['jev', 'cached_jev', 'fixture']),
543
- created_at: Timestamp,
544
- expires_at: Timestamp,
545
- policy_revision: z.string(),
546
- exclusions: z.record(z.string(), z.number().int().nonnegative()),
547
- cutoffs: z.record(z.string(), z.string()),
548
- execution_counts: z.record(z.string(), z.number().int().nonnegative()).optional(),
549
- });
550
- export const ActionState = z.enum([
551
- 'unclaimed',
552
- 'reserved',
553
- 'dispatching',
554
- 'accepted',
555
- 'delivered',
556
- 'rejected',
557
- 'invalidated',
558
- 'unknown',
559
- ]);
560
- export const PlanItemReplacementSchema = z.strictObject({
561
- item_id: Id,
562
- opportunity_id: Id,
563
- listing_id: Id,
564
- recipient_id: Id,
565
- reason: z.string(),
566
- replaced_at: Timestamp,
567
- });
568
- export const PlanItemSchema = z.strictObject({
569
- id: Id,
570
- slot: z.number().int(),
571
- rank: z.number().int(),
572
- opportunity_id: Id,
573
- listing_id: Id,
574
- episode_id: Id,
575
- recipient_id: Id,
576
- primary_angle: z.string(),
577
- trigger_event_ids: z.array(Id),
578
- supporting_evidence_refs: z.array(z.string()),
579
- reason_codes: z.array(z.string()),
580
- valid_until: Timestamp,
581
- execution_state: ActionState,
582
- action_id: Id.nullable(),
583
- // Selection facts remain the original snapshot; this names the reserve executing its logical slot.
584
- replacement: PlanItemReplacementSchema.optional(),
585
- });
586
- export const DispatchRequest = z.strictObject({
587
- plan_id: Id,
588
- limit: z.number().int().min(1).max(100),
589
- worker_id: Id,
590
- });
591
- export const DispatchActionSchema = z.strictObject({
592
- id: Id,
593
- plan_item_id: Id,
594
- listing_id: Id,
595
- recipient_id: Id,
596
- action_kind: z.string(),
597
- lease_token: z.string(),
598
- lease_expires_at: Timestamp,
599
- endpoint: z.string(),
600
- payload: z.record(z.string(), Json),
601
- });
602
- export const DispatchBatchSchema = z.strictObject({
603
- id: Id,
604
- plan_id: Id,
605
- actions: z.array(DispatchActionSchema),
606
- exhausted: z.boolean(),
607
- });
608
- export const AttemptRequest = z.strictObject({ id: Id, lease_token: z.string().min(1).max(1024) });
609
- export const AttemptSchema = z.strictObject({
610
- id: Id,
611
- action_id: Id,
612
- provider_idempotency_key: z.string(),
613
- state: z.literal('dispatching'),
614
- started_at: Timestamp,
615
- replayed: z.boolean(),
616
- });
617
- export const ActionOutcomeSchema = z.strictObject({
618
- event_id: Id,
619
- action_id: Id,
620
- attempt_id: Id,
621
- provider: Id,
622
- provider_event_id: Id,
623
- outcome: z.enum(['accepted', 'delivered', 'rejected', 'unknown', 'bounced', 'complained']),
624
- occurred_at: Timestamp,
625
- details: z.record(z.string(), Json).optional(),
626
- });
627
- export const OutcomesRequest = z.strictObject({
628
- outcomes: z.array(ActionOutcomeSchema).min(1).max(100),
629
- });
630
- export const ProductContextEventSchema = z.strictObject({
631
- event_id: Id,
632
- producer_id: Id,
633
- stream_epoch: Id,
634
- coverage_id: Id,
635
- subject_id: Id,
636
- subject_type: Id,
637
- subject_version: z.number().int().nonnegative(),
638
- stream_sequence: z.number().int().nonnegative(),
639
- type: z.string().min(1).max(128),
640
- occurred_at: Timestamp,
641
- payload: z.record(z.string(), Json),
642
- });
643
- export const ContextEventsRequest = z.strictObject({
644
- events: z.array(ProductContextEventSchema).min(1).max(100),
645
- });
646
- export const BatchResultSchema = z.strictObject({
647
- results: z.array(z.strictObject({
648
- event_id: Id,
649
- status: z.enum(['accepted', 'duplicate', 'rejected']),
650
- code: z.string().nullable(),
651
- })),
652
- });
653
- export const WebhookRequest = z.strictObject({
654
- url: z.url(),
655
- dataset_id: Id,
656
- event_types: z.array(z.string()).min(1).max(20),
657
- });
658
- export const WebhookSchema = z.strictObject({
659
- id: Id,
660
- url: z.string(),
661
- status: z.enum(['pending_verification', 'active', 'disabled']),
662
- });
663
485
  export const CapabilitiesSchema = z.strictObject({
664
486
  api_version: z.literal('v1'),
665
487
  dataset_ids: z.array(Id),