@dcoder-x/plugin-shared 0.1.2 → 0.1.4

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 (36) hide show
  1. package/dist/buildId.d.ts +5 -0
  2. package/dist/buildId.js +17 -0
  3. package/dist/extractors/ComponentContextResolver.d.ts +37 -0
  4. package/dist/extractors/ComponentContextResolver.js +167 -0
  5. package/dist/extractors/ComponentExtractor.d.ts +6 -1
  6. package/dist/extractors/ComponentExtractor.js +76 -1
  7. package/dist/extractors/FlowInferrer.d.ts +4 -2
  8. package/dist/extractors/FlowInferrer.js +34 -2
  9. package/dist/extractors/InteractionGraphExtractor.d.ts +34 -0
  10. package/dist/extractors/InteractionGraphExtractor.js +245 -0
  11. package/dist/extractors/SelectorGenerator.d.ts +6 -1
  12. package/dist/extractors/SelectorGenerator.js +28 -3
  13. package/dist/index.d.ts +6 -1
  14. package/dist/index.js +20 -0
  15. package/dist/injection/ClippyIdInjector.d.ts +12 -0
  16. package/dist/injection/ClippyIdInjector.js +91 -0
  17. package/dist/injection/HtmlTagGuards.d.ts +3 -0
  18. package/dist/injection/HtmlTagGuards.js +41 -0
  19. package/dist/injection/IdStrategy.d.ts +2 -0
  20. package/dist/injection/IdStrategy.js +38 -0
  21. package/dist/types.d.ts +102 -1
  22. package/dist/upload/Adapter.d.ts +3 -0
  23. package/dist/upload/Adapter.js +13 -0
  24. package/dist/upload/BackendAdapter.d.ts +30 -0
  25. package/dist/upload/BackendAdapter.js +42 -0
  26. package/dist/upload/BackendUploadAdapter.d.ts +13 -0
  27. package/dist/upload/BackendUploadAdapter.js +51 -0
  28. package/dist/upload/PackageBuilder.d.ts +32 -1
  29. package/dist/upload/PackageBuilder.js +143 -0
  30. package/dist/upload/PackageWriter.d.ts +9 -1
  31. package/dist/upload/PackageWriter.js +26 -0
  32. package/dist/upload/UploadStrategy.d.ts +11 -0
  33. package/dist/upload/UploadStrategy.js +40 -0
  34. package/dist/upload/Uploader.d.ts +6 -1
  35. package/dist/upload/Uploader.js +48 -3
  36. package/package.json +1 -1
@@ -0,0 +1,30 @@
1
+ import type { PolicyArtifacts } from '../types';
2
+ export interface SiteComponentRegistryUpload {
3
+ domain: string;
4
+ components: {
5
+ version: string;
6
+ generatedAt: string;
7
+ selectors: Array<{
8
+ clippyId: string;
9
+ selector: string;
10
+ tag: string;
11
+ component: string;
12
+ file: string;
13
+ attributes?: Array<{
14
+ name: string;
15
+ value: string | null;
16
+ }>;
17
+ }>;
18
+ components: Array<{
19
+ name: string;
20
+ file: string;
21
+ stateVariables?: any[];
22
+ interactions?: any[];
23
+ conditionalRenders?: any[];
24
+ effects?: any[];
25
+ contextUsages?: any[];
26
+ }>;
27
+ };
28
+ }
29
+ export declare function mapArtifactsToSiteRegistry(artifacts: PolicyArtifacts, domain: string): SiteComponentRegistryUpload;
30
+ export default mapArtifactsToSiteRegistry;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapArtifactsToSiteRegistry = mapArtifactsToSiteRegistry;
4
+ // Convert internal PolicyArtifacts -> backend SiteComponentRegistryUpload.
5
+ // This is intentionally a best-effort mapping: the plugin has richer
6
+ // fields (routes, flows, selector candidates). The adapter flattens
7
+ // and retains the core selectors/components the backend requires.
8
+ function mapArtifactsToSiteRegistry(artifacts, domain) {
9
+ const generatedAt = artifacts.metadata.generatedAt;
10
+ const selectors = (artifacts.policy.selectors || []).map((s) => ({
11
+ clippyId: s.clippyId,
12
+ selector: s.selector,
13
+ tag: s.tag,
14
+ component: s.component,
15
+ file: s.filePath,
16
+ attributes: s.attributes || [],
17
+ }));
18
+ const components = (artifacts.policy.components || []).map((c) => ({
19
+ name: c.name,
20
+ file: c.filePath,
21
+ stateVariables: c.stateVariables || [],
22
+ interactions: c.interactions || [],
23
+ // Best-effort placeholders: preserve array shape expected by backend.
24
+ conditionalRenders: [],
25
+ effects: (c.interactions || []).map((it) => ({
26
+ dependencies: [],
27
+ asyncCalls: [],
28
+ runsOnMount: false,
29
+ })),
30
+ contextUsages: [],
31
+ }));
32
+ return {
33
+ domain,
34
+ components: {
35
+ version: artifacts.metadata.version,
36
+ generatedAt,
37
+ selectors,
38
+ components,
39
+ },
40
+ };
41
+ }
42
+ exports.default = mapArtifactsToSiteRegistry;
@@ -0,0 +1,13 @@
1
+ import type { PolicyArtifacts, UploadResult } from '../types';
2
+ export interface BackendUploadOptions {
3
+ /** Full URL to POST the SiteComponentRegistryUpload to, e.g. http://localhost:3000/website/registry */
4
+ url: string;
5
+ /** Authorization header value (full header string, e.g. 'Bearer ...') */
6
+ authorization?: string;
7
+ /** Domain to include in the payload (required by backend contract) */
8
+ domain: string;
9
+ }
10
+ export declare function createBackendUploadAdapter(opts: BackendUploadOptions): {
11
+ uploadArtifacts(artifacts: PolicyArtifacts): Promise<UploadResult>;
12
+ };
13
+ export default createBackendUploadAdapter;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBackendUploadAdapter = createBackendUploadAdapter;
4
+ const BackendAdapter_1 = require("./BackendAdapter");
5
+ function createBackendUploadAdapter(opts) {
6
+ if (!opts || !opts.url)
7
+ throw new Error('BackendUploadAdapter requires a target url');
8
+ if (!opts.domain)
9
+ throw new Error('BackendUploadAdapter requires a domain');
10
+ return {
11
+ async uploadArtifacts(artifacts) {
12
+ const payload = (0, BackendAdapter_1.mapArtifactsToSiteRegistry)(artifacts, opts.domain);
13
+ // Use global fetch when available (Node 18+), otherwise try dynamic import
14
+ const headers = { 'Content-Type': 'application/json' };
15
+ if (opts.authorization)
16
+ headers['authorization'] = opts.authorization;
17
+ if (typeof fetch === 'function') {
18
+ const res = await fetch(opts.url, {
19
+ method: 'POST',
20
+ headers,
21
+ body: JSON.stringify(payload),
22
+ });
23
+ return {
24
+ skipped: false,
25
+ policyUploaded: res.ok,
26
+ selectorsUploaded: res.ok,
27
+ mode: 'single',
28
+ };
29
+ }
30
+ // Fallback to http/https
31
+ const { URL } = await import('url');
32
+ const parsed = new URL(opts.url);
33
+ const lib = parsed.protocol === 'https:' ? await import('https') : await import('http');
34
+ return new Promise((resolve, reject) => {
35
+ const req = lib.request(opts.url, {
36
+ method: 'POST',
37
+ headers,
38
+ }, (res) => {
39
+ const ok = res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
40
+ // consume body
41
+ res.on('data', () => { });
42
+ res.on('end', () => resolve({ skipped: false, policyUploaded: !!ok, selectorsUploaded: !!ok, mode: 'single' }));
43
+ });
44
+ req.on('error', (err) => reject(err));
45
+ req.write(JSON.stringify(payload));
46
+ req.end();
47
+ });
48
+ },
49
+ };
50
+ }
51
+ exports.default = createBackendUploadAdapter;
@@ -1,5 +1,36 @@
1
- import type { KnowledgePackage } from '../types';
1
+ import type { ElementWithSelectors, InferredFlow, KnowledgePackage, PolicyArtifacts, PolicyComponent, PolicyDocument, PolicySelectorEntry, SelectorManifest, DiscoveredRoute } from '../types';
2
2
  export declare class PackageBuilder {
3
+ private version;
3
4
  buildPackage(data: Omit<KnowledgePackage, 'generatedAt'>): KnowledgePackage;
5
+ buildArtifacts(data: {
6
+ buildId: string;
7
+ bundler: 'webpack' | 'vite';
8
+ routes: DiscoveredRoute[];
9
+ selectors: ElementWithSelectors[];
10
+ flows: InferredFlow[];
11
+ components?: PolicyComponent[];
12
+ }): PolicyArtifacts;
13
+ buildPolicyDocument(data: {
14
+ buildId: string;
15
+ generatedAt: string;
16
+ bundler: 'webpack' | 'vite';
17
+ routes: DiscoveredRoute[];
18
+ selectors: ElementWithSelectors[];
19
+ flows: InferredFlow[];
20
+ components?: PolicyComponent[];
21
+ }): PolicyDocument;
22
+ buildSelectorManifest(data: {
23
+ buildId: string;
24
+ generatedAt: string;
25
+ selectors: PolicySelectorEntry[];
26
+ }): SelectorManifest;
4
27
  build(data: Omit<KnowledgePackage, 'generatedAt'>): Buffer;
28
+ buildArtifactsBuffer(artifacts: PolicyArtifacts): Buffer;
29
+ private toPolicySelectors;
30
+ private toPolicyComponents;
31
+ private toPolicyFlows;
32
+ private deriveComponentName;
33
+ private deriveStableId;
34
+ private extractClippyIdFromSelector;
35
+ private simpleHash;
5
36
  }
@@ -5,16 +5,159 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PackageBuilder = void 0;
7
7
  const zlib_1 = __importDefault(require("zlib"));
8
+ const IdStrategy_1 = require("../injection/IdStrategy");
8
9
  class PackageBuilder {
10
+ constructor() {
11
+ this.version = '1.0.0';
12
+ }
9
13
  buildPackage(data) {
10
14
  return {
11
15
  ...data,
12
16
  generatedAt: new Date().toISOString(),
13
17
  };
14
18
  }
19
+ buildArtifacts(data) {
20
+ const generatedAt = new Date().toISOString();
21
+ const policy = this.buildPolicyDocument({
22
+ ...data,
23
+ generatedAt,
24
+ });
25
+ const selectorManifest = this.buildSelectorManifest({
26
+ buildId: data.buildId,
27
+ generatedAt,
28
+ selectors: policy.selectors,
29
+ });
30
+ return {
31
+ metadata: {
32
+ version: this.version,
33
+ buildId: data.buildId,
34
+ generatedAt,
35
+ bundler: data.bundler,
36
+ },
37
+ policy,
38
+ selectorManifest,
39
+ };
40
+ }
41
+ buildPolicyDocument(data) {
42
+ const policySelectors = this.toPolicySelectors(data.selectors);
43
+ return {
44
+ version: this.version,
45
+ buildId: data.buildId,
46
+ generatedAt: data.generatedAt,
47
+ bundler: data.bundler,
48
+ routes: data.routes,
49
+ selectors: policySelectors,
50
+ components: data.components ?? this.toPolicyComponents(data.selectors),
51
+ flows: this.toPolicyFlows(data.flows, data.routes, policySelectors),
52
+ };
53
+ }
54
+ buildSelectorManifest(data) {
55
+ const selectors = data.selectors.map((entry) => ({
56
+ id: entry.clippyId,
57
+ selector: entry.selector,
58
+ component: entry.component,
59
+ tag: entry.tag,
60
+ route: entry.route,
61
+ }));
62
+ return {
63
+ version: this.version,
64
+ buildId: data.buildId,
65
+ generatedAt: data.generatedAt,
66
+ selectors,
67
+ };
68
+ }
15
69
  build(data) {
16
70
  const pkg = this.buildPackage(data);
17
71
  return zlib_1.default.gzipSync(JSON.stringify(pkg));
18
72
  }
73
+ buildArtifactsBuffer(artifacts) {
74
+ return zlib_1.default.gzipSync(JSON.stringify(artifacts));
75
+ }
76
+ toPolicySelectors(selectors) {
77
+ return selectors.map((element, index) => {
78
+ const component = (0, IdStrategy_1.deriveComponentName)(element.filePath);
79
+ const preferred = element.selectors.find((candidate) => candidate.type === 'clippy_id') ||
80
+ element.selectors[0];
81
+ const stableLine = element.loc?.line ?? index;
82
+ const clippyId = preferred?.type === 'clippy_id'
83
+ ? this.extractClippyIdFromSelector(preferred.value) ||
84
+ this.deriveStableId(component, element.tag, stableLine)
85
+ : this.deriveStableId(component, element.tag, stableLine);
86
+ const selector = preferred?.value ||
87
+ `[data-clippy-id='${clippyId}']`;
88
+ return {
89
+ clippyId,
90
+ selector,
91
+ tag: element.tag,
92
+ component,
93
+ route: element.route,
94
+ filePath: element.filePath,
95
+ attributes: Object.entries(element.staticProps).map(([name, value]) => ({ name, value })),
96
+ candidates: element.selectors,
97
+ };
98
+ });
99
+ }
100
+ toPolicyComponents(selectors) {
101
+ const byFile = new Map();
102
+ for (const element of selectors) {
103
+ if (!byFile.has(element.filePath))
104
+ byFile.set(element.filePath, []);
105
+ byFile.get(element.filePath).push(element);
106
+ }
107
+ return Array.from(byFile.entries()).map(([filePath, items]) => {
108
+ const componentName = this.deriveComponentName(filePath);
109
+ const route = items[0]?.route || '/';
110
+ return {
111
+ name: componentName,
112
+ filePath,
113
+ route,
114
+ stateVariables: [],
115
+ interactions: [],
116
+ };
117
+ });
118
+ }
119
+ toPolicyFlows(flows, routes, selectors) {
120
+ return flows.map((flow, flowIndex) => {
121
+ const firstRoute = flow.steps[0] || '/';
122
+ const page = flow.page || routes.find((route) => route.path === firstRoute)?.path || firstRoute;
123
+ const hasInteraction = flow.edges.some((edge) => edge.from === edge.to && edge.trigger?.startsWith('on'));
124
+ const steps = flow.steps.map((routePath, stepIndex) => {
125
+ const target = selectors.find((entry) => entry.route === routePath)?.selector ||
126
+ `[data-clippy-route='${routePath}']`;
127
+ return {
128
+ step: stepIndex + 1,
129
+ action: stepIndex === 0
130
+ ? hasInteraction
131
+ ? 'interact'
132
+ : 'navigate'
133
+ : 'transition',
134
+ target,
135
+ };
136
+ });
137
+ return {
138
+ flowId: flow.id || `flow_${flowIndex + 1}`,
139
+ page,
140
+ intentPatterns: [flow.name.toLowerCase()],
141
+ steps,
142
+ };
143
+ });
144
+ }
145
+ deriveComponentName(filePath) {
146
+ return (0, IdStrategy_1.deriveComponentName)(filePath);
147
+ }
148
+ deriveStableId(component, tag, line) {
149
+ return (0, IdStrategy_1.deriveClippyId)(component, tag, line);
150
+ }
151
+ extractClippyIdFromSelector(selector) {
152
+ const match = selector.match(/\[data-clippy-id=['\"]([^'\"]+)['\"]\]/);
153
+ return match?.[1] || null;
154
+ }
155
+ simpleHash(input) {
156
+ let hash = 0;
157
+ for (let i = 0; i < input.length; i++) {
158
+ hash = (hash * 31 + input.charCodeAt(i)) >>> 0;
159
+ }
160
+ return hash.toString(36);
161
+ }
19
162
  }
20
163
  exports.PackageBuilder = PackageBuilder;
@@ -1,7 +1,15 @@
1
- import type { KnowledgePackage } from '../types';
1
+ import type { KnowledgePackage, PolicyArtifacts } from '../types';
2
2
  export declare class PackageWriter {
3
3
  write(outputDir: string, knowledgePackage: KnowledgePackage): {
4
4
  jsonPath: string;
5
5
  gzipPath: string;
6
6
  };
7
+ writeArtifacts(outputDir: string, artifacts: PolicyArtifacts, options?: {
8
+ gzip?: boolean;
9
+ }): {
10
+ policyPath: string;
11
+ selectorsPath: string;
12
+ policyGzipPath?: string;
13
+ selectorsGzipPath?: string;
14
+ };
7
15
  }
@@ -18,5 +18,31 @@ class PackageWriter {
18
18
  fs_1.default.writeFileSync(gzipPath, zlib_1.default.gzipSync(content));
19
19
  return { jsonPath, gzipPath };
20
20
  }
21
+ writeArtifacts(outputDir, artifacts, options) {
22
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
23
+ const policyPath = path_1.default.join(outputDir, 'clippy-policy.json');
24
+ const selectorsPath = path_1.default.join(outputDir, 'clippy-selectors.json');
25
+ const policyContent = JSON.stringify(artifacts.policy, null, 2);
26
+ const selectorsContent = JSON.stringify(artifacts.selectorManifest, null, 2);
27
+ fs_1.default.writeFileSync(policyPath, policyContent, 'utf-8');
28
+ fs_1.default.writeFileSync(selectorsPath, selectorsContent, 'utf-8');
29
+ const shouldGzip = options?.gzip ?? true;
30
+ if (!shouldGzip) {
31
+ return {
32
+ policyPath,
33
+ selectorsPath,
34
+ };
35
+ }
36
+ const policyGzipPath = `${policyPath}.gz`;
37
+ const selectorsGzipPath = `${selectorsPath}.gz`;
38
+ fs_1.default.writeFileSync(policyGzipPath, zlib_1.default.gzipSync(policyContent));
39
+ fs_1.default.writeFileSync(selectorsGzipPath, zlib_1.default.gzipSync(selectorsContent));
40
+ return {
41
+ policyPath,
42
+ selectorsPath,
43
+ policyGzipPath,
44
+ selectorsGzipPath,
45
+ };
46
+ }
21
47
  }
22
48
  exports.PackageWriter = PackageWriter;
@@ -0,0 +1,11 @@
1
+ import type { PolicyArtifacts } from '../types';
2
+ export interface ArtifactRequest {
3
+ endpoint: string;
4
+ payload: Buffer;
5
+ extraHeaders?: Record<string, string>;
6
+ }
7
+ declare const POLICY_ENDPOINT = "https://api.clippy.dev/v1/policy";
8
+ declare const SELECTORS_ENDPOINT = "https://api.clippy.dev/v1/selectors";
9
+ declare const BUNDLE_ENDPOINT = "https://api.clippy.dev/v1/policy-artifacts";
10
+ export declare function buildArtifactRequests(artifacts: PolicyArtifacts, mode: 'single' | 'split'): ArtifactRequest[];
11
+ export { POLICY_ENDPOINT, SELECTORS_ENDPOINT, BUNDLE_ENDPOINT };
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BUNDLE_ENDPOINT = exports.SELECTORS_ENDPOINT = exports.POLICY_ENDPOINT = void 0;
7
+ exports.buildArtifactRequests = buildArtifactRequests;
8
+ const zlib_1 = __importDefault(require("zlib"));
9
+ const POLICY_ENDPOINT = 'https://api.clippy.dev/v1/policy';
10
+ exports.POLICY_ENDPOINT = POLICY_ENDPOINT;
11
+ const SELECTORS_ENDPOINT = 'https://api.clippy.dev/v1/selectors';
12
+ exports.SELECTORS_ENDPOINT = SELECTORS_ENDPOINT;
13
+ const BUNDLE_ENDPOINT = 'https://api.clippy.dev/v1/policy-artifacts';
14
+ exports.BUNDLE_ENDPOINT = BUNDLE_ENDPOINT;
15
+ function buildArtifactRequests(artifacts, mode) {
16
+ if (mode === 'split') {
17
+ const policyPayload = zlib_1.default.gzipSync(JSON.stringify(artifacts.policy));
18
+ const selectorsPayload = zlib_1.default.gzipSync(JSON.stringify(artifacts.selectorManifest));
19
+ return [
20
+ {
21
+ endpoint: POLICY_ENDPOINT,
22
+ payload: policyPayload,
23
+ extraHeaders: { 'X-Clippy-Artifact': 'policy' },
24
+ },
25
+ {
26
+ endpoint: SELECTORS_ENDPOINT,
27
+ payload: selectorsPayload,
28
+ extraHeaders: { 'X-Clippy-Artifact': 'selectors' },
29
+ },
30
+ ];
31
+ }
32
+ const bundle = zlib_1.default.gzipSync(JSON.stringify(artifacts));
33
+ return [
34
+ {
35
+ endpoint: BUNDLE_ENDPOINT,
36
+ payload: bundle,
37
+ extraHeaders: { 'X-Clippy-Artifact': 'bundle' },
38
+ },
39
+ ];
40
+ }
@@ -1,10 +1,15 @@
1
- import type { ClippyPluginOptions } from '../types';
1
+ import type { ClippyPluginOptions, PolicyArtifacts, UploadResult } from '../types';
2
2
  export declare class Uploader {
3
3
  private options;
4
4
  private endpoint;
5
+ private policyArtifactsEndpoint;
6
+ private policyEndpoint;
7
+ private selectorsEndpoint;
5
8
  private requestTimeoutMs;
6
9
  private maxAttempts;
7
10
  constructor(options: ClippyPluginOptions);
8
11
  upload(compressedPackage: Buffer): Promise<boolean>;
12
+ uploadArtifacts(artifacts: PolicyArtifacts): Promise<UploadResult>;
13
+ private uploadWithRetry;
9
14
  private uploadOnce;
10
15
  }
@@ -5,10 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Uploader = void 0;
7
7
  const https_1 = __importDefault(require("https"));
8
+ const UploadStrategy_1 = require("./UploadStrategy");
8
9
  class Uploader {
9
10
  constructor(options) {
10
11
  this.options = options;
11
12
  this.endpoint = 'https://api.clippy.dev/v1/knowledge';
13
+ this.policyArtifactsEndpoint = 'https://api.clippy.dev/v1/policy-artifacts';
14
+ this.policyEndpoint = 'https://api.clippy.dev/v1/policy';
15
+ this.selectorsEndpoint = 'https://api.clippy.dev/v1/selectors';
12
16
  this.requestTimeoutMs = 10000;
13
17
  this.maxAttempts = 3;
14
18
  }
@@ -18,7 +22,7 @@ class Uploader {
18
22
  let lastError = null;
19
23
  for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
20
24
  try {
21
- await this.uploadOnce(compressedPackage);
25
+ await this.uploadOnce(this.endpoint, compressedPackage);
22
26
  return true;
23
27
  }
24
28
  catch (err) {
@@ -30,7 +34,47 @@ class Uploader {
30
34
  }
31
35
  throw lastError || new Error('Upload failed after retries');
32
36
  }
33
- async uploadOnce(compressedPackage) {
37
+ async uploadArtifacts(artifacts) {
38
+ if (this.options.skipUpload) {
39
+ return {
40
+ skipped: true,
41
+ policyUploaded: false,
42
+ selectorsUploaded: false,
43
+ mode: this.options.artifactUploadMode || 'single',
44
+ };
45
+ }
46
+ const mode = this.options.artifactUploadMode || 'single';
47
+ const requests = (0, UploadStrategy_1.buildArtifactRequests)(artifacts, mode);
48
+ for (const req of requests) {
49
+ await this.uploadWithRetry(req.endpoint, req.payload, {
50
+ ...(req.extraHeaders || {}),
51
+ 'X-Build-Id': artifacts.metadata.buildId,
52
+ });
53
+ }
54
+ return {
55
+ skipped: false,
56
+ policyUploaded: true,
57
+ selectorsUploaded: true,
58
+ mode,
59
+ };
60
+ }
61
+ async uploadWithRetry(endpoint, payload, extraHeaders) {
62
+ let lastError = null;
63
+ for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
64
+ try {
65
+ await this.uploadOnce(endpoint, payload, extraHeaders);
66
+ return;
67
+ }
68
+ catch (err) {
69
+ lastError = err;
70
+ if (attempt < this.maxAttempts) {
71
+ await delay(250 * Math.pow(2, attempt - 1));
72
+ }
73
+ }
74
+ }
75
+ throw lastError || new Error('Upload failed after retries');
76
+ }
77
+ async uploadOnce(endpoint, compressedPackage, extraHeaders) {
34
78
  return new Promise((resolve, reject) => {
35
79
  let settled = false;
36
80
  const finalize = (fn) => {
@@ -39,7 +83,7 @@ class Uploader {
39
83
  settled = true;
40
84
  fn();
41
85
  };
42
- const req = https_1.default.request(this.endpoint, {
86
+ const req = https_1.default.request(endpoint, {
43
87
  method: 'POST',
44
88
  headers: {
45
89
  Authorization: `Bearer ${this.options.apiKey}`,
@@ -47,6 +91,7 @@ class Uploader {
47
91
  'Content-Type': 'application/octet-stream',
48
92
  'Content-Encoding': 'gzip',
49
93
  'Content-Length': compressedPackage.length,
94
+ ...extraHeaders,
50
95
  },
51
96
  }, (res) => {
52
97
  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcoder-x/plugin-shared",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",