@drawbridge/drawbridge-utils 0.0.176 → 0.0.178

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 (69) hide show
  1. package/dist/admin-B9ZaLvan.d.cts +697 -0
  2. package/dist/admin-C3HtEM6h.d.ts +697 -0
  3. package/dist/billing-Bc4yo9XG.d.cts +175 -0
  4. package/dist/billing-mNsKflmQ.d.ts +175 -0
  5. package/dist/billing.d.cts +1 -1
  6. package/dist/billing.d.ts +1 -1
  7. package/dist/connections/index.cjs +3391 -271
  8. package/dist/connections/index.d.cts +13 -4
  9. package/dist/connections/index.d.ts +13 -4
  10. package/dist/connections/index.js +3388 -270
  11. package/dist/features.cjs +3080 -241
  12. package/dist/features.d.cts +13 -4
  13. package/dist/features.d.ts +13 -4
  14. package/dist/features.js +3120 -275
  15. package/dist/http.cjs +10 -1
  16. package/dist/http.d.cts +10 -1
  17. package/dist/http.d.ts +10 -1
  18. package/dist/http.js +10 -1
  19. package/dist/{index-qY18QITf.d.cts → index-C2rxasGZ.d.cts} +2670 -354
  20. package/dist/{index-B8JhYfvU.d.ts → index-DOYCXtd7.d.ts} +2670 -354
  21. package/dist/oauth/index.d.cts +1 -1
  22. package/dist/oauth/index.d.ts +1 -1
  23. package/dist/oauth-BJDh0sdM.d.cts +527 -0
  24. package/dist/oauth-DveZMLHx.d.ts +527 -0
  25. package/dist/partner-BOZltuh2.d.ts +94 -0
  26. package/dist/partner-ed2OfW1J.d.cts +94 -0
  27. package/dist/plans.cjs +3079 -240
  28. package/dist/plans.d.cts +13 -4
  29. package/dist/plans.d.ts +13 -4
  30. package/dist/plans.js +3120 -275
  31. package/dist/pricing.cjs +3112 -273
  32. package/dist/pricing.d.cts +13 -4
  33. package/dist/pricing.d.ts +13 -4
  34. package/dist/pricing.js +3117 -272
  35. package/dist/providers.cjs +3082 -262
  36. package/dist/providers.d.cts +12 -3
  37. package/dist/providers.d.ts +12 -3
  38. package/dist/providers.js +3086 -260
  39. package/dist/sendgrid.cjs +10 -1
  40. package/dist/sendgrid.js +10 -1
  41. package/dist/shopify/admin.cjs +562 -0
  42. package/dist/shopify/admin.d.cts +3 -0
  43. package/dist/shopify/admin.d.ts +3 -0
  44. package/dist/shopify/admin.js +528 -0
  45. package/dist/shopify/billing.cjs +166 -0
  46. package/dist/shopify/billing.d.cts +3 -0
  47. package/dist/shopify/billing.d.ts +3 -0
  48. package/dist/shopify/billing.js +140 -0
  49. package/dist/shopify/constants.cjs +63 -0
  50. package/dist/shopify/constants.d.cts +58 -0
  51. package/dist/shopify/constants.d.ts +58 -0
  52. package/dist/shopify/constants.js +32 -0
  53. package/dist/shopify/oauth.cjs +509 -0
  54. package/dist/shopify/oauth.d.cts +7 -0
  55. package/dist/shopify/oauth.d.ts +7 -0
  56. package/dist/shopify/oauth.js +466 -0
  57. package/dist/shopify/partner.cjs +156 -0
  58. package/dist/shopify/partner.d.cts +3 -0
  59. package/dist/shopify/partner.d.ts +3 -0
  60. package/dist/shopify/partner.js +130 -0
  61. package/dist/shopify/storefront.cjs +611 -0
  62. package/dist/shopify/storefront.d.cts +3 -0
  63. package/dist/shopify/storefront.d.ts +3 -0
  64. package/dist/shopify/storefront.js +576 -0
  65. package/dist/storefront-C8FKOGeD.d.cts +659 -0
  66. package/dist/storefront-DJFGLqPl.d.ts +659 -0
  67. package/dist/twilio.cjs +10 -1
  68. package/dist/twilio.js +10 -1
  69. package/package.json +98 -68
@@ -0,0 +1,175 @@
1
+ import { request } from './http.cjs';
2
+ import { SHOPIFY_APP_API_VERSION } from './shopify/constants.cjs';
3
+
4
+ // Shopify App Pricing usage billing goes through the App Events API
5
+ // (api.shopify.com), NOT the shop-scoped Admin Billing API. Events are
6
+ // app-level: authenticated with the app's own client credentials and addressed
7
+ // by `shop_id`, so no merchant admin token or subscription line item is needed.
8
+ // An event whose `event_handle` matches a meter configured in the app's pricing
9
+ // is billed as `value × the meter's per-unit rate` within the current cycle.
10
+ // There is no capped amount — accrual is uncapped.
11
+
12
+ const APP_API = 'https://api.shopify.com';
13
+
14
+ // Env-overridable so a stable App Events version can be adopted without a
15
+ // republish; defaults to the only documented version today ('unstable').
16
+ const APP_API_VERSION = process.env.SHOPIFY_APP_API_VERSION || SHOPIFY_APP_API_VERSION;
17
+
18
+ // App-level bearer tokens from client_credentials, cached per clientId until
19
+ // just before they expire — one process can serve several apps' credentials,
20
+ // and a shared slot would send app A's token for app B's billing event.
21
+ // Cleared + re-fetched on a 401.
22
+ const cachedTokens = {};
23
+
24
+ const fetchAppToken = async ({ clientId, clientSecret, fetcher }) => {
25
+
26
+ const data = await request({
27
+ fetcher,
28
+ method : 'POST',
29
+ url : APP_API + '/auth/access_token',
30
+ body : {
31
+ client_id : clientId,
32
+ client_secret : clientSecret,
33
+ grant_type : 'client_credentials'
34
+ }
35
+ });
36
+
37
+ if( ! data?.access_token ){
38
+
39
+ throw new Error( 'Shopify app token request failed: no access token returned' );
40
+
41
+ }
42
+ const ttl = ( data.expires_in ? data.expires_in * 1000 : 60 * 60 * 1000 );
43
+
44
+ cachedTokens[ clientId ] = {
45
+ expiresAt : Date.now() + ttl - ( 60 * 1000 ),
46
+ token : data.access_token
47
+ };
48
+
49
+ return data.access_token;
50
+
51
+ };
52
+
53
+ const getAppToken = async ({ clientId, clientSecret, fetcher }) => {
54
+
55
+ const cached = cachedTokens[ clientId ];
56
+
57
+ if( cached && cached.expiresAt > Date.now() ){
58
+
59
+ return cached.token;
60
+
61
+ }
62
+ return fetchAppToken({ clientId, clientSecret, fetcher });
63
+
64
+ };
65
+
66
+ const toShopGid = ( shopId ) => (
67
+ String( shopId ).startsWith( 'gid://' ) ? String( shopId ) : ( 'gid://shopify/Shop/' + shopId )
68
+ );
69
+
70
+ // Send an event to the Shopify App Events API for an app-installed store. When
71
+ // `eventHandle` matches a usage meter in the app's pricing config, Shopify bills
72
+ // `value × the meter's per-unit rate` — so this doubles as the usage-billing call.
73
+ // `value` is the event quantity (e.g. the commission in cents).
74
+ //
75
+ // `eventHandle` is REQUIRED — deliberately no default. The handle is the string
76
+ // that decides whether the event bills or is silently ingested as a plain
77
+ // custom event, so every caller must pass the value it verified (the shopify
78
+ // manifest's events.order.handle for order billing); a package-level fallback
79
+ // is exactly the second unvalidated copy that let handle drift go unnoticed.
80
+ // `SHOPIFY_USAGE_ORDERS_EVENT_HANDLE` in lib/constants.js remains the recorded
81
+ // contract value the manifest must agree with. `idempotencyKey` (e.g. the
82
+ // order id) dedupes retries — Shopify's window is 24h for custom events but
83
+ // PERMANENT for billing events. Retries once on a 401 (stale cached token).
84
+ //
85
+ // `revision` suffixes the idempotency key ('.r' + revision) for a corrective
86
+ // resend once a key has been spent — a 202 consumes the key whether or not the
87
+ // event actually billed. The encoding lives HERE, and only here, because both
88
+ // drawbridge-sync's billing handler and drawbridge-api's recovery migration
89
+ // resend the same orders: two hand-rolled copies of the suffix would drift, and
90
+ // against a permanent idempotency ledger a drifted key either double-bills a
91
+ // merchant or misses the key it meant to supersede. Callers pass the bare order
92
+ // id plus revision and never build keys themselves.
93
+ //
94
+ // `clientId` / `clientSecret` are the app's own credentials — read from the
95
+ // encrypted `provider` record by the calling app, never from the environment —
96
+ // and back the client_credentials grant this authenticates with.
97
+ //
98
+ // `reference` is an optional opaque tracing attribute (e.g. the order document
99
+ // id) carried alongside the required `value`. Attributes allow at most 15
100
+ // scalar keys and must never contain personal data — pass ids, not people.
101
+ const sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
102
+
103
+ if( ! shopId || ! eventHandle || ! ( Number( value ) > 0 ) ){
104
+
105
+ throw new Error( 'sendAppEvent requires shopId, eventHandle, and a positive value' );
106
+
107
+ }
108
+ // Checked here, not in fetchAppToken, so a missing credential fails the same
109
+ // way on every call. Deferred to the grant it would ride the module-level
110
+ // cached token until that expired, turning a config error into an
111
+ // intermittent billing outage against a PERMANENT idempotency ledger.
112
+ if( ! clientId || ! clientSecret ){
113
+
114
+ throw new Error( 'sendAppEvent requires clientId and clientSecret' );
115
+
116
+ }
117
+ // The 64-char cap truncates the BASE, never the suffix — a cap that ate
118
+ // '.rN' would collide a corrective resend with the spent original. The
119
+ // Math.max keeps the slice bound non-negative should a revision ever
120
+ // arrive long enough to swallow the whole budget.
121
+ const suffix = ( revision ? '.r' + revision : '' );
122
+
123
+ const key = idempotencyKey && (
124
+ String( idempotencyKey ).slice( 0, Math.max( 0, 64 - suffix.length ) ) + suffix
125
+ );
126
+
127
+ const body = {
128
+ attributes : {
129
+ value : Number( value ),
130
+ ...( reference && {
131
+ reference : String( reference ).slice( 0, 128 )
132
+ })
133
+ },
134
+ event_handle : eventHandle,
135
+ shop_id : toShopGid( shopId ),
136
+ timestamp : timestamp || new Date().toISOString(),
137
+ ...( key && {
138
+ idempotency_key : key
139
+ })
140
+ };
141
+
142
+ const send = async ( token ) => request({
143
+ method : 'POST',
144
+ url : APP_API + '/app/' + APP_API_VERSION + '/events',
145
+ headers : {
146
+ 'Authorization' : 'Bearer ' + token
147
+ },
148
+ body
149
+ });
150
+
151
+ try {
152
+
153
+ return await send( await getAppToken({ clientId, clientSecret, fetcher }) );
154
+
155
+ } catch ( error ) {
156
+
157
+ if( error?.status === 401 ){
158
+
159
+ delete cachedTokens[ clientId ];
160
+
161
+ return send( await fetchAppToken({ clientId, clientSecret, fetcher }) );
162
+
163
+ }
164
+ throw error;
165
+
166
+ }
167
+
168
+ };
169
+
170
+ declare const shopifyBilling_sendAppEvent: typeof sendAppEvent;
171
+ declare namespace shopifyBilling {
172
+ export { shopifyBilling_sendAppEvent as sendAppEvent };
173
+ }
174
+
175
+ export { shopifyBilling as a, sendAppEvent as s };
@@ -0,0 +1,175 @@
1
+ import { request } from './http.js';
2
+ import { SHOPIFY_APP_API_VERSION } from './shopify/constants.js';
3
+
4
+ // Shopify App Pricing usage billing goes through the App Events API
5
+ // (api.shopify.com), NOT the shop-scoped Admin Billing API. Events are
6
+ // app-level: authenticated with the app's own client credentials and addressed
7
+ // by `shop_id`, so no merchant admin token or subscription line item is needed.
8
+ // An event whose `event_handle` matches a meter configured in the app's pricing
9
+ // is billed as `value × the meter's per-unit rate` within the current cycle.
10
+ // There is no capped amount — accrual is uncapped.
11
+
12
+ const APP_API = 'https://api.shopify.com';
13
+
14
+ // Env-overridable so a stable App Events version can be adopted without a
15
+ // republish; defaults to the only documented version today ('unstable').
16
+ const APP_API_VERSION = process.env.SHOPIFY_APP_API_VERSION || SHOPIFY_APP_API_VERSION;
17
+
18
+ // App-level bearer tokens from client_credentials, cached per clientId until
19
+ // just before they expire — one process can serve several apps' credentials,
20
+ // and a shared slot would send app A's token for app B's billing event.
21
+ // Cleared + re-fetched on a 401.
22
+ const cachedTokens = {};
23
+
24
+ const fetchAppToken = async ({ clientId, clientSecret, fetcher }) => {
25
+
26
+ const data = await request({
27
+ fetcher,
28
+ method : 'POST',
29
+ url : APP_API + '/auth/access_token',
30
+ body : {
31
+ client_id : clientId,
32
+ client_secret : clientSecret,
33
+ grant_type : 'client_credentials'
34
+ }
35
+ });
36
+
37
+ if( ! data?.access_token ){
38
+
39
+ throw new Error( 'Shopify app token request failed: no access token returned' );
40
+
41
+ }
42
+ const ttl = ( data.expires_in ? data.expires_in * 1000 : 60 * 60 * 1000 );
43
+
44
+ cachedTokens[ clientId ] = {
45
+ expiresAt : Date.now() + ttl - ( 60 * 1000 ),
46
+ token : data.access_token
47
+ };
48
+
49
+ return data.access_token;
50
+
51
+ };
52
+
53
+ const getAppToken = async ({ clientId, clientSecret, fetcher }) => {
54
+
55
+ const cached = cachedTokens[ clientId ];
56
+
57
+ if( cached && cached.expiresAt > Date.now() ){
58
+
59
+ return cached.token;
60
+
61
+ }
62
+ return fetchAppToken({ clientId, clientSecret, fetcher });
63
+
64
+ };
65
+
66
+ const toShopGid = ( shopId ) => (
67
+ String( shopId ).startsWith( 'gid://' ) ? String( shopId ) : ( 'gid://shopify/Shop/' + shopId )
68
+ );
69
+
70
+ // Send an event to the Shopify App Events API for an app-installed store. When
71
+ // `eventHandle` matches a usage meter in the app's pricing config, Shopify bills
72
+ // `value × the meter's per-unit rate` — so this doubles as the usage-billing call.
73
+ // `value` is the event quantity (e.g. the commission in cents).
74
+ //
75
+ // `eventHandle` is REQUIRED — deliberately no default. The handle is the string
76
+ // that decides whether the event bills or is silently ingested as a plain
77
+ // custom event, so every caller must pass the value it verified (the shopify
78
+ // manifest's events.order.handle for order billing); a package-level fallback
79
+ // is exactly the second unvalidated copy that let handle drift go unnoticed.
80
+ // `SHOPIFY_USAGE_ORDERS_EVENT_HANDLE` in lib/constants.js remains the recorded
81
+ // contract value the manifest must agree with. `idempotencyKey` (e.g. the
82
+ // order id) dedupes retries — Shopify's window is 24h for custom events but
83
+ // PERMANENT for billing events. Retries once on a 401 (stale cached token).
84
+ //
85
+ // `revision` suffixes the idempotency key ('.r' + revision) for a corrective
86
+ // resend once a key has been spent — a 202 consumes the key whether or not the
87
+ // event actually billed. The encoding lives HERE, and only here, because both
88
+ // drawbridge-sync's billing handler and drawbridge-api's recovery migration
89
+ // resend the same orders: two hand-rolled copies of the suffix would drift, and
90
+ // against a permanent idempotency ledger a drifted key either double-bills a
91
+ // merchant or misses the key it meant to supersede. Callers pass the bare order
92
+ // id plus revision and never build keys themselves.
93
+ //
94
+ // `clientId` / `clientSecret` are the app's own credentials — read from the
95
+ // encrypted `provider` record by the calling app, never from the environment —
96
+ // and back the client_credentials grant this authenticates with.
97
+ //
98
+ // `reference` is an optional opaque tracing attribute (e.g. the order document
99
+ // id) carried alongside the required `value`. Attributes allow at most 15
100
+ // scalar keys and must never contain personal data — pass ids, not people.
101
+ const sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
102
+
103
+ if( ! shopId || ! eventHandle || ! ( Number( value ) > 0 ) ){
104
+
105
+ throw new Error( 'sendAppEvent requires shopId, eventHandle, and a positive value' );
106
+
107
+ }
108
+ // Checked here, not in fetchAppToken, so a missing credential fails the same
109
+ // way on every call. Deferred to the grant it would ride the module-level
110
+ // cached token until that expired, turning a config error into an
111
+ // intermittent billing outage against a PERMANENT idempotency ledger.
112
+ if( ! clientId || ! clientSecret ){
113
+
114
+ throw new Error( 'sendAppEvent requires clientId and clientSecret' );
115
+
116
+ }
117
+ // The 64-char cap truncates the BASE, never the suffix — a cap that ate
118
+ // '.rN' would collide a corrective resend with the spent original. The
119
+ // Math.max keeps the slice bound non-negative should a revision ever
120
+ // arrive long enough to swallow the whole budget.
121
+ const suffix = ( revision ? '.r' + revision : '' );
122
+
123
+ const key = idempotencyKey && (
124
+ String( idempotencyKey ).slice( 0, Math.max( 0, 64 - suffix.length ) ) + suffix
125
+ );
126
+
127
+ const body = {
128
+ attributes : {
129
+ value : Number( value ),
130
+ ...( reference && {
131
+ reference : String( reference ).slice( 0, 128 )
132
+ })
133
+ },
134
+ event_handle : eventHandle,
135
+ shop_id : toShopGid( shopId ),
136
+ timestamp : timestamp || new Date().toISOString(),
137
+ ...( key && {
138
+ idempotency_key : key
139
+ })
140
+ };
141
+
142
+ const send = async ( token ) => request({
143
+ method : 'POST',
144
+ url : APP_API + '/app/' + APP_API_VERSION + '/events',
145
+ headers : {
146
+ 'Authorization' : 'Bearer ' + token
147
+ },
148
+ body
149
+ });
150
+
151
+ try {
152
+
153
+ return await send( await getAppToken({ clientId, clientSecret, fetcher }) );
154
+
155
+ } catch ( error ) {
156
+
157
+ if( error?.status === 401 ){
158
+
159
+ delete cachedTokens[ clientId ];
160
+
161
+ return send( await fetchAppToken({ clientId, clientSecret, fetcher }) );
162
+
163
+ }
164
+ throw error;
165
+
166
+ }
167
+
168
+ };
169
+
170
+ declare const shopifyBilling_sendAppEvent: typeof sendAppEvent;
171
+ declare namespace shopifyBilling {
172
+ export { shopifyBilling_sendAppEvent as sendAppEvent };
173
+ }
174
+
175
+ export { shopifyBilling as a, sendAppEvent as s };
@@ -13,7 +13,7 @@ import { createLogger } from '@drawbridge/drawbridge-telemetry';
13
13
  //
14
14
  // Sits on top of transactions.js (the generic credit/debit money rail) and
15
15
  // usage.js (the action-ledger + usage-totals primitives). Shopify order fees are
16
- // NOT billed here — they meter through @drawbridge/shopify's recordUsage and stay
16
+ // NOT billed here — they meter through the Shopify client's usage records and stay
17
17
  // Shopify-only by design. The Stripe meter EMIT for actions also stays in the
18
18
  // Stripe layer (utils must not depend on Stripe); billing.action.sum gives that
19
19
  // layer the authoritative number to report.
package/dist/billing.d.ts CHANGED
@@ -13,7 +13,7 @@ import { createLogger } from '@drawbridge/drawbridge-telemetry';
13
13
  //
14
14
  // Sits on top of transactions.js (the generic credit/debit money rail) and
15
15
  // usage.js (the action-ledger + usage-totals primitives). Shopify order fees are
16
- // NOT billed here — they meter through @drawbridge/shopify's recordUsage and stay
16
+ // NOT billed here — they meter through the Shopify client's usage records and stay
17
17
  // Shopify-only by design. The Stripe meter EMIT for actions also stays in the
18
18
  // Stripe layer (utils must not depend on Stripe); billing.action.sum gives that
19
19
  // layer the authoritative number to report.