@volter/twin-hubspot 0.1.37 → 1.0.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 (135) hide show
  1. package/README.md +9 -193
  2. package/dist/generated/pack-facts.json +42 -0
  3. package/dist/journeys/coverage.json +60 -0
  4. package/dist/journeys/customer-life.json +7 -0
  5. package/dist/journeys/customer-life.outline.md +6 -0
  6. package/dist/journeys/decisions.json +623 -0
  7. package/dist/journeys/demand.json +171 -0
  8. package/dist/journeys/score.json +82 -0
  9. package/dist/journeys/unreachable.json +12 -0
  10. package/dist/journeys/vendor-examples.json +7 -0
  11. package/dist/spec/SOURCE.md +40 -0
  12. package/dist/spec/openapi/associations.json.gz +0 -0
  13. package/dist/spec/openapi/companies.json.gz +0 -0
  14. package/dist/spec/openapi/contacts.json.gz +0 -0
  15. package/dist/spec/openapi/deals.json.gz +0 -0
  16. package/dist/spec/openapi/meetings.json.gz +0 -0
  17. package/dist/spec/openapi/oauth.json.gz +0 -0
  18. package/dist/spec/openapi/owners.json.gz +0 -0
  19. package/dist/spec/openapi/pipelines.json.gz +0 -0
  20. package/dist/spec/openapi/properties.json.gz +0 -0
  21. package/dist/spec/openapi/tickets.json.gz +0 -0
  22. package/dist/spec/patches.json +235 -0
  23. package/dist/spec/sources.json +32 -0
  24. package/dist/src/cli.js +10 -22
  25. package/dist/src/fetch.d.ts +2 -0
  26. package/dist/src/fetch.js +13 -0
  27. package/dist/src/generated/surface.gen.json +1 -1
  28. package/dist/src/index.d.ts +5 -11
  29. package/dist/src/index.js +10 -94
  30. package/dist/src/manifest.js +32 -66
  31. package/dist/src/semantics/index.d.ts +2 -0
  32. package/dist/src/semantics/index.js +1 -0
  33. package/dist/src/semantics/states.d.ts +1 -0
  34. package/dist/src/semantics/states.js +2 -0
  35. package/dist/src/server.d.ts +7 -0
  36. package/dist/src/server.js +7 -0
  37. package/generated/pack-facts.json +42 -0
  38. package/journeys/coverage.json +60 -0
  39. package/journeys/customer-life.json +7 -0
  40. package/journeys/customer-life.outline.md +6 -0
  41. package/journeys/decisions.json +623 -0
  42. package/journeys/demand.json +171 -0
  43. package/journeys/score.json +82 -0
  44. package/journeys/unreachable.json +12 -0
  45. package/journeys/vendor-examples.json +7 -0
  46. package/package.json +10 -23
  47. package/spec/SOURCE.md +40 -0
  48. package/spec/openapi/associations.json.gz +0 -0
  49. package/spec/openapi/companies.json.gz +0 -0
  50. package/spec/openapi/contacts.json.gz +0 -0
  51. package/spec/openapi/deals.json.gz +0 -0
  52. package/spec/openapi/meetings.json.gz +0 -0
  53. package/spec/openapi/oauth.json.gz +0 -0
  54. package/spec/openapi/owners.json.gz +0 -0
  55. package/spec/openapi/pipelines.json.gz +0 -0
  56. package/spec/openapi/properties.json.gz +0 -0
  57. package/spec/openapi/tickets.json.gz +0 -0
  58. package/spec/patches.json +235 -0
  59. package/spec/sources.json +32 -0
  60. package/src/cli.ts +10 -19
  61. package/src/fetch.ts +14 -0
  62. package/src/generated/surface.gen.json +1 -1
  63. package/src/index.ts +12 -152
  64. package/src/manifest.ts +33 -92
  65. package/src/semantics/index.ts +6 -0
  66. package/src/semantics/states.ts +2 -0
  67. package/src/server.ts +8 -0
  68. package/client/hubspot-mirror.css +0 -43
  69. package/client/hubspot-mirror.tsx +0 -132
  70. package/dist/client/hubspot-mirror.bundle.js +0 -516
  71. package/dist/client/hubspot-mirror.css +0 -43
  72. package/dist/client/hubspot-mirror.d.ts +0 -15
  73. package/dist/client/hubspot-mirror.js +0 -59
  74. package/dist/client/hubspot-mirror.tsx +0 -132
  75. package/dist/src/accounts.d.ts +0 -30
  76. package/dist/src/accounts.js +0 -122
  77. package/dist/src/hubspot-areas.d.ts +0 -10
  78. package/dist/src/hubspot-areas.js +0 -114
  79. package/dist/src/hubspot-budget.d.ts +0 -58
  80. package/dist/src/hubspot-budget.js +0 -176
  81. package/dist/src/hubspot-capabilities.d.ts +0 -3
  82. package/dist/src/hubspot-capabilities.js +0 -1588
  83. package/dist/src/hubspot-conformance.d.ts +0 -16
  84. package/dist/src/hubspot-conformance.js +0 -523
  85. package/dist/src/hubspot-connector.d.ts +0 -125
  86. package/dist/src/hubspot-connector.js +0 -390
  87. package/dist/src/hubspot-deferred-capabilities.d.ts +0 -6
  88. package/dist/src/hubspot-deferred-capabilities.js +0 -64
  89. package/dist/src/hubspot-mirror-ui.d.ts +0 -62
  90. package/dist/src/hubspot-mirror-ui.js +0 -152
  91. package/dist/src/hubspot-oauth.d.ts +0 -8
  92. package/dist/src/hubspot-oauth.js +0 -291
  93. package/dist/src/hubspot-server.d.ts +0 -24
  94. package/dist/src/hubspot-server.js +0 -116
  95. package/dist/src/hubspot-twin.d.ts +0 -65
  96. package/dist/src/hubspot-twin.js +0 -1558
  97. package/dist/src/portal.d.ts +0 -20
  98. package/dist/src/portal.js +0 -30
  99. package/dist/src/screens/account.d.ts +0 -1
  100. package/dist/src/screens/account.js +0 -139
  101. package/dist/src/screens/crm.d.ts +0 -2
  102. package/dist/src/screens/crm.js +0 -153
  103. package/dist/src/screens/developer.d.ts +0 -4
  104. package/dist/src/screens/developer.js +0 -191
  105. package/dist/src/screens/forms.d.ts +0 -5
  106. package/dist/src/screens/forms.js +0 -126
  107. package/dist/src/screens/page.d.ts +0 -21
  108. package/dist/src/screens/page.js +0 -49
  109. package/dist/src/screens/session.d.ts +0 -1
  110. package/dist/src/screens/session.js +0 -32
  111. package/dist/src/semantics/crm.d.ts +0 -8
  112. package/dist/src/semantics/crm.js +0 -101
  113. package/dist/src/webhooks.d.ts +0 -12
  114. package/dist/src/webhooks.js +0 -79
  115. package/src/accounts.ts +0 -127
  116. package/src/hubspot-areas.ts +0 -155
  117. package/src/hubspot-budget.ts +0 -202
  118. package/src/hubspot-capabilities.ts +0 -1523
  119. package/src/hubspot-conformance.ts +0 -537
  120. package/src/hubspot-connector.ts +0 -419
  121. package/src/hubspot-deferred-capabilities.ts +0 -99
  122. package/src/hubspot-journey.uitest.ts +0 -104
  123. package/src/hubspot-mirror-ui.ts +0 -166
  124. package/src/hubspot-oauth.tsx +0 -296
  125. package/src/hubspot-server.ts +0 -115
  126. package/src/hubspot-twin.ts +0 -1534
  127. package/src/portal.ts +0 -40
  128. package/src/screens/account.tsx +0 -129
  129. package/src/screens/crm.tsx +0 -154
  130. package/src/screens/developer.tsx +0 -181
  131. package/src/screens/forms.tsx +0 -117
  132. package/src/screens/page.tsx +0 -55
  133. package/src/screens/session.tsx +0 -36
  134. package/src/semantics/crm.ts +0 -116
  135. package/src/webhooks.ts +0 -82
@@ -1,11 +1,5 @@
1
- export { handleHubspotTwinRequest, correlationIdFor, resolveObjectType, HUBSPOT_RATE_LIMIT_HEADERS, LIST_LIMIT_MAX, OBJECT_TYPES, RESOURCE_TYPES, SEARCH_FILTER_OPERATORS, SEARCH_LIMIT_MAX, SEARCH_MAX_FILTERS_PER_GROUP, SEARCH_MAX_FILTERS_TOTAL, SEARCH_MAX_FILTER_GROUPS, SEARCH_RESULT_CAP, } from './hubspot-twin.js';
2
- export type { HubspotRequest, HubspotResponse, ObjectType } from './hubspot-twin.js';
3
- export { createHubspotTwinFetch, createHubspotTwinServer, type HubspotTwinFetchOptions } from './hubspot-server.js';
4
- export { hubspotRequestForAction, liveHubspotExecute, mapCrmObject, mapOwner, pollTimestamp, pullHubspotAll, pullHubspotObjects, pullHubspotOwners, pushPendingHubspotActions, syncHubspotFromReal, } from './hubspot-connector.js';
5
- export type { HubspotErrorBody, HubspotExecute, LiveHubspotOptions } from './hubspot-connector.js';
6
- export { HUBSPOT_BUDGET_CEILING, HUBSPOT_BUDGET_MAX_RETRY_AFTER_S, HUBSPOT_BUDGET_WINDOW_MS, HUBSPOT_CALL_WEIGHTS, HUBSPOT_RATE_BUDGET, HubspotBudget, HubspotBudgetError, hubspotBudgetPath, hubspotCallWeight, } from './hubspot-budget.js';
7
- export type { HubspotBudgetErrorKind, HubspotBudgetOptions, HubspotBudgetReservation, HubspotBudgetSnapshot } from './hubspot-budget.js';
8
- export { MIRROR_SECTIONS, SECTION_COLUMNS, TITLE_PROPERTY, buildHubspotMirrorClient, createHubspotMirrorServer, formatAmount, groupByStage, hubspotMirrorHtml, recordTitle, stageTone, } from './hubspot-mirror-ui.js';
9
- export type { HubspotRow, MirrorSectionKey, PillTone } from './hubspot-mirror-ui.js';
10
- import { type TwinPack } from '@volter/world-core';
11
- export declare const pack: TwinPack;
1
+ import { manifest } from './manifest.js';
2
+ export { createHubspotFetch } from './fetch.js';
3
+ export { createHubspotTwinServer } from './server.js';
4
+ export { manifest };
5
+ export declare const pack: import("@volter/world-core").PackDescriptor;
package/dist/src/index.js CHANGED
@@ -1,94 +1,10 @@
1
- // @volter/twin-hubspot — the HubSpot CRM twin (one vendor, one package), built on the shared
2
- // @volter/world-core kernel. REST transport over HubSpot's real v3/v4 CRM paths, the vendor's own
3
- // `{ results, paging: { next: { after } } }` collection envelope and
4
- // `{ status:'error', message, correlationId, category }` error envelope, stateful CRM records
5
- // (contacts / companies / deals / tickets) with properties, associations, pipelines and owners.
6
- // The CRM API accepts any bearer; the OAuth install (consent and token endpoints) mints the tokens an app is given.
7
- // Served as a derived pack (Protocol 3, src/manifest.ts). Conformance tooling lives in
8
- // @volter/world-tooling (a dev dependency) — not shipped in the runtime API.
9
- export { handleHubspotTwinRequest, correlationIdFor, resolveObjectType, HUBSPOT_RATE_LIMIT_HEADERS, LIST_LIMIT_MAX, OBJECT_TYPES, RESOURCE_TYPES, SEARCH_FILTER_OPERATORS, SEARCH_LIMIT_MAX, SEARCH_MAX_FILTERS_PER_GROUP, SEARCH_MAX_FILTERS_TOTAL, SEARCH_MAX_FILTER_GROUPS, SEARCH_RESULT_CAP, } from "./hubspot-twin.js";
10
- export { createHubspotTwinFetch, createHubspotTwinServer } from "./hubspot-server.js";
11
- export { hubspotRequestForAction, liveHubspotExecute, mapCrmObject, mapOwner, pollTimestamp, pullHubspotAll, pullHubspotObjects, pullHubspotOwners, pushPendingHubspotActions, syncHubspotFromReal, } from "./hubspot-connector.js";
12
- // The client-side rate budget — the fail-closed backstop `liveHubspotExecute` routes every live
13
- // request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
14
- // here is HubSpot's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
15
- // bindings. Exported so an operator can inspect spend (`snapshot`) and so a caller can catch
16
- // `HubspotBudgetError` by type; there is deliberately no export that disables the guard.
17
- export { HUBSPOT_BUDGET_CEILING, HUBSPOT_BUDGET_MAX_RETRY_AFTER_S, HUBSPOT_BUDGET_WINDOW_MS, HUBSPOT_CALL_WEIGHTS, HUBSPOT_RATE_BUDGET, HubspotBudget, HubspotBudgetError, hubspotBudgetPath, hubspotCallWeight, } from "./hubspot-budget.js";
18
- export { MIRROR_SECTIONS, SECTION_COLUMNS, TITLE_PROPERTY, buildHubspotMirrorClient, createHubspotMirrorServer, formatAmount, groupByStage, hubspotMirrorHtml, recordTitle, stageTone, } from "./hubspot-mirror-ui.js";
19
- // Registry descriptor: the pack self-describes so tooling can discover it.
20
- import { registerPack } from '@volter/world-core';
21
- import { performHubspotAction, syncHubspotFromRemote } from "./hubspot-connector.js";
22
- import { HUBSPOT_RATE_BUDGET as RATE_BUDGET } from "./hubspot-budget.js";
23
- export const pack = {
24
- // PROTOCOL 2 (docs/contributing/architecture.md#protocol-2-the-pack-is-a-plugin): the pack is a plugin — its wire, its tree, and its half of
25
- // the real state system. Moved 2026-09-08.
26
- protocol: '2',
27
- // HubSpot publishes webhooks for CRM object changes, which is what actually moves; the refresh
28
- // walks every object type and is the backstop.
29
- refresh: { every: '15m', webhook: true, onDemand: { atMost: '30s' } },
30
- stateSystem: { perform: performHubspotAction, refresh: syncHubspotFromRemote },
31
- // The round trip is a CONTACT create — HubSpot's own write, and the portal mints a fresh record
32
- // id for every one, so a second send on a branch is a second contact rather than a collision.
33
- roundTrip: { method: 'POST', path: '/crm/v3/objects/contacts', body: { properties: { email: 'round@trip.test', firstname: 'Round', lastname: 'Trip' } }, headers: { authorization: 'Bearer twin' } },
34
- parityOrigin: 'http://twin',
35
- vendor: 'hubspot',
36
- // The SAME object hubspot-budget.ts declares at module load — one source of truth, so
37
- // registering the pack and importing the connector can never arm two different ceilings.
38
- rateBudget: RATE_BUDGET,
39
- transport: 'rest',
40
- archetype: 'crud',
41
- bin: 'world-hubspot',
42
- // R2 adopted-as-debt, legibly: declared types no replay can create, each with its reason.
43
- resourcesUnreachable: {
44
- 'owner': "a portal user provisioned in HubSpot's admin, never through the CRM API",
45
- },
46
- resources: ['crm_object', 'property', 'property_group', 'pipeline', 'association', 'owner'],
47
- specSource: "nine documents of HubSpot's public spec catalog (api.hubspot.com/public/api/spec/v1/specs): Contacts, Companies, "
48
- + 'Deals, Tickets, Crm Owners, Properties and Pipelines v3, Associations v4 and OAuth v1, vendored under spec/openapi/ '
49
- + '(provenance in spec/SOURCE.md) and read as their union; the calls dubinc/dub makes (apps/web/lib/integrations/hubspot/).',
50
- description: 'HubSpot CRM twin — v3/v4 objects, properties, associations, pipelines and owners, with the vendor error envelope, paging.next.after and a CRM mirror.',
51
- // A browser-side HubSpot call goes to api.hubapi.com/crm/… — the dev proxy forwards '/crm/'
52
- // to the twin and strips the absolute host so calls come back same-origin.
53
- browserRouting: { apiPathPrefix: '/crm/', loaderHost: 'https://api.hubapi.com' },
54
- // ADOPTION — how an app repo betrays that it talks to HubSpot.
55
- // • `@hubspot/api-client` is the ONE official npm client of the surface modeled here.
56
- // • The `@hubspot/` scope is NOT claimed wholesale: it also carries the CMS/CLI toolchain
57
- // (`@hubspot/cli`, `@hubspot/local-dev-lib`, the `@hubspot/cms-components` family), which
58
- // are build tooling for a product area this twin explicitly does not model. Claiming the
59
- // scope would report a CMS-theme repo as covered by a CRM twin — the exact lie `covers`
60
- // exists to prevent (the `googleapis` precedent in covers.ts).
61
- // • `HUBSPOT` is the credential stem: HUBSPOT_ACCESS_TOKEN / HUBSPOT_API_KEY /
62
- // HUBSPOT_CLIENT_ID / HUBSPOT_CLIENT_SECRET all normalize to it under covers.ts's
63
- // ENV_SUFFIX rules (framework prefix stripped, suffix matched, remainder lowercased with
64
- // punctuation removed). `HUBSPOT_PRIVATE_APP_TOKEN` — the name HubSpot's own private-app
65
- // docs use — stems to `hubspotprivateapp` instead, because `_TOKEN` is the matched suffix,
66
- // so it is claimed explicitly rather than assumed.
67
- adoption: {
68
- // HubSpot's official Python client, the counterpart of `@hubspot/api-client`.
69
- pypi: ['hubspot-api-client'],
70
- sdks: ['@hubspot/api-client'],
71
- envStems: ['HUBSPOT', 'HUBSPOTPRIVATEAPP'],
72
- },
73
- // INTERCEPTION — api.hubapi.com is the SDK's only API server: every generated
74
- // `servers.ts` in @hubspot/api-client 14.0.1 declares exactly
75
- // `new ServerConfiguration("https://api.hubapi.com", {})`, and a sweep of the whole published
76
- // tarball finds no other API host. `app.hubspot.com` appears once, in
77
- // `OauthDiscovery.getAuthorizationUrl`: the URL a person is sent to for consent, which the twin serves
78
- // (hubspot-oauth.tsx).
79
- // app.hubspot.com carries every page the twin builds (sign-in, consent, apps, records, forms, settings) and
80
- // api.hsforms.com a form's submission (src/screens/).
81
- hosts: [{ host: 'api.hubapi.com' }, { host: 'app.hubspot.com' }, { host: 'api.hsforms.com' }],
82
- // WORLD WIRING — `endpointEnvNone`, and this is a ruling, not an omission. The SDK IS
83
- // base-URL-configurable, but through a CONSTRUCTOR OPTION (`IConfiguration.basePath`, read by
84
- // `ApiClientConfigurator.getBaseServer`), never an environment variable: nothing in
85
- // @hubspot/api-client 14.0.1 reads `process.env`, and HubSpot documents no `HUBSPOT_BASE_URL`.
86
- // Inventing one would make `covers` report a world covered while the app, reading no such var,
87
- // still talked to the real portal. Interception is the `hosts` entry above.
88
- endpointEnvNone: 'the @hubspot/api-client SDK takes its base URL from the `basePath` CONSTRUCTOR option '
89
- + '(IConfiguration.basePath → ApiClientConfigurator.getBaseServer), not from any environment '
90
- + 'variable — it reads no process.env at all, and HubSpot documents no base-URL env var. '
91
- + 'Interception is the api.hubapi.com host entry; inventing a HUBSPOT_BASE_URL the app does '
92
- + 'not read would report a world covered while its traffic still left the machine.',
93
- };
94
- registerPack(pack);
1
+ // @volter/twin-hubspot — the hubspot twin (Protocol 3). A fixed file (create-pack): the descriptor is the manifest's, and the
2
+ // kernel derives the vendor-backed half from the generated surface, unless the manifest declares none (vendorBacked.none;
3
+ // architecture, "The real-system adapters").
4
+ import { packOf, registerPack } from '@volter/world-core';
5
+ import { manifest } from "./manifest.js";
6
+ import surface from './generated/surface.gen.json' with { type: 'json' };
7
+ export { createHubspotFetch } from "./fetch.js";
8
+ export { createHubspotTwinServer } from "./server.js";
9
+ export { manifest };
10
+ export const pack = registerPack(packOf(manifest, surface));
@@ -1,68 +1,34 @@
1
1
  export const manifest = {
2
- vendor: 'hubspot',
3
- service: 'hubspot',
4
- body: {},
5
- // the core mints no id (it owns no operation); a record's id is the request handler's
6
- ids: { template: '{prefix}_{n}' },
7
- time: 'iso',
8
- error: { status: 'error', message: '{message}', category: '{code}' },
9
- readOnly: { status: 405, message: 'this twin was started read-only; omit readOnly to accept writes', code: 'TWIN_READ_ONLY' },
10
- malformedBody: { status: 400, message: 'Invalid input JSON', code: 'VALIDATION_ERROR' },
11
- notFound: { status: 404, message: 'resource not found', code: 'OBJECT_NOT_FOUND' },
12
- // `{results, paging: {next: {after}}}`, paged by `after` and `limit` (10 by default, 100 at most: the documents'
13
- // getPage operations). Required by the type; the core lists nothing.
14
- list: { style: 'envelope', envelope: { results: '{data}' }, limit: { param: 'limit', default: 10, max: 100 }, after: 'after' },
15
- deleted: {},
16
- // HubSpot's pages the life walks (src/screens/, src/hubspot-oauth.tsx), each built from @volter/world-ui's pieces.
17
- screens: [
18
- {
19
- id: 'sign-in', kind: 'flow', host: 'app.hubspot.com', path: '/login',
20
- demand: 'every person on a HubSpot page is who signed in there', status: 'done', controls: ['Email address', 'Password', 'Log in'],
21
- source: 'https://knowledge.hubspot.com/account-management/log-in-to-hubspot',
22
- },
23
- {
24
- id: 'oauth-consent', kind: 'flow', host: 'app.hubspot.com', path: '/oauth/authorize',
25
- demand: "Dub's HubSpot integration sends a workspace owner here to connect a portal (apps/web/lib/integrations/hubspot/oauth.ts, authUrl)",
26
- status: 'done', controls: ['Select an account', 'Connect app', 'Cancel', 'Type I accept the risk', 'Connect'],
27
- source: 'https://developers.hubspot.com/docs/apps/developer-platform/build-apps/manage-apps-in-hubspot',
28
- },
29
- {
30
- id: 'app-auth', kind: 'workspace', host: 'app.hubspot.com', path: '/developer-projects/:hubId/project/:name/component/:uid/auth',
31
- demand: "an app's client ID and secret are read here", status: 'done',
32
- source: 'https://developers.hubspot.com/docs/apps/developer-platform/build-apps/manage-apps-in-hubspot',
33
- },
34
- {
35
- id: 'app-distribution', kind: 'workspace', host: 'app.hubspot.com', path: '/developer-projects/:hubId/project/:name/component/:uid/distribution',
36
- demand: 'an OAuth marketplace app is published (Begin publishing, the AUP) before any install; a static app is installed and its token shown here',
37
- status: 'done', controls: ['Begin publishing', 'Sign', 'Install now', 'Show'],
38
- source: 'https://developers.hubspot.com/docs/apps/developer-platform/build-apps/manage-apps-in-hubspot',
39
- },
40
- {
41
- id: 'records', kind: 'workspace', host: 'app.hubspot.com', path: '/contacts/:hubId/record/:objectTypeId/:id',
42
- demand: "reps create deals on a contact's record and move a deal's stage; account managers add companies", status: 'done',
43
- controls: ['Create deal', 'Deal stage', 'Save', 'Create company'], source: 'https://knowledge.hubspot.com/records/create-records-on-the-updated-index-page',
44
- },
45
- {
46
- id: 'pipelines', kind: 'workspace', host: 'app.hubspot.com', path: '/settings/:hubId/objects/deals/pipelines',
47
- demand: "a custom pipeline's closed-won stage ID is read here (Dub's guide: \"enter the stage ID corresponding to your custom Closed Won stage\")",
48
- status: 'done', source: 'https://knowledge.hubspot.com/object-settings/set-up-and-customize-pipelines',
49
- },
50
- {
51
- id: 'forms', kind: 'workspace', host: 'app.hubspot.com', path: '/forms/:hubId',
52
- demand: "Dub's guide: \"In the HubSpot form builder, add a hidden field and map it to the Dub Id contact property\"", status: 'done',
53
- controls: ['Create form', 'Add hidden field'], source: 'https://dub.co/docs/integrations/hubspot',
54
- },
55
- {
56
- id: 'connected-apps', kind: 'workspace', host: 'app.hubspot.com', path: '/connected-apps/:hubId/installed',
57
- demand: 'a client uninstalls the app it connected', status: 'done', controls: ['Uninstall', 'Type uninstall'],
58
- source: 'https://knowledge.hubspot.com/marketplace/install-apps-in-the-hubspot-marketplace',
59
- },
60
- {
61
- id: 'account-and-billing', kind: 'workspace', host: 'app.hubspot.com', path: '/account-and-billing/:hubId/subscriptions',
62
- demand: "a company leaving HubSpot cancels its subscription's auto-renewal, then deletes the account once the subscription has ended",
63
- status: 'done', controls: ['Cancel auto-renewal', 'Next', 'Delete account', 'Enter your Hub ID to confirm', 'Yes, delete'],
64
- source: 'https://knowledge.hubspot.com/account/how-do-i-cancel-my-hubspot-account',
65
- },
66
- ],
67
- resources: {},
2
+ vendor: 'hubspot', service: 'hubspot', body: { json: 'always', form: { coerce: false } },
3
+ ids: { template: '{n}' }, time: 'iso',
4
+ // source: https://developers.hubspot.com/docs/api-reference/error-handling "return a JSON formatted response with details about the error"
5
+ error: { status: 'error', message: '{message}', correlationId: '{id}' },
6
+ readOnly: { status: 403, message: 'This World is read-only.' },
7
+ // source: https://developers.hubspot.com/docs/api-reference/webhooks-webhooks-v3/guide "404 Not Found : requested resource not found"
8
+ // The documentation gives the not-found class, not words for every unknown path; use that class in its documented JSON envelope.
9
+ notFound: { status: 404, message: 'requested resource not found' },
10
+ gap: { status: 404, message: 'requested resource not found' },
11
+ // source: https://developers.hubspot.com/docs/api-reference/webhooks-webhooks-v3/guide "Authorization : Bearer YOUR_ACCESS_TOKEN"
12
+ // source: https://developers.hubspot.com/docs/api-reference/error-handling "Returned when the authentication provided is invalid."
13
+ // Where documentation stops: The 401 rule describes invalid authentication, not missing-key versus unknown-route precedence; the gap stands.
14
+ auth: { header: 'authorization', scheme: 'Bearer', paths: '^/crm/', gateWhenAbsent: true,
15
+ missing: { status: 401, message: 'Unauthorized' }, invalid: { status: 401, message: 'Unauthorized' }, invalidKeys: ['invalid'] },
16
+ deleted: {}, resources: {},
17
+ unmodeled: ["get-/crm/v3/objects/0-3_getPage", "post-/crm/v3/objects/0-3_create", "get-/crm/v3/objects/0-3/{dealId}_getById", "patch-/crm/v3/objects/0-3/{dealId}_update", "delete-/crm/v3/objects/0-3/{dealId}_archive", "post-/crm/v3/objects/0-3/batch/archive_archive", "post-/crm/v3/objects/0-3/batch/create_create", "post-/crm/v3/objects/0-3/batch/read_read", "post-/crm/v3/objects/0-3/batch/update_update", "post-/crm/v3/objects/0-3/batch/upsert_upsert", "post-/crm/v3/objects/0-3/merge_merge", "post-/crm/v3/objects/0-3/search_doSearch", "get-/crm/v3/objects/companies_getPage", "post-/crm/v3/objects/companies_create", "get-/crm/v3/objects/companies/{companyId}_getById", "patch-/crm/v3/objects/companies/{companyId}_update", "delete-/crm/v3/objects/companies/{companyId}_archive", "post-/crm/v3/objects/companies/batch/archive_archive", "post-/crm/v3/objects/companies/batch/create_create", "post-/crm/v3/objects/companies/batch/read_read", "post-/crm/v3/objects/companies/batch/update_update", "post-/crm/v3/objects/companies/batch/upsert_upsert", "post-/crm/v3/objects/companies/merge_merge", "post-/crm/v3/objects/companies/search_doSearch", "get-/crm/v3/objects/contacts_getPage", "post-/crm/v3/objects/contacts_create", "get-/crm/v3/objects/contacts/{contactId}_getById", "patch-/crm/v3/objects/contacts/{contactId}_update", "delete-/crm/v3/objects/contacts/{contactId}_archive", "post-/crm/v3/objects/contacts/batch/archive_archive", "post-/crm/v3/objects/contacts/batch/create_create", "post-/crm/v3/objects/contacts/batch/read_read", "post-/crm/v3/objects/contacts/batch/update_update", "post-/crm/v3/objects/contacts/batch/upsert_upsert", "post-/crm/v3/objects/contacts/gdpr-delete_purge", "post-/crm/v3/objects/contacts/merge_merge", "post-/crm/v3/objects/contacts/search_doSearch", "get-/crm/v3/objects/meetings_getPage", "post-/crm/v3/objects/meetings_create", "get-/crm/v3/objects/meetings/{meetingId}_getById", "patch-/crm/v3/objects/meetings/{meetingId}_update", "delete-/crm/v3/objects/meetings/{meetingId}_archive", "post-/crm/v3/objects/meetings/batch/archive_archive", "post-/crm/v3/objects/meetings/batch/create_create", "post-/crm/v3/objects/meetings/batch/read_read", "post-/crm/v3/objects/meetings/batch/update_update", "post-/crm/v3/objects/meetings/batch/upsert_upsert", "post-/crm/v3/objects/meetings/search_doSearch", "get-/crm/v3/objects/tickets_getPage", "post-/crm/v3/objects/tickets_create", "get-/crm/v3/objects/tickets/{ticketId}_getById", "patch-/crm/v3/objects/tickets/{ticketId}_update", "delete-/crm/v3/objects/tickets/{ticketId}_archive", "post-/crm/v3/objects/tickets/batch/archive_archive", "post-/crm/v3/objects/tickets/batch/create_create", "post-/crm/v3/objects/tickets/batch/read_read", "post-/crm/v3/objects/tickets/batch/update_update", "post-/crm/v3/objects/tickets/batch/upsert_upsert", "post-/crm/v3/objects/tickets/merge_merge", "post-/crm/v3/objects/tickets/search_doSearch", "get-/crm/v3/owners_/crm/v3/owners", "get-/crm/v3/owners/{ownerId}_getById", "get-/crm/v3/pipelines/{objectType}_getAll", "post-/crm/v3/pipelines/{objectType}_create", "get-/crm/v3/pipelines/{objectType}/{pipelineId}_getById", "put-/crm/v3/pipelines/{objectType}/{pipelineId}_replace", "patch-/crm/v3/pipelines/{objectType}/{pipelineId}_update", "delete-/crm/v3/pipelines/{objectType}/{pipelineId}_archive", "get-/crm/v3/pipelines/{objectType}/{pipelineId}/audit_getAudit", "get-/crm/v3/pipelines/{objectType}/{pipelineId}/stages_getAll", "post-/crm/v3/pipelines/{objectType}/{pipelineId}/stages_create", "get-/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}_getById", "put-/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}_replace", "patch-/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}_update", "delete-/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}_archive", "get-/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}/audit_getAudit", "get-/crm/v3/properties/{objectType}_getAll", "post-/crm/v3/properties/{objectType}_create", "get-/crm/v3/properties/{objectType}/{propertyName}_getByName", "patch-/crm/v3/properties/{objectType}/{propertyName}_update", "delete-/crm/v3/properties/{objectType}/{propertyName}_archive", "post-/crm/v3/properties/{objectType}/batch/archive_archive", "post-/crm/v3/properties/{objectType}/batch/create_create", "post-/crm/v3/properties/{objectType}/batch/read_read", "get-/crm/v3/properties/{objectType}/groups_getAll", "post-/crm/v3/properties/{objectType}/groups_create", "get-/crm/v3/properties/{objectType}/groups/{groupName}_getByName", "patch-/crm/v3/properties/{objectType}/groups/{groupName}_update", "delete-/crm/v3/properties/{objectType}/groups/{groupName}_archive", "post-/crm/v4/associations/{fromObjectType}/{toObjectType}/batch/archive_archive", "post-/crm/v4/associations/{fromObjectType}/{toObjectType}/batch/associate/default_createDefault", "post-/crm/v4/associations/{fromObjectType}/{toObjectType}/batch/create_create", "post-/crm/v4/associations/{fromObjectType}/{toObjectType}/batch/labels/archive_archiveLabels", "post-/crm/v4/associations/{fromObjectType}/{toObjectType}/batch/read_getPage", "post-/crm/v4/associations/usage/high-usage-report/{userId}_request", "put-/crm/v4/objects/{fromObjectType}/{fromObjectId}/associations/default/{toObjectType}/{toObjectId}_createDefault", "get-/crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType}_getPage", "put-/crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType}/{toObjectId}_create", "delete-/crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType}/{toObjectId}_archive", "get-/oauth/v1/access-tokens/{token}_get", "get-/oauth/v1/refresh-tokens/{token}_get", "delete-/oauth/v1/refresh-tokens/{token}_archive", "post-/oauth/v1/token_create"],
18
+ vendorBacked: { none: 'No vendor resource or operation is served; every spec operation answers the gap.' },
19
+ // source: https://developers.hubspot.com/docs/developer-tooling/platform/usage-guidelines "110 requests every 10 seconds"
20
+ rateBudget: { windowMs: 60000, ceiling: 660, defaultWeight: 1, maxRetryAfterSeconds: 60, reason: 'Public OAuth allowance of 110 requests per 10 seconds, expressed as 660 per minute.',
21
+ allowance: { perMinute: 660, source: 'https://developers.hubspot.com/docs/developer-tooling/platform/usage-guidelines' } },
22
+ discovery: { twinOf: 'HubSpot CRM, Meetings and OAuth v1 APIs', stores: 'No vendor resources',
23
+ behavior: 'Every spec operation answers the vendor-shaped gap; Cal.com and Dub require an optional HubSpot app installation.' },
24
+ descriptor: {
25
+ protocol: '3', transport: 'rest', archetype: 'crud', bin: 'world-hubspot', resources: [],
26
+ specSource: 'HubSpot CRM, Meetings and OAuth v1 OpenAPI documents; provenance in spec/SOURCE.md',
27
+ description: 'HubSpot API twin: every spec operation answers the vendor-shaped gap.',
28
+ adoption: { sdks: ['@hubspot/api-client'], pypi: ['hubspot-api-client'], envStems: ['HUBSPOT', 'HUBSPOTPRIVATEAPP'] },
29
+ hosts: [{ host: 'api.hubapi.com' }, { host: 'app.hubspot.com', demandPathPattern: false }, { host: 'api.hsforms.com' }],
30
+ endpointEnvNone: 'The optional integrations address HubSpot hosts; no SDK base-URL environment variable is injected.',
31
+ // source: https://raw.githubusercontent.com/dubinc/dub/7e0101363b2ab30afc537c1893f159d1dcba513a/apps/web/lib/integrations/hubspot/oauth.ts "process.env.HUBSPOT_CLIENT_ID"
32
+ clientCasesNone: 'Optional integration calls remain unserved; no SDK case is claimed for the retained gap.',
33
+ },
68
34
  };
@@ -0,0 +1,2 @@
1
+ import type { Handler } from '@volter/world-core';
2
+ export declare const hubspotSemantics: Record<string, Handler>;
@@ -0,0 +1 @@
1
+ export const hubspotSemantics = {};
@@ -0,0 +1 @@
1
+ export declare const states: {};
@@ -0,0 +1,2 @@
1
+ // No vendor resources are served, so there are no state machines or notState rulings.
2
+ export const states = {};
@@ -0,0 +1,7 @@
1
+ import { type PackFetchOptions } from '@volter/world-core';
2
+ export declare function createHubspotTwinServer(options?: PackFetchOptions & {
3
+ port?: number;
4
+ }): Promise<{
5
+ port: number;
6
+ stop: () => void;
7
+ }>;
@@ -0,0 +1,7 @@
1
+ // FETCH-FIRST (runtime contract R12b): a fixed file, the kernel's serve seam around the pack's fetch (./fetch.ts).
2
+ import { serveHttp } from '@volter/world-core';
3
+ import { createHubspotFetch } from "./fetch.js";
4
+ export async function createHubspotTwinServer(options = {}) {
5
+ const server = await serveHttp({ port: options.port ?? 0, idleTimeout: 60, fetch: createHubspotFetch(options) });
6
+ return { port: server.port ?? options.port ?? 0, stop: () => server.stop(true) };
7
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "//": "GENERATED by scripts/pack-facts.ts — current pack descriptors and explicit world-runtime server backings; no prior-artifact entries are retained.",
3
+ "packs": {
4
+ "hubspot": {
5
+ "adoption": {
6
+ "sdks": [
7
+ "@hubspot/api-client"
8
+ ],
9
+ "pypi": [
10
+ "hubspot-api-client"
11
+ ],
12
+ "envStems": [
13
+ "HUBSPOT",
14
+ "HUBSPOTPRIVATEAPP"
15
+ ]
16
+ },
17
+ "hosts": [
18
+ {
19
+ "host": "api.hubapi.com"
20
+ },
21
+ {
22
+ "host": "app.hubspot.com",
23
+ "demandPathPattern": false
24
+ },
25
+ {
26
+ "host": "api.hsforms.com"
27
+ }
28
+ ],
29
+ "archetype": "crud",
30
+ "protocol": {
31
+ "declared": "3",
32
+ "major": 3,
33
+ "standing": "current"
34
+ },
35
+ "transport": "rest",
36
+ "resources": [],
37
+ "endpointEnvNone": "The optional integrations address HubSpot hosts; no SDK base-URL environment variable is injected.",
38
+ "serveExport": "createHubspotTwinServer"
39
+ }
40
+ },
41
+ "undeclared": []
42
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "vendor": "hubspot",
3
+ "missing": 0,
4
+ "stale": [],
5
+ "code": {
6
+ "lines": 49,
7
+ "run": 47,
8
+ "recorded": 2,
9
+ "missing": 0
10
+ },
11
+ "state": {
12
+ "declared": 0,
13
+ "reached": 0,
14
+ "recorded": 0,
15
+ "missing": [],
16
+ "undeclared": []
17
+ },
18
+ "files": [
19
+ {
20
+ "file": "semantics/index.ts",
21
+ "lines": {
22
+ "found": 2,
23
+ "hit": 2
24
+ },
25
+ "missing": []
26
+ },
27
+ {
28
+ "file": "manifest.ts",
29
+ "lines": {
30
+ "found": 26,
31
+ "hit": 26
32
+ },
33
+ "missing": []
34
+ },
35
+ {
36
+ "file": "index.ts",
37
+ "lines": {
38
+ "found": 7,
39
+ "hit": 7
40
+ },
41
+ "missing": []
42
+ },
43
+ {
44
+ "file": "server.ts",
45
+ "lines": {
46
+ "found": 4,
47
+ "hit": 2
48
+ },
49
+ "missing": []
50
+ },
51
+ {
52
+ "file": "fetch.ts",
53
+ "lines": {
54
+ "found": 10,
55
+ "hit": 10
56
+ },
57
+ "missing": []
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "vendor": "hubspot",
3
+ "host": "api.hubapi.com",
4
+ "author": "Codex",
5
+ "about": "Demanded calls are recorded in demand.json and remain unserved in the retained gap. Scope is unchanged pending a separate implementation; supplying credentials is not opt-in.",
6
+ "steps": []
7
+ }
@@ -0,0 +1,6 @@
1
+ # HubSpot customer life
2
+
3
+ Author: `Codex`.
4
+
5
+ Cal.com’s shipped CRM app is demand; Dub’s additional integration installation is optional. Pinned evidence for both is in `demand.json`.
6
+ Demanded calls are recorded in demand.json and remain unserved in the retained gap. Scope is unchanged pending a separate implementation; supplying credentials is not opt-in.