@shipfox/api-integration-jira 7.1.0 → 9.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 20 files with swc (383.29ms)
2
+ Successfully compiled: 20 files with swc (378.85ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @shipfox/api-integration-jira
2
2
 
3
+ ## 9.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 475ce59: Republishes all public packages after restoring release authorization.
8
+ - Updated dependencies [475ce59]
9
+ - @shipfox/api-auth-context@9.0.1
10
+ - @shipfox/api-integration-jira-dto@9.0.1
11
+ - @shipfox/api-integration-spi@0.2.1
12
+ - @shipfox/config@1.2.3
13
+ - @shipfox/node-drizzle@0.3.3
14
+ - @shipfox/node-fastify@0.3.1
15
+ - @shipfox/node-opentelemetry@0.6.1
16
+ - @shipfox/node-postgres@0.4.3
17
+
18
+ ## 9.0.0
19
+
20
+ ### Patch Changes
21
+
22
+ - 4a6d124: Separates Integrations provider SPI contracts from the public DTO surface.
23
+ - Updated dependencies [4a6d124]
24
+ - @shipfox/api-integration-spi@0.2.0
25
+ - @shipfox/api-auth-context@9.0.0
26
+ - @shipfox/api-integration-jira-dto@9.0.0
27
+ - @shipfox/config@1.2.2
28
+ - @shipfox/node-drizzle@0.3.2
29
+ - @shipfox/node-fastify@0.3.0
30
+ - @shipfox/node-opentelemetry@0.6.0
31
+ - @shipfox/node-postgres@0.4.2
32
+
33
+ ## 8.0.0
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [7f227c6]
38
+ - @shipfox/api-integration-core-dto@8.0.0
39
+ - @shipfox/api-integration-jira-dto@8.0.0
40
+
3
41
  ## 7.1.0
4
42
 
5
43
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import { IntegrationProviderError } from '@shipfox/api-integration-core-dto';
1
+ import { IntegrationProviderError } from '@shipfox/api-integration-spi';
2
2
  export declare class JiraConnectionNotFoundError extends Error {
3
3
  constructor(connectionId: string);
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,wBAAwB,EAAC,MAAM,mCAAmC,CAAC;AAE3E,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,YAAY,EAAE,MAAM;CAIjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,YAAY,EAAE,MAAM;CAIjC;AAED,qBAAa,iCAAkC,SAAQ,KAAK;gBAC9C,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAMlD;AAED,qBAAa,4BAA6B,SAAQ,wBAAwB;CAAG;AAE7E,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,SAA+B;CAInD;AAED,qBAAa,kCAAmC,SAAQ,KAAK;;CAK5D;AAED,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,aAAa,EAAE,MAAM;aACrB,mBAAmB,EAAE,MAAM,GAAG,SAAS;gBADvC,aAAa,EAAE,MAAM,EACrB,mBAAmB,EAAE,MAAM,GAAG,SAAS;CAK1D;AAED,qBAAa,mCAAoC,SAAQ,KAAK;aAChC,aAAa,EAAE,MAAM,EAAE;gBAAvB,aAAa,EAAE,MAAM,EAAE;CAIpD;AAED,qBAAa,gCAAiC,SAAQ,KAAK;;CAO1D;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAC/C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC7C,YAAY,EAAE,MAAM;CAIjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;aACxB,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM;CAIjD;AAED,qBAAa,8BAA+B,SAAQ,KAAK;gBAC3C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,iCAAkC,SAAQ,KAAK;;CAK3D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,wBAAwB,EAAC,MAAM,8BAA8B,CAAC;AAEtE,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,YAAY,EAAE,MAAM;CAIjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,YAAY,EAAE,MAAM;CAIjC;AAED,qBAAa,iCAAkC,SAAQ,KAAK;gBAC9C,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAMlD;AAED,qBAAa,4BAA6B,SAAQ,wBAAwB;CAAG;AAE7E,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,SAA+B;CAInD;AAED,qBAAa,kCAAmC,SAAQ,KAAK;;CAK5D;AAED,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,aAAa,EAAE,MAAM;aACrB,mBAAmB,EAAE,MAAM,GAAG,SAAS;gBADvC,aAAa,EAAE,MAAM,EACrB,mBAAmB,EAAE,MAAM,GAAG,SAAS;CAK1D;AAED,qBAAa,mCAAoC,SAAQ,KAAK;aAChC,aAAa,EAAE,MAAM,EAAE;gBAAvB,aAAa,EAAE,MAAM,EAAE;CAIpD;AAED,qBAAa,gCAAiC,SAAQ,KAAK;;CAO1D;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAC/C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC7C,YAAY,EAAE,MAAM;CAIjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;aACxB,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM;CAIjD;AAED,qBAAa,8BAA+B,SAAQ,KAAK;gBAC3C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,iCAAkC,SAAQ,KAAK;;CAK3D"}
@@ -1,4 +1,4 @@
1
- import { IntegrationProviderError } from '@shipfox/api-integration-core-dto';
1
+ import { IntegrationProviderError } from '@shipfox/api-integration-spi';
2
2
  export class JiraConnectionNotFoundError extends Error {
3
3
  constructor(connectionId){
4
4
  super(`Jira integration connection was not found: ${connectionId}`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["import {IntegrationProviderError} from '@shipfox/api-integration-core-dto';\n\nexport class JiraConnectionNotFoundError extends Error {\n constructor(connectionId: string) {\n super(`Jira integration connection was not found: ${connectionId}`);\n this.name = 'JiraConnectionNotFoundError';\n }\n}\n\nexport class JiraAccessTokenMissingError extends Error {\n constructor(connectionId: string) {\n super(`Jira access token is missing for connection: ${connectionId}`);\n this.name = 'JiraAccessTokenMissingError';\n }\n}\n\nexport class JiraInstallationSiteMismatchError extends Error {\n constructor(connectionId: string, cloudId: string) {\n super(\n `Jira connection is already linked to a different site: ${connectionId} (attempted ${cloudId})`,\n );\n this.name = 'JiraInstallationSiteMismatchError';\n }\n}\n\nexport class JiraIntegrationProviderError extends IntegrationProviderError {}\n\nexport class JiraInstallStateError extends Error {\n constructor(message = 'Invalid Jira install state') {\n super(message);\n this.name = 'JiraInstallStateError';\n }\n}\n\nexport class JiraInstallStateActorMismatchError extends Error {\n constructor() {\n super('Jira install state was created by a different user');\n this.name = 'JiraInstallStateActorMismatchError';\n }\n}\n\nexport class JiraOAuthCallbackError extends Error {\n constructor(\n public readonly providerError: string,\n public readonly providerDescription: string | undefined,\n ) {\n super(providerDescription ?? `Jira OAuth callback failed: ${providerError}`);\n this.name = 'JiraOAuthCallbackError';\n }\n}\n\nexport class JiraAuthorizationScopeMismatchError extends Error {\n constructor(public readonly missingScopes: string[]) {\n super(`Jira authorization is missing usable site scopes: ${missingScopes.join(', ')}`);\n this.name = 'JiraAuthorizationScopeMismatchError';\n }\n}\n\nexport class JiraOfflineAccessNotGrantedError extends Error {\n constructor() {\n super(\n 'Jira authorization did not grant offline access; reconnect and approve the requested scopes',\n );\n this.name = 'JiraOfflineAccessNotGrantedError';\n }\n}\n\nexport class JiraInstallationAlreadyLinkedError extends Error {\n constructor(cloudId: string) {\n super(`Jira site is already linked to another Shipfox workspace: ${cloudId}`);\n this.name = 'JiraInstallationAlreadyLinkedError';\n }\n}\n\nexport class JiraConnectionAlreadyLinkedError extends Error {\n constructor(connectionId: string) {\n super(`Integration connection is already linked to another Jira site: ${connectionId}`);\n this.name = 'JiraConnectionAlreadyLinkedError';\n }\n}\n\nexport class JiraTokenUnrefreshableError extends Error {\n constructor(public readonly connectionId: string) {\n super(`Jira token cannot be refreshed; reconnect is required: ${connectionId}`);\n this.name = 'JiraTokenUnrefreshableError';\n }\n}\n\nexport class JiraSiteSelectionMismatchError extends Error {\n constructor(cloudId: string) {\n super(`Jira site was not included in the authorized site selection: ${cloudId}`);\n this.name = 'JiraSiteSelectionMismatchError';\n }\n}\n\nexport class JiraPendingSelectionNotFoundError extends Error {\n constructor() {\n super('The Jira site selection expired or has already been completed; start over');\n this.name = 'JiraPendingSelectionNotFoundError';\n }\n}\n"],"names":["IntegrationProviderError","JiraConnectionNotFoundError","Error","connectionId","name","JiraAccessTokenMissingError","JiraInstallationSiteMismatchError","cloudId","JiraIntegrationProviderError","JiraInstallStateError","message","JiraInstallStateActorMismatchError","JiraOAuthCallbackError","providerError","providerDescription","JiraAuthorizationScopeMismatchError","missingScopes","join","JiraOfflineAccessNotGrantedError","JiraInstallationAlreadyLinkedError","JiraConnectionAlreadyLinkedError","JiraTokenUnrefreshableError","JiraSiteSelectionMismatchError","JiraPendingSelectionNotFoundError"],"mappings":"AAAA,SAAQA,wBAAwB,QAAO,oCAAoC;AAE3E,OAAO,MAAMC,oCAAoCC;IAC/C,YAAYC,YAAoB,CAAE;QAChC,KAAK,CAAC,CAAC,2CAA2C,EAAEA,cAAc;QAClE,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMC,oCAAoCH;IAC/C,YAAYC,YAAoB,CAAE;QAChC,KAAK,CAAC,CAAC,6CAA6C,EAAEA,cAAc;QACpE,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAME,0CAA0CJ;IACrD,YAAYC,YAAoB,EAAEI,OAAe,CAAE;QACjD,KAAK,CACH,CAAC,uDAAuD,EAAEJ,aAAa,YAAY,EAAEI,QAAQ,CAAC,CAAC;QAEjG,IAAI,CAACH,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMI,qCAAqCR;AAA0B;AAE5E,OAAO,MAAMS,8BAA8BP;IACzC,YAAYQ,UAAU,4BAA4B,CAAE;QAClD,KAAK,CAACA;QACN,IAAI,CAACN,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMO,2CAA2CT;IACtD,aAAc;QACZ,KAAK,CAAC;QACN,IAAI,CAACE,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMQ,+BAA+BV;IAC1C,YACE,AAAgBW,aAAqB,EACrC,AAAgBC,mBAAuC,CACvD;QACA,KAAK,CAACA,uBAAuB,CAAC,4BAA4B,EAAED,eAAe,QAH3DA,gBAAAA,oBACAC,sBAAAA;QAGhB,IAAI,CAACV,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMW,4CAA4Cb;IACvD,YAAY,AAAgBc,aAAuB,CAAE;QACnD,KAAK,CAAC,CAAC,kDAAkD,EAAEA,cAAcC,IAAI,CAAC,OAAO,QAD3DD,gBAAAA;QAE1B,IAAI,CAACZ,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMc,yCAAyChB;IACpD,aAAc;QACZ,KAAK,CACH;QAEF,IAAI,CAACE,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMe,2CAA2CjB;IACtD,YAAYK,OAAe,CAAE;QAC3B,KAAK,CAAC,CAAC,0DAA0D,EAAEA,SAAS;QAC5E,IAAI,CAACH,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMgB,yCAAyClB;IACpD,YAAYC,YAAoB,CAAE;QAChC,KAAK,CAAC,CAAC,+DAA+D,EAAEA,cAAc;QACtF,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMiB,oCAAoCnB;IAC/C,YAAY,AAAgBC,YAAoB,CAAE;QAChD,KAAK,CAAC,CAAC,uDAAuD,EAAEA,cAAc,QADpDA,eAAAA;QAE1B,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMkB,uCAAuCpB;IAClD,YAAYK,OAAe,CAAE;QAC3B,KAAK,CAAC,CAAC,6DAA6D,EAAEA,SAAS;QAC/E,IAAI,CAACH,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMmB,0CAA0CrB;IACrD,aAAc;QACZ,KAAK,CAAC;QACN,IAAI,CAACE,IAAI,GAAG;IACd;AACF"}
1
+ {"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["import {IntegrationProviderError} from '@shipfox/api-integration-spi';\n\nexport class JiraConnectionNotFoundError extends Error {\n constructor(connectionId: string) {\n super(`Jira integration connection was not found: ${connectionId}`);\n this.name = 'JiraConnectionNotFoundError';\n }\n}\n\nexport class JiraAccessTokenMissingError extends Error {\n constructor(connectionId: string) {\n super(`Jira access token is missing for connection: ${connectionId}`);\n this.name = 'JiraAccessTokenMissingError';\n }\n}\n\nexport class JiraInstallationSiteMismatchError extends Error {\n constructor(connectionId: string, cloudId: string) {\n super(\n `Jira connection is already linked to a different site: ${connectionId} (attempted ${cloudId})`,\n );\n this.name = 'JiraInstallationSiteMismatchError';\n }\n}\n\nexport class JiraIntegrationProviderError extends IntegrationProviderError {}\n\nexport class JiraInstallStateError extends Error {\n constructor(message = 'Invalid Jira install state') {\n super(message);\n this.name = 'JiraInstallStateError';\n }\n}\n\nexport class JiraInstallStateActorMismatchError extends Error {\n constructor() {\n super('Jira install state was created by a different user');\n this.name = 'JiraInstallStateActorMismatchError';\n }\n}\n\nexport class JiraOAuthCallbackError extends Error {\n constructor(\n public readonly providerError: string,\n public readonly providerDescription: string | undefined,\n ) {\n super(providerDescription ?? `Jira OAuth callback failed: ${providerError}`);\n this.name = 'JiraOAuthCallbackError';\n }\n}\n\nexport class JiraAuthorizationScopeMismatchError extends Error {\n constructor(public readonly missingScopes: string[]) {\n super(`Jira authorization is missing usable site scopes: ${missingScopes.join(', ')}`);\n this.name = 'JiraAuthorizationScopeMismatchError';\n }\n}\n\nexport class JiraOfflineAccessNotGrantedError extends Error {\n constructor() {\n super(\n 'Jira authorization did not grant offline access; reconnect and approve the requested scopes',\n );\n this.name = 'JiraOfflineAccessNotGrantedError';\n }\n}\n\nexport class JiraInstallationAlreadyLinkedError extends Error {\n constructor(cloudId: string) {\n super(`Jira site is already linked to another Shipfox workspace: ${cloudId}`);\n this.name = 'JiraInstallationAlreadyLinkedError';\n }\n}\n\nexport class JiraConnectionAlreadyLinkedError extends Error {\n constructor(connectionId: string) {\n super(`Integration connection is already linked to another Jira site: ${connectionId}`);\n this.name = 'JiraConnectionAlreadyLinkedError';\n }\n}\n\nexport class JiraTokenUnrefreshableError extends Error {\n constructor(public readonly connectionId: string) {\n super(`Jira token cannot be refreshed; reconnect is required: ${connectionId}`);\n this.name = 'JiraTokenUnrefreshableError';\n }\n}\n\nexport class JiraSiteSelectionMismatchError extends Error {\n constructor(cloudId: string) {\n super(`Jira site was not included in the authorized site selection: ${cloudId}`);\n this.name = 'JiraSiteSelectionMismatchError';\n }\n}\n\nexport class JiraPendingSelectionNotFoundError extends Error {\n constructor() {\n super('The Jira site selection expired or has already been completed; start over');\n this.name = 'JiraPendingSelectionNotFoundError';\n }\n}\n"],"names":["IntegrationProviderError","JiraConnectionNotFoundError","Error","connectionId","name","JiraAccessTokenMissingError","JiraInstallationSiteMismatchError","cloudId","JiraIntegrationProviderError","JiraInstallStateError","message","JiraInstallStateActorMismatchError","JiraOAuthCallbackError","providerError","providerDescription","JiraAuthorizationScopeMismatchError","missingScopes","join","JiraOfflineAccessNotGrantedError","JiraInstallationAlreadyLinkedError","JiraConnectionAlreadyLinkedError","JiraTokenUnrefreshableError","JiraSiteSelectionMismatchError","JiraPendingSelectionNotFoundError"],"mappings":"AAAA,SAAQA,wBAAwB,QAAO,+BAA+B;AAEtE,OAAO,MAAMC,oCAAoCC;IAC/C,YAAYC,YAAoB,CAAE;QAChC,KAAK,CAAC,CAAC,2CAA2C,EAAEA,cAAc;QAClE,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMC,oCAAoCH;IAC/C,YAAYC,YAAoB,CAAE;QAChC,KAAK,CAAC,CAAC,6CAA6C,EAAEA,cAAc;QACpE,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAME,0CAA0CJ;IACrD,YAAYC,YAAoB,EAAEI,OAAe,CAAE;QACjD,KAAK,CACH,CAAC,uDAAuD,EAAEJ,aAAa,YAAY,EAAEI,QAAQ,CAAC,CAAC;QAEjG,IAAI,CAACH,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMI,qCAAqCR;AAA0B;AAE5E,OAAO,MAAMS,8BAA8BP;IACzC,YAAYQ,UAAU,4BAA4B,CAAE;QAClD,KAAK,CAACA;QACN,IAAI,CAACN,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMO,2CAA2CT;IACtD,aAAc;QACZ,KAAK,CAAC;QACN,IAAI,CAACE,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMQ,+BAA+BV;IAC1C,YACE,AAAgBW,aAAqB,EACrC,AAAgBC,mBAAuC,CACvD;QACA,KAAK,CAACA,uBAAuB,CAAC,4BAA4B,EAAED,eAAe,QAH3DA,gBAAAA,oBACAC,sBAAAA;QAGhB,IAAI,CAACV,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMW,4CAA4Cb;IACvD,YAAY,AAAgBc,aAAuB,CAAE;QACnD,KAAK,CAAC,CAAC,kDAAkD,EAAEA,cAAcC,IAAI,CAAC,OAAO,QAD3DD,gBAAAA;QAE1B,IAAI,CAACZ,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMc,yCAAyChB;IACpD,aAAc;QACZ,KAAK,CACH;QAEF,IAAI,CAACE,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMe,2CAA2CjB;IACtD,YAAYK,OAAe,CAAE;QAC3B,KAAK,CAAC,CAAC,0DAA0D,EAAEA,SAAS;QAC5E,IAAI,CAACH,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMgB,yCAAyClB;IACpD,YAAYC,YAAoB,CAAE;QAChC,KAAK,CAAC,CAAC,+DAA+D,EAAEA,cAAc;QACtF,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMiB,oCAAoCnB;IAC/C,YAAY,AAAgBC,YAAoB,CAAE;QAChD,KAAK,CAAC,CAAC,uDAAuD,EAAEA,cAAc,QADpDA,eAAAA;QAE1B,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMkB,uCAAuCpB;IAClD,YAAYK,OAAe,CAAE;QAC3B,KAAK,CAAC,CAAC,6DAA6D,EAAEA,SAAS;QAC/E,IAAI,CAACH,IAAI,GAAG;IACd;AACF;AAEA,OAAO,MAAMmB,0CAA0CrB;IACrD,aAAc;QACZ,KAAK,CAAC;QACN,IAAI,CAACE,IAAI,GAAG;IACd;AACF"}
@@ -1,5 +1,5 @@
1
1
  import type { UserContextMembership } from '@shipfox/api-auth-context';
2
- import type { IntegrationConnection } from '@shipfox/api-integration-core-dto';
2
+ import type { IntegrationConnection } from '@shipfox/api-integration-spi';
3
3
  import type { JiraAccessibleResource, JiraApiClient } from '#api/client.js';
4
4
  import type { JiraPendingSelectionStore } from '#core/pending.js';
5
5
  import type { JiraTokenStore } from '#core/tokens.js';
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/core/install.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAC;AAE7E,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAoB,MAAM,gBAAgB,CAAC;AAC7F,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAapD,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAChD,YAAY,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACzD,0BAA0B,CAAC,KAAK,EAAE;QAChC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;KACnD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrB,yBAAyB,CAAC,KAAK,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACvD,uBAAuB,CACrB,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,0BAA0B,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG;IAAC,KAAK,EAAE,sBAAsB,EAAE,CAAA;CAAC,CAAC,CA4B5E;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,GAAG;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GACjE,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAkBxC;AAED,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACzD,0BAA0B,CAAC,KAAK,EAAE;QAChC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;KACnD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB,GAAG,OAAO,CAAC,KAAK,CAAC,CAGjB"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/core/install.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAoB,MAAM,gBAAgB,CAAC;AAC7F,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAapD,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAChD,YAAY,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACzD,0BAA0B,CAAC,KAAK,EAAE;QAChC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;KACnD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrB,yBAAyB,CAAC,KAAK,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACvD,uBAAuB,CACrB,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,0BAA0B,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG;IAAC,KAAK,EAAE,sBAAsB,EAAE,CAAA;CAAC,CAAC,CA4B5E;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,GAAG;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GACjE,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAkBxC;AAED,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACzD,0BAA0B,CAAC,KAAK,EAAE;QAChC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;KACnD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB,GAAG,OAAO,CAAC,KAAK,CAAC,CAGjB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/install.ts"],"sourcesContent":["import type {UserContextMembership} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-core-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {JiraAccessibleResource, JiraApiClient, JiraAuthorization} from '#api/client.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from './errors.js';\nimport {assertJiraAuthorizationScopes} from './scopes.js';\nimport {verifyJiraInstallState} from './state.js';\n\nexport interface ConnectJiraInstallationInput {\n workspaceId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n tokenExpiresAt: Date | null;\n displayName: string;\n}\n\nexport interface HandleJiraCallbackParams {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n code: string;\n state: string;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n disconnectJiraInstallation(input: {connectionId: string}): Promise<void>;\n}\n\nexport async function handleJiraCallback(\n params: HandleJiraCallbackParams,\n): Promise<IntegrationConnection<'jira'> | {sites: JiraAccessibleResource[]}> {\n const claims = await verifyClaims(params, params.state);\n const authorization = await params.jira.exchangeAuthorizationCode({code: params.code});\n if (!authorization.refreshToken) throw new JiraOfflineAccessNotGrantedError();\n const sites = await params.jira.getAccessibleResources({accessToken: authorization.accessToken});\n if (sites.length === 0)\n throw new JiraIntegrationProviderError(\n 'access-denied',\n 'Jira authorization did not grant access to a site',\n );\n if (sites.length > 1) {\n await params.pendingStore.save({\n workspaceId: claims.workspaceId,\n state: params.state,\n authorization,\n sites,\n });\n return {sites};\n }\n const site = sites[0];\n if (!site) {\n throw new JiraIntegrationProviderError(\n 'malformed-provider-response',\n 'Jira accessible-resources response did not contain a selected site',\n );\n }\n assertJiraAuthorizationScopes(site.scopes);\n return resolveJiraSite({...params, authorization, site, claims});\n}\n\nexport async function handleJiraSiteSelection(\n params: Omit<HandleJiraCallbackParams, 'code'> & {cloudId: string},\n): Promise<IntegrationConnection<'jira'>> {\n const claims = await verifyClaims(params, params.state);\n const pending = await params.pendingStore.load({\n workspaceId: claims.workspaceId,\n state: params.state,\n });\n if (!pending) throw new JiraPendingSelectionNotFoundError();\n const site = pending.sites.find((candidate) => candidate.cloudId === params.cloudId);\n if (!site) throw new JiraSiteSelectionMismatchError(params.cloudId);\n assertJiraAuthorizationScopes(site.scopes);\n const connection = await resolveJiraSite({\n ...params,\n authorization: pending.authorization,\n site,\n claims,\n });\n await params.pendingStore.clear({workspaceId: claims.workspaceId, state: params.state});\n return connection;\n}\n\nexport async function handleJiraOAuthCallbackError(params: {\n state: string;\n error: string;\n errorDescription?: string | undefined;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n}): Promise<never> {\n await verifyClaims(params, params.state);\n throw new JiraOAuthCallbackError(params.error, params.errorDescription);\n}\n\nasync function verifyClaims(\n params: Pick<\n HandleJiraCallbackParams,\n 'sessionUserId' | 'sessionMemberships' | 'requireWorkspaceMembership'\n >,\n state: string,\n) {\n const claims = verifyJiraInstallState(state);\n if (claims.userId !== params.sessionUserId) throw new JiraInstallStateActorMismatchError();\n await params.requireWorkspaceMembership({\n workspaceId: claims.workspaceId,\n userId: claims.userId,\n memberships: params.sessionMemberships,\n });\n return claims;\n}\n\nasync function resolveJiraSite(\n params: Omit<HandleJiraCallbackParams, 'code'> & {\n authorization: JiraAuthorization;\n site: JiraAccessibleResource;\n claims: {workspaceId: string; userId: string};\n },\n): Promise<IntegrationConnection<'jira'>> {\n const existing = await params.getExistingJiraConnection({cloudId: params.site.cloudId});\n if (existing && existing.workspaceId !== params.claims.workspaceId)\n throw new JiraInstallationAlreadyLinkedError(params.site.cloudId);\n let connectionId: string | undefined;\n try {\n const identity = await params.jira.getMyself({\n accessToken: params.authorization.accessToken,\n cloudId: params.site.cloudId,\n });\n const connection = await params.connectJiraInstallation({\n workspaceId: params.claims.workspaceId,\n cloudId: params.site.cloudId,\n siteUrl: params.site.url,\n siteName: params.site.name,\n authorizingAccountId: identity.accountId,\n scopes: params.site.scopes,\n tokenExpiresAt: params.authorization.expiresAt ?? null,\n displayName: `Jira ${params.site.name}`,\n });\n connectionId = connection.id;\n await params.tokenStore.storeTokens({\n connectionId: connection.id,\n accessToken: params.authorization.accessToken,\n refreshToken: params.authorization.refreshToken,\n editedBy: params.claims.userId,\n });\n return connection;\n } catch (error) {\n if (connectionId && !existing) await bestEffortDisconnect(params, connectionId);\n throw error;\n }\n}\n\nasync function bestEffortDisconnect(\n params: Pick<HandleJiraCallbackParams, 'disconnectJiraInstallation'>,\n connectionId: string,\n): Promise<void> {\n try {\n await params.disconnectJiraInstallation({connectionId});\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Jira connect compensation failed after token storage rejection',\n );\n }\n}\n"],"names":["logger","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","assertJiraAuthorizationScopes","verifyJiraInstallState","handleJiraCallback","params","claims","verifyClaims","state","authorization","jira","exchangeAuthorizationCode","code","refreshToken","sites","getAccessibleResources","accessToken","length","pendingStore","save","workspaceId","site","scopes","resolveJiraSite","handleJiraSiteSelection","pending","load","find","candidate","cloudId","connection","clear","handleJiraOAuthCallbackError","error","errorDescription","userId","sessionUserId","requireWorkspaceMembership","memberships","sessionMemberships","existing","getExistingJiraConnection","connectionId","identity","getMyself","connectJiraInstallation","siteUrl","url","siteName","name","authorizingAccountId","accountId","tokenExpiresAt","expiresAt","displayName","id","tokenStore","storeTokens","editedBy","bestEffortDisconnect","disconnectJiraInstallation","warn","err"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/install.ts"],"sourcesContent":["import type {UserContextMembership} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-spi';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {JiraAccessibleResource, JiraApiClient, JiraAuthorization} from '#api/client.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from './errors.js';\nimport {assertJiraAuthorizationScopes} from './scopes.js';\nimport {verifyJiraInstallState} from './state.js';\n\nexport interface ConnectJiraInstallationInput {\n workspaceId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n tokenExpiresAt: Date | null;\n displayName: string;\n}\n\nexport interface HandleJiraCallbackParams {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n code: string;\n state: string;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n disconnectJiraInstallation(input: {connectionId: string}): Promise<void>;\n}\n\nexport async function handleJiraCallback(\n params: HandleJiraCallbackParams,\n): Promise<IntegrationConnection<'jira'> | {sites: JiraAccessibleResource[]}> {\n const claims = await verifyClaims(params, params.state);\n const authorization = await params.jira.exchangeAuthorizationCode({code: params.code});\n if (!authorization.refreshToken) throw new JiraOfflineAccessNotGrantedError();\n const sites = await params.jira.getAccessibleResources({accessToken: authorization.accessToken});\n if (sites.length === 0)\n throw new JiraIntegrationProviderError(\n 'access-denied',\n 'Jira authorization did not grant access to a site',\n );\n if (sites.length > 1) {\n await params.pendingStore.save({\n workspaceId: claims.workspaceId,\n state: params.state,\n authorization,\n sites,\n });\n return {sites};\n }\n const site = sites[0];\n if (!site) {\n throw new JiraIntegrationProviderError(\n 'malformed-provider-response',\n 'Jira accessible-resources response did not contain a selected site',\n );\n }\n assertJiraAuthorizationScopes(site.scopes);\n return resolveJiraSite({...params, authorization, site, claims});\n}\n\nexport async function handleJiraSiteSelection(\n params: Omit<HandleJiraCallbackParams, 'code'> & {cloudId: string},\n): Promise<IntegrationConnection<'jira'>> {\n const claims = await verifyClaims(params, params.state);\n const pending = await params.pendingStore.load({\n workspaceId: claims.workspaceId,\n state: params.state,\n });\n if (!pending) throw new JiraPendingSelectionNotFoundError();\n const site = pending.sites.find((candidate) => candidate.cloudId === params.cloudId);\n if (!site) throw new JiraSiteSelectionMismatchError(params.cloudId);\n assertJiraAuthorizationScopes(site.scopes);\n const connection = await resolveJiraSite({\n ...params,\n authorization: pending.authorization,\n site,\n claims,\n });\n await params.pendingStore.clear({workspaceId: claims.workspaceId, state: params.state});\n return connection;\n}\n\nexport async function handleJiraOAuthCallbackError(params: {\n state: string;\n error: string;\n errorDescription?: string | undefined;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n}): Promise<never> {\n await verifyClaims(params, params.state);\n throw new JiraOAuthCallbackError(params.error, params.errorDescription);\n}\n\nasync function verifyClaims(\n params: Pick<\n HandleJiraCallbackParams,\n 'sessionUserId' | 'sessionMemberships' | 'requireWorkspaceMembership'\n >,\n state: string,\n) {\n const claims = verifyJiraInstallState(state);\n if (claims.userId !== params.sessionUserId) throw new JiraInstallStateActorMismatchError();\n await params.requireWorkspaceMembership({\n workspaceId: claims.workspaceId,\n userId: claims.userId,\n memberships: params.sessionMemberships,\n });\n return claims;\n}\n\nasync function resolveJiraSite(\n params: Omit<HandleJiraCallbackParams, 'code'> & {\n authorization: JiraAuthorization;\n site: JiraAccessibleResource;\n claims: {workspaceId: string; userId: string};\n },\n): Promise<IntegrationConnection<'jira'>> {\n const existing = await params.getExistingJiraConnection({cloudId: params.site.cloudId});\n if (existing && existing.workspaceId !== params.claims.workspaceId)\n throw new JiraInstallationAlreadyLinkedError(params.site.cloudId);\n let connectionId: string | undefined;\n try {\n const identity = await params.jira.getMyself({\n accessToken: params.authorization.accessToken,\n cloudId: params.site.cloudId,\n });\n const connection = await params.connectJiraInstallation({\n workspaceId: params.claims.workspaceId,\n cloudId: params.site.cloudId,\n siteUrl: params.site.url,\n siteName: params.site.name,\n authorizingAccountId: identity.accountId,\n scopes: params.site.scopes,\n tokenExpiresAt: params.authorization.expiresAt ?? null,\n displayName: `Jira ${params.site.name}`,\n });\n connectionId = connection.id;\n await params.tokenStore.storeTokens({\n connectionId: connection.id,\n accessToken: params.authorization.accessToken,\n refreshToken: params.authorization.refreshToken,\n editedBy: params.claims.userId,\n });\n return connection;\n } catch (error) {\n if (connectionId && !existing) await bestEffortDisconnect(params, connectionId);\n throw error;\n }\n}\n\nasync function bestEffortDisconnect(\n params: Pick<HandleJiraCallbackParams, 'disconnectJiraInstallation'>,\n connectionId: string,\n): Promise<void> {\n try {\n await params.disconnectJiraInstallation({connectionId});\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Jira connect compensation failed after token storage rejection',\n );\n }\n}\n"],"names":["logger","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","assertJiraAuthorizationScopes","verifyJiraInstallState","handleJiraCallback","params","claims","verifyClaims","state","authorization","jira","exchangeAuthorizationCode","code","refreshToken","sites","getAccessibleResources","accessToken","length","pendingStore","save","workspaceId","site","scopes","resolveJiraSite","handleJiraSiteSelection","pending","load","find","candidate","cloudId","connection","clear","handleJiraOAuthCallbackError","error","errorDescription","userId","sessionUserId","requireWorkspaceMembership","memberships","sessionMemberships","existing","getExistingJiraConnection","connectionId","identity","getMyself","connectJiraInstallation","siteUrl","url","siteName","name","authorizingAccountId","accountId","tokenExpiresAt","expiresAt","displayName","id","tokenStore","storeTokens","editedBy","bestEffortDisconnect","disconnectJiraInstallation","warn","err"],"mappings":"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"}
@@ -1,2 +1,16 @@
1
- export { toIntegrationConnectionDto } from '@shipfox/api-integration-core-dto';
1
+ import type { IntegrationCapability, IntegrationConnection } from '@shipfox/api-integration-spi';
2
+ export declare function toIntegrationConnectionDto(connection: IntegrationConnection, params: {
3
+ capabilities: IntegrationCapability[];
4
+ }): {
5
+ id: string;
6
+ workspace_id: string;
7
+ provider: string;
8
+ external_account_id: string;
9
+ slug: string;
10
+ display_name: string;
11
+ lifecycle_status: import("@shipfox/api-integration-spi").IntegrationConnectionLifecycleStatus;
12
+ capabilities: IntegrationCapability[];
13
+ created_at: string;
14
+ updated_at: string;
15
+ };
2
16
  //# sourceMappingURL=integrations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,0BAA0B,EAAC,MAAM,mCAAmC,CAAC"}
1
+ {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AAE/F,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,qBAAqB,EACjC,MAAM,EAAE;IAAC,YAAY,EAAE,qBAAqB,EAAE,CAAA;CAAC;;;;;;;;;;;EAchD"}
@@ -1,3 +1,16 @@
1
- export { toIntegrationConnectionDto } from '@shipfox/api-integration-core-dto';
1
+ export function toIntegrationConnectionDto(connection, params) {
2
+ return {
3
+ id: connection.id,
4
+ workspace_id: connection.workspaceId,
5
+ provider: connection.provider,
6
+ external_account_id: connection.externalAccountId,
7
+ slug: connection.slug,
8
+ display_name: connection.displayName,
9
+ lifecycle_status: connection.lifecycleStatus,
10
+ capabilities: params.capabilities,
11
+ created_at: connection.createdAt.toISOString(),
12
+ updated_at: connection.updatedAt.toISOString()
13
+ };
14
+ }
2
15
 
3
16
  //# sourceMappingURL=integrations.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/integrations.ts"],"sourcesContent":["export {toIntegrationConnectionDto} from '@shipfox/api-integration-core-dto';\n"],"names":["toIntegrationConnectionDto"],"mappings":"AAAA,SAAQA,0BAA0B,QAAO,oCAAoC"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/integrations.ts"],"sourcesContent":["import type {IntegrationCapability, IntegrationConnection} from '@shipfox/api-integration-spi';\n\nexport function toIntegrationConnectionDto(\n connection: IntegrationConnection,\n params: {capabilities: IntegrationCapability[]},\n) {\n return {\n id: connection.id,\n workspace_id: connection.workspaceId,\n provider: connection.provider,\n external_account_id: connection.externalAccountId,\n slug: connection.slug,\n display_name: connection.displayName,\n lifecycle_status: connection.lifecycleStatus,\n capabilities: params.capabilities,\n created_at: connection.createdAt.toISOString(),\n updated_at: connection.updatedAt.toISOString(),\n };\n}\n"],"names":["toIntegrationConnectionDto","connection","params","id","workspace_id","workspaceId","provider","external_account_id","externalAccountId","slug","display_name","displayName","lifecycle_status","lifecycleStatus","capabilities","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAEA,OAAO,SAASA,2BACdC,UAAiC,EACjCC,MAA+C;IAE/C,OAAO;QACLC,IAAIF,WAAWE,EAAE;QACjBC,cAAcH,WAAWI,WAAW;QACpCC,UAAUL,WAAWK,QAAQ;QAC7BC,qBAAqBN,WAAWO,iBAAiB;QACjDC,MAAMR,WAAWQ,IAAI;QACrBC,cAAcT,WAAWU,WAAW;QACpCC,kBAAkBX,WAAWY,eAAe;QAC5CC,cAAcZ,OAAOY,YAAY;QACjCC,YAAYd,WAAWe,SAAS,CAACC,WAAW;QAC5CC,YAAYjB,WAAWkB,SAAS,CAACF,WAAW;IAC9C;AACF"}
@@ -1,4 +1,4 @@
1
- import { ConnectionSlugConflictError } from '@shipfox/api-integration-core-dto';
1
+ import { ConnectionSlugConflictError } from '@shipfox/api-integration-spi';
2
2
  import { ClientError } from '@shipfox/node-fastify';
3
3
  import { JiraAuthorizationScopeMismatchError, JiraConnectionAlreadyLinkedError, JiraInstallationAlreadyLinkedError, JiraInstallStateActorMismatchError, JiraInstallStateError, JiraIntegrationProviderError, JiraOAuthCallbackError, JiraOfflineAccessNotGrantedError, JiraPendingSelectionNotFoundError, JiraSiteSelectionMismatchError } from '#core/errors.js';
4
4
  function providerStatus(reason) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/errors.ts"],"sourcesContent":["import {\n ConnectionSlugConflictError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-core-dto';\nimport {ClientError} from '@shipfox/node-fastify';\nimport {\n JiraAuthorizationScopeMismatchError,\n JiraConnectionAlreadyLinkedError,\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraInstallStateError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from '#core/errors.js';\n\nfunction providerStatus(reason: IntegrationProviderErrorReason): number {\n if (reason === 'rate-limited') return 429;\n if (reason === 'timeout' || reason === 'provider-unavailable') return 503;\n return 422;\n}\n\nexport function jiraRouteErrorHandler(error: unknown): never {\n if (error instanceof JiraInstallStateError)\n throw new ClientError(error.message, 'invalid-jira-install-state', {status: 400});\n if (error instanceof JiraInstallStateActorMismatchError)\n throw new ClientError(error.message, 'jira-install-state-actor-mismatch', {status: 403});\n if (error instanceof JiraInstallationAlreadyLinkedError)\n throw new ClientError(error.message, 'jira-installation-already-linked', {status: 409});\n if (error instanceof JiraConnectionAlreadyLinkedError)\n throw new ClientError(error.message, 'jira-connection-already-linked', {status: 409});\n if (error instanceof JiraAuthorizationScopeMismatchError)\n throw new ClientError(error.message, 'jira-authorization-scope-mismatch', {\n status: 422,\n details: {missing_scopes: error.missingScopes},\n });\n if (error instanceof JiraOfflineAccessNotGrantedError)\n throw new ClientError(error.message, 'jira-offline-access-not-granted', {status: 422});\n if (error instanceof JiraSiteSelectionMismatchError)\n throw new ClientError(error.message, 'jira-site-selection-mismatch', {status: 422});\n if (error instanceof JiraPendingSelectionNotFoundError)\n throw new ClientError(error.message, 'jira-pending-selection-not-found', {status: 410});\n if (error instanceof JiraOAuthCallbackError)\n throw new ClientError(error.message, 'jira-oauth-callback-error', {\n status: 422,\n details: {\n error: error.providerError,\n ...(error.providerDescription ? {error_description: error.providerDescription} : {}),\n },\n });\n if (error instanceof ConnectionSlugConflictError)\n throw new ClientError(error.message, 'slug-conflict', {status: 409});\n if (error instanceof JiraIntegrationProviderError)\n throw new ClientError(error.message, error.reason, {\n status: providerStatus(error.reason),\n details: {retry_after_seconds: error.retryAfterSeconds},\n });\n throw error;\n}\n"],"names":["ConnectionSlugConflictError","ClientError","JiraAuthorizationScopeMismatchError","JiraConnectionAlreadyLinkedError","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraInstallStateError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","providerStatus","reason","jiraRouteErrorHandler","error","message","status","details","missing_scopes","missingScopes","providerError","providerDescription","error_description","retry_after_seconds","retryAfterSeconds"],"mappings":"AAAA,SACEA,2BAA2B,QAEtB,oCAAoC;AAC3C,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SACEC,mCAAmC,EACnCC,gCAAgC,EAChCC,kCAAkC,EAClCC,kCAAkC,EAClCC,qBAAqB,EACrBC,4BAA4B,EAC5BC,sBAAsB,EACtBC,gCAAgC,EAChCC,iCAAiC,EACjCC,8BAA8B,QACzB,kBAAkB;AAEzB,SAASC,eAAeC,MAAsC;IAC5D,IAAIA,WAAW,gBAAgB,OAAO;IACtC,IAAIA,WAAW,aAAaA,WAAW,wBAAwB,OAAO;IACtE,OAAO;AACT;AAEA,OAAO,SAASC,sBAAsBC,KAAc;IAClD,IAAIA,iBAAiBT,uBACnB,MAAM,IAAIL,YAAYc,MAAMC,OAAO,EAAE,8BAA8B;QAACC,QAAQ;IAAG;IACjF,IAAIF,iBAAiBV,oCACnB,MAAM,IAAIJ,YAAYc,MAAMC,OAAO,EAAE,qCAAqC;QAACC,QAAQ;IAAG;IACxF,IAAIF,iBAAiBX,oCACnB,MAAM,IAAIH,YAAYc,MAAMC,OAAO,EAAE,oCAAoC;QAACC,QAAQ;IAAG;IACvF,IAAIF,iBAAiBZ,kCACnB,MAAM,IAAIF,YAAYc,MAAMC,OAAO,EAAE,kCAAkC;QAACC,QAAQ;IAAG;IACrF,IAAIF,iBAAiBb,qCACnB,MAAM,IAAID,YAAYc,MAAMC,OAAO,EAAE,qCAAqC;QACxEC,QAAQ;QACRC,SAAS;YAACC,gBAAgBJ,MAAMK,aAAa;QAAA;IAC/C;IACF,IAAIL,iBAAiBN,kCACnB,MAAM,IAAIR,YAAYc,MAAMC,OAAO,EAAE,mCAAmC;QAACC,QAAQ;IAAG;IACtF,IAAIF,iBAAiBJ,gCACnB,MAAM,IAAIV,YAAYc,MAAMC,OAAO,EAAE,gCAAgC;QAACC,QAAQ;IAAG;IACnF,IAAIF,iBAAiBL,mCACnB,MAAM,IAAIT,YAAYc,MAAMC,OAAO,EAAE,oCAAoC;QAACC,QAAQ;IAAG;IACvF,IAAIF,iBAAiBP,wBACnB,MAAM,IAAIP,YAAYc,MAAMC,OAAO,EAAE,6BAA6B;QAChEC,QAAQ;QACRC,SAAS;YACPH,OAAOA,MAAMM,aAAa;YAC1B,GAAIN,MAAMO,mBAAmB,GAAG;gBAACC,mBAAmBR,MAAMO,mBAAmB;YAAA,IAAI,CAAC,CAAC;QACrF;IACF;IACF,IAAIP,iBAAiBf,6BACnB,MAAM,IAAIC,YAAYc,MAAMC,OAAO,EAAE,iBAAiB;QAACC,QAAQ;IAAG;IACpE,IAAIF,iBAAiBR,8BACnB,MAAM,IAAIN,YAAYc,MAAMC,OAAO,EAAED,MAAMF,MAAM,EAAE;QACjDI,QAAQL,eAAeG,MAAMF,MAAM;QACnCK,SAAS;YAACM,qBAAqBT,MAAMU,iBAAiB;QAAA;IACxD;IACF,MAAMV;AACR"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/errors.ts"],"sourcesContent":["import {\n ConnectionSlugConflictError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-spi';\nimport {ClientError} from '@shipfox/node-fastify';\nimport {\n JiraAuthorizationScopeMismatchError,\n JiraConnectionAlreadyLinkedError,\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraInstallStateError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from '#core/errors.js';\n\nfunction providerStatus(reason: IntegrationProviderErrorReason): number {\n if (reason === 'rate-limited') return 429;\n if (reason === 'timeout' || reason === 'provider-unavailable') return 503;\n return 422;\n}\n\nexport function jiraRouteErrorHandler(error: unknown): never {\n if (error instanceof JiraInstallStateError)\n throw new ClientError(error.message, 'invalid-jira-install-state', {status: 400});\n if (error instanceof JiraInstallStateActorMismatchError)\n throw new ClientError(error.message, 'jira-install-state-actor-mismatch', {status: 403});\n if (error instanceof JiraInstallationAlreadyLinkedError)\n throw new ClientError(error.message, 'jira-installation-already-linked', {status: 409});\n if (error instanceof JiraConnectionAlreadyLinkedError)\n throw new ClientError(error.message, 'jira-connection-already-linked', {status: 409});\n if (error instanceof JiraAuthorizationScopeMismatchError)\n throw new ClientError(error.message, 'jira-authorization-scope-mismatch', {\n status: 422,\n details: {missing_scopes: error.missingScopes},\n });\n if (error instanceof JiraOfflineAccessNotGrantedError)\n throw new ClientError(error.message, 'jira-offline-access-not-granted', {status: 422});\n if (error instanceof JiraSiteSelectionMismatchError)\n throw new ClientError(error.message, 'jira-site-selection-mismatch', {status: 422});\n if (error instanceof JiraPendingSelectionNotFoundError)\n throw new ClientError(error.message, 'jira-pending-selection-not-found', {status: 410});\n if (error instanceof JiraOAuthCallbackError)\n throw new ClientError(error.message, 'jira-oauth-callback-error', {\n status: 422,\n details: {\n error: error.providerError,\n ...(error.providerDescription ? {error_description: error.providerDescription} : {}),\n },\n });\n if (error instanceof ConnectionSlugConflictError)\n throw new ClientError(error.message, 'slug-conflict', {status: 409});\n if (error instanceof JiraIntegrationProviderError)\n throw new ClientError(error.message, error.reason, {\n status: providerStatus(error.reason),\n details: {retry_after_seconds: error.retryAfterSeconds},\n });\n throw error;\n}\n"],"names":["ConnectionSlugConflictError","ClientError","JiraAuthorizationScopeMismatchError","JiraConnectionAlreadyLinkedError","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraInstallStateError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","providerStatus","reason","jiraRouteErrorHandler","error","message","status","details","missing_scopes","missingScopes","providerError","providerDescription","error_description","retry_after_seconds","retryAfterSeconds"],"mappings":"AAAA,SACEA,2BAA2B,QAEtB,+BAA+B;AACtC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SACEC,mCAAmC,EACnCC,gCAAgC,EAChCC,kCAAkC,EAClCC,kCAAkC,EAClCC,qBAAqB,EACrBC,4BAA4B,EAC5BC,sBAAsB,EACtBC,gCAAgC,EAChCC,iCAAiC,EACjCC,8BAA8B,QACzB,kBAAkB;AAEzB,SAASC,eAAeC,MAAsC;IAC5D,IAAIA,WAAW,gBAAgB,OAAO;IACtC,IAAIA,WAAW,aAAaA,WAAW,wBAAwB,OAAO;IACtE,OAAO;AACT;AAEA,OAAO,SAASC,sBAAsBC,KAAc;IAClD,IAAIA,iBAAiBT,uBACnB,MAAM,IAAIL,YAAYc,MAAMC,OAAO,EAAE,8BAA8B;QAACC,QAAQ;IAAG;IACjF,IAAIF,iBAAiBV,oCACnB,MAAM,IAAIJ,YAAYc,MAAMC,OAAO,EAAE,qCAAqC;QAACC,QAAQ;IAAG;IACxF,IAAIF,iBAAiBX,oCACnB,MAAM,IAAIH,YAAYc,MAAMC,OAAO,EAAE,oCAAoC;QAACC,QAAQ;IAAG;IACvF,IAAIF,iBAAiBZ,kCACnB,MAAM,IAAIF,YAAYc,MAAMC,OAAO,EAAE,kCAAkC;QAACC,QAAQ;IAAG;IACrF,IAAIF,iBAAiBb,qCACnB,MAAM,IAAID,YAAYc,MAAMC,OAAO,EAAE,qCAAqC;QACxEC,QAAQ;QACRC,SAAS;YAACC,gBAAgBJ,MAAMK,aAAa;QAAA;IAC/C;IACF,IAAIL,iBAAiBN,kCACnB,MAAM,IAAIR,YAAYc,MAAMC,OAAO,EAAE,mCAAmC;QAACC,QAAQ;IAAG;IACtF,IAAIF,iBAAiBJ,gCACnB,MAAM,IAAIV,YAAYc,MAAMC,OAAO,EAAE,gCAAgC;QAACC,QAAQ;IAAG;IACnF,IAAIF,iBAAiBL,mCACnB,MAAM,IAAIT,YAAYc,MAAMC,OAAO,EAAE,oCAAoC;QAACC,QAAQ;IAAG;IACvF,IAAIF,iBAAiBP,wBACnB,MAAM,IAAIP,YAAYc,MAAMC,OAAO,EAAE,6BAA6B;QAChEC,QAAQ;QACRC,SAAS;YACPH,OAAOA,MAAMM,aAAa;YAC1B,GAAIN,MAAMO,mBAAmB,GAAG;gBAACC,mBAAmBR,MAAMO,mBAAmB;YAAA,IAAI,CAAC,CAAC;QACrF;IACF;IACF,IAAIP,iBAAiBf,6BACnB,MAAM,IAAIC,YAAYc,MAAMC,OAAO,EAAE,iBAAiB;QAACC,QAAQ;IAAG;IACpE,IAAIF,iBAAiBR,8BACnB,MAAM,IAAIN,YAAYc,MAAMC,OAAO,EAAED,MAAMF,MAAM,EAAE;QACjDI,QAAQL,eAAeG,MAAMF,MAAM;QACnCK,SAAS;YAACM,qBAAqBT,MAAMU,iBAAiB;QAAA;IACxD;IACF,MAAMV;AACR"}
@@ -1,4 +1,4 @@
1
- import { type IntegrationCapability, type IntegrationConnection } from '@shipfox/api-integration-core-dto';
1
+ import { type IntegrationCapability, type IntegrationConnection } from '@shipfox/api-integration-spi';
2
2
  import { type RouteGroup } from '@shipfox/node-fastify';
3
3
  import type { JiraApiClient } from '#api/client.js';
4
4
  import { type ConnectJiraInstallationInput } from '#core/install.js';
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE3B,MAAM,mCAAmC,CAAC;AAS3C,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAElD,OAAO,EACL,KAAK,4BAA4B,EAIlC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAIpD,MAAM,WAAW,kCAAkC;IACjD,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAChD,YAAY,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACzE,yBAAyB,CAAC,KAAK,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACvD,uBAAuB,CACrB,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,0BAA0B,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,UAAU,CA6FZ"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAElD,OAAO,EACL,KAAK,4BAA4B,EAIlC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAIpD,MAAM,WAAW,kCAAkC;IACjD,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAChD,YAAY,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACzE,yBAAyB,CAAC,KAAK,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACvD,uBAAuB,CACrB,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,0BAA0B,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;IAChD,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,UAAU,CA6FZ"}
@@ -1,6 +1,6 @@
1
1
  import { AUTH_USER, requireUserContext, requireWorkspaceAccess } from '@shipfox/api-auth-context';
2
- import { integrationConnectionDtoSchema } from '@shipfox/api-integration-core-dto';
3
2
  import { completeJiraSiteSelectionBodySchema, createJiraInstallBodySchema, createJiraInstallResponseSchema, jiraCallbackQuerySchema, jiraCallbackResponseSchema } from '@shipfox/api-integration-jira-dto';
3
+ import { integrationConnectionDtoSchema } from '@shipfox/api-integration-spi';
4
4
  import { defineRoute } from '@shipfox/node-fastify';
5
5
  import { config } from '#config.js';
6
6
  import { handleJiraCallback, handleJiraOAuthCallbackError, handleJiraSiteSelection } from '#core/install.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n type IntegrationCapability,\n type IntegrationConnection,\n integrationConnectionDtoSchema,\n} from '@shipfox/api-integration-core-dto';\nimport {\n completeJiraSiteSelectionBodySchema,\n createJiraInstallBodySchema,\n createJiraInstallResponseSchema,\n type JiraCallbackQueryDto,\n jiraCallbackQuerySchema,\n jiraCallbackResponseSchema,\n} from '@shipfox/api-integration-jira-dto';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {JiraApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {\n type ConnectJiraInstallationInput,\n handleJiraCallback,\n handleJiraOAuthCallbackError,\n handleJiraSiteSelection,\n} from '#core/install.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport {formatJiraOAuthScopes} from '#core/scopes.js';\nimport {signJiraInstallState} from '#core/state.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {jiraRouteErrorHandler} from './errors.js';\n\nexport interface CreateJiraIntegrationRoutesOptions {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n disconnectJiraInstallation(input: {connectionId: string}): Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createJiraIntegrationRoutes(\n options: CreateJiraIntegrationRoutesOptions,\n): RouteGroup {\n const requireMembership =\n options.requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck;\n const installRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a Jira OAuth authorization URL for a workspace.',\n schema: {body: createJiraInstallBodySchema, response: {200: createJiraInstallResponseSchema}},\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n requireWorkspaceAccess({request, workspaceId});\n const installUrl = new URL(`${config.JIRA_AUTH_BASE_URL}/authorize`);\n installUrl.searchParams.set('audience', 'api.atlassian.com');\n installUrl.searchParams.set('client_id', config.JIRA_OAUTH_CLIENT_ID);\n installUrl.searchParams.set('scope', formatJiraOAuthScopes());\n installUrl.searchParams.set('redirect_uri', config.JIRA_OAUTH_REDIRECT_URL);\n installUrl.searchParams.set(\n 'state',\n signJiraInstallState({workspaceId, userId: actor.userId}),\n );\n installUrl.searchParams.set('response_type', 'code');\n installUrl.searchParams.set('prompt', 'consent');\n return {install_url: installUrl.toString()};\n },\n });\n const callbackApiRoute = defineRoute({\n method: 'GET',\n path: '/callback/api',\n auth: AUTH_USER,\n description: 'Handle the Jira OAuth callback.',\n schema: {querystring: jiraCallbackQuerySchema, response: {200: jiraCallbackResponseSchema}},\n errorHandler: jiraRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const query = request.query;\n if (isJiraOAuthErrorCallback(query))\n return await handleJiraOAuthCallbackError({\n state: query.state,\n error: query.error,\n errorDescription: query.error_description,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership: requireMembership,\n });\n const result = await handleJiraCallback({\n ...options,\n code: query.code,\n state: query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership: requireMembership,\n });\n return 'sites' in result\n ? {\n sites: result.sites.map((site) => ({\n cloud_id: site.cloudId,\n name: site.name,\n url: site.url,\n scopes: site.scopes,\n })),\n }\n : toIntegrationConnectionDto(result, {capabilities: options.connectionCapabilities});\n },\n });\n const siteSelectionRoute = defineRoute({\n method: 'POST',\n path: '/callback/site',\n auth: AUTH_USER,\n description: 'Complete Jira OAuth site selection.',\n schema: {\n body: completeJiraSiteSelectionBodySchema,\n response: {200: integrationConnectionDtoSchema},\n },\n errorHandler: jiraRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const connection = await handleJiraSiteSelection({\n ...options,\n cloudId: request.body.cloud_id,\n state: request.body.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership: requireMembership,\n });\n return toIntegrationConnectionDto(connection, {capabilities: options.connectionCapabilities});\n },\n });\n return {\n prefix: '/integrations/jira',\n routes: [installRoute, callbackApiRoute, siteSelectionRoute],\n };\n}\n\nfunction unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\n}\n\nfunction isJiraOAuthErrorCallback(query: JiraCallbackQueryDto): query is JiraCallbackQueryDto & {\n error: string;\n error_description?: string | undefined;\n state: string;\n} {\n return 'error' in query && typeof query.error === 'string';\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","integrationConnectionDtoSchema","completeJiraSiteSelectionBodySchema","createJiraInstallBodySchema","createJiraInstallResponseSchema","jiraCallbackQuerySchema","jiraCallbackResponseSchema","defineRoute","config","handleJiraCallback","handleJiraOAuthCallbackError","handleJiraSiteSelection","formatJiraOAuthScopes","signJiraInstallState","toIntegrationConnectionDto","jiraRouteErrorHandler","createJiraIntegrationRoutes","options","requireMembership","requireActiveWorkspaceMembership","unavailableWorkspaceMembershipCheck","installRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","installUrl","URL","JIRA_AUTH_BASE_URL","searchParams","set","JIRA_OAUTH_CLIENT_ID","JIRA_OAUTH_REDIRECT_URL","userId","install_url","toString","callbackApiRoute","querystring","errorHandler","query","isJiraOAuthErrorCallback","state","error","errorDescription","error_description","sessionUserId","sessionMemberships","memberships","requireWorkspaceMembership","result","code","sites","map","site","cloud_id","cloudId","name","url","scopes","capabilities","connectionCapabilities","siteSelectionRoute","connection","prefix","routes","_input","Promise","reject","Error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n completeJiraSiteSelectionBodySchema,\n createJiraInstallBodySchema,\n createJiraInstallResponseSchema,\n type JiraCallbackQueryDto,\n jiraCallbackQuerySchema,\n jiraCallbackResponseSchema,\n} from '@shipfox/api-integration-jira-dto';\nimport {\n type IntegrationCapability,\n type IntegrationConnection,\n integrationConnectionDtoSchema,\n} from '@shipfox/api-integration-spi';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {JiraApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {\n type ConnectJiraInstallationInput,\n handleJiraCallback,\n handleJiraOAuthCallbackError,\n handleJiraSiteSelection,\n} from '#core/install.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport {formatJiraOAuthScopes} from '#core/scopes.js';\nimport {signJiraInstallState} from '#core/state.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {jiraRouteErrorHandler} from './errors.js';\n\nexport interface CreateJiraIntegrationRoutesOptions {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n disconnectJiraInstallation(input: {connectionId: string}): Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createJiraIntegrationRoutes(\n options: CreateJiraIntegrationRoutesOptions,\n): RouteGroup {\n const requireMembership =\n options.requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck;\n const installRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a Jira OAuth authorization URL for a workspace.',\n schema: {body: createJiraInstallBodySchema, response: {200: createJiraInstallResponseSchema}},\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n requireWorkspaceAccess({request, workspaceId});\n const installUrl = new URL(`${config.JIRA_AUTH_BASE_URL}/authorize`);\n installUrl.searchParams.set('audience', 'api.atlassian.com');\n installUrl.searchParams.set('client_id', config.JIRA_OAUTH_CLIENT_ID);\n installUrl.searchParams.set('scope', formatJiraOAuthScopes());\n installUrl.searchParams.set('redirect_uri', config.JIRA_OAUTH_REDIRECT_URL);\n installUrl.searchParams.set(\n 'state',\n signJiraInstallState({workspaceId, userId: actor.userId}),\n );\n installUrl.searchParams.set('response_type', 'code');\n installUrl.searchParams.set('prompt', 'consent');\n return {install_url: installUrl.toString()};\n },\n });\n const callbackApiRoute = defineRoute({\n method: 'GET',\n path: '/callback/api',\n auth: AUTH_USER,\n description: 'Handle the Jira OAuth callback.',\n schema: {querystring: jiraCallbackQuerySchema, response: {200: jiraCallbackResponseSchema}},\n errorHandler: jiraRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const query = request.query;\n if (isJiraOAuthErrorCallback(query))\n return await handleJiraOAuthCallbackError({\n state: query.state,\n error: query.error,\n errorDescription: query.error_description,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership: requireMembership,\n });\n const result = await handleJiraCallback({\n ...options,\n code: query.code,\n state: query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership: requireMembership,\n });\n return 'sites' in result\n ? {\n sites: result.sites.map((site) => ({\n cloud_id: site.cloudId,\n name: site.name,\n url: site.url,\n scopes: site.scopes,\n })),\n }\n : toIntegrationConnectionDto(result, {capabilities: options.connectionCapabilities});\n },\n });\n const siteSelectionRoute = defineRoute({\n method: 'POST',\n path: '/callback/site',\n auth: AUTH_USER,\n description: 'Complete Jira OAuth site selection.',\n schema: {\n body: completeJiraSiteSelectionBodySchema,\n response: {200: integrationConnectionDtoSchema},\n },\n errorHandler: jiraRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const connection = await handleJiraSiteSelection({\n ...options,\n cloudId: request.body.cloud_id,\n state: request.body.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership: requireMembership,\n });\n return toIntegrationConnectionDto(connection, {capabilities: options.connectionCapabilities});\n },\n });\n return {\n prefix: '/integrations/jira',\n routes: [installRoute, callbackApiRoute, siteSelectionRoute],\n };\n}\n\nfunction unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\n}\n\nfunction isJiraOAuthErrorCallback(query: JiraCallbackQueryDto): query is JiraCallbackQueryDto & {\n error: string;\n error_description?: string | undefined;\n state: string;\n} {\n return 'error' in query && typeof query.error === 'string';\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","completeJiraSiteSelectionBodySchema","createJiraInstallBodySchema","createJiraInstallResponseSchema","jiraCallbackQuerySchema","jiraCallbackResponseSchema","integrationConnectionDtoSchema","defineRoute","config","handleJiraCallback","handleJiraOAuthCallbackError","handleJiraSiteSelection","formatJiraOAuthScopes","signJiraInstallState","toIntegrationConnectionDto","jiraRouteErrorHandler","createJiraIntegrationRoutes","options","requireMembership","requireActiveWorkspaceMembership","unavailableWorkspaceMembershipCheck","installRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","installUrl","URL","JIRA_AUTH_BASE_URL","searchParams","set","JIRA_OAUTH_CLIENT_ID","JIRA_OAUTH_REDIRECT_URL","userId","install_url","toString","callbackApiRoute","querystring","errorHandler","query","isJiraOAuthErrorCallback","state","error","errorDescription","error_description","sessionUserId","sessionMemberships","memberships","requireWorkspaceMembership","result","code","sites","map","site","cloud_id","cloudId","name","url","scopes","capabilities","connectionCapabilities","siteSelectionRoute","connection","prefix","routes","_input","Promise","reject","Error"],"mappings":"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"}