@pagecrawl/n8n-nodes-pagecrawl 0.3.2 → 0.3.5

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.
@@ -5,6 +5,8 @@ const n8n_workflow_1 = require("n8n-workflow");
5
5
  const PageDescription_1 = require("./descriptions/PageDescription");
6
6
  const CheckDescription_1 = require("./descriptions/CheckDescription");
7
7
  const ScreenshotDescription_1 = require("./descriptions/ScreenshotDescription");
8
+ const package_json_1 = require("../../package.json");
9
+ const API_CLIENT_HEADER = { 'X-Api-Client': `n8n/${package_json_1.version}` };
8
10
  class PageCrawl {
9
11
  constructor() {
10
12
  this.description = {
@@ -31,6 +33,7 @@ class PageCrawl {
31
33
  headers: {
32
34
  Accept: 'application/json',
33
35
  'Content-Type': 'application/json',
36
+ ...API_CLIENT_HEADER,
34
37
  },
35
38
  },
36
39
  properties: [
@@ -62,9 +65,9 @@ class PageCrawl {
62
65
  displayName: 'Workspace',
63
66
  name: 'workspace',
64
67
  type: 'resourceLocator',
65
- required: false,
68
+ required: true,
66
69
  default: { mode: 'list', value: '' },
67
- description: 'Select workspace (auto-selected if you have only one)',
70
+ description: 'Select workspace to use for this operation',
68
71
  modes: [
69
72
  {
70
73
  displayName: 'From List',
@@ -99,6 +102,7 @@ class PageCrawl {
99
102
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
100
103
  method: 'GET',
101
104
  url: `${baseUrl}/api/user`,
105
+ headers: API_CLIENT_HEADER,
102
106
  json: true,
103
107
  });
104
108
  // Get available locations from user account
@@ -156,6 +160,7 @@ class PageCrawl {
156
160
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
157
161
  method: 'GET',
158
162
  url: `${baseUrl}/api/user`,
163
+ headers: API_CLIENT_HEADER,
159
164
  json: true,
160
165
  });
161
166
  // Get available frequencies from user account
@@ -177,6 +182,7 @@ class PageCrawl {
177
182
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
178
183
  method: 'GET',
179
184
  url: `${baseUrl}/api/user`,
185
+ headers: API_CLIENT_HEADER,
180
186
  json: true,
181
187
  });
182
188
  // Get available devices from user account
@@ -215,6 +221,7 @@ class PageCrawl {
215
221
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
216
222
  method: 'GET',
217
223
  url: `${baseUrl}/api/workspaces/${workspaceId}/ai/available-models`,
224
+ headers: API_CLIENT_HEADER,
218
225
  json: true,
219
226
  });
220
227
  // Get available AI models
@@ -239,9 +246,17 @@ class PageCrawl {
239
246
  listSearch: {
240
247
  async pageSearch(filter) {
241
248
  const baseUrl = 'https://pagecrawl.io';
249
+ // Get workspace ID - return empty if not selected
250
+ const workspaceLocator = this.getNodeParameter('workspace', 0, {});
251
+ const workspaceId = workspaceLocator?.value || '';
252
+ if (!workspaceId) {
253
+ return { results: [] };
254
+ }
242
255
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
243
256
  method: 'GET',
244
257
  url: `${baseUrl}/api/pages`,
258
+ qs: { workspace_id: workspaceId },
259
+ headers: API_CLIENT_HEADER,
245
260
  json: true,
246
261
  });
247
262
  const pages = response.data || response;
@@ -260,10 +275,18 @@ class PageCrawl {
260
275
  },
261
276
  async templateSearch(filter) {
262
277
  const baseUrl = 'https://pagecrawl.io';
278
+ // Get workspace ID - return empty if not selected
279
+ const workspaceLocator = this.getNodeParameter('workspace', 0, {});
280
+ const workspaceId = workspaceLocator?.value || '';
281
+ if (!workspaceId) {
282
+ return { results: [] };
283
+ }
263
284
  try {
264
285
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
265
286
  method: 'GET',
266
287
  url: `${baseUrl}/api/templates`,
288
+ qs: { workspace_id: workspaceId },
289
+ headers: API_CLIENT_HEADER,
267
290
  json: true,
268
291
  });
269
292
  // Handle various response formats
@@ -292,6 +315,7 @@ class PageCrawl {
292
315
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
293
316
  method: 'GET',
294
317
  url: `${baseUrl}/api/user`,
318
+ headers: API_CLIENT_HEADER,
295
319
  json: true,
296
320
  });
297
321
  // Extract workspaces from user response (may be nested under user)
@@ -316,11 +340,18 @@ class PageCrawl {
316
340
  },
317
341
  async folderSearch(filter) {
318
342
  const baseUrl = 'https://pagecrawl.io';
343
+ // Get workspace ID - return empty if not selected
344
+ const workspaceLocator = this.getNodeParameter('workspace', 0, {});
345
+ const workspaceId = workspaceLocator?.value || '';
346
+ if (!workspaceId) {
347
+ return { results: [] };
348
+ }
319
349
  try {
320
350
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
321
351
  method: 'GET',
322
352
  url: `${baseUrl}/api/folders`,
323
- qs: { all: true },
353
+ qs: { all: true, workspace_id: workspaceId },
354
+ headers: API_CLIENT_HEADER,
324
355
  json: true,
325
356
  });
326
357
  // Handle various response formats
@@ -355,10 +386,18 @@ class PageCrawl {
355
386
  },
356
387
  async authSearch(filter) {
357
388
  const baseUrl = 'https://pagecrawl.io';
389
+ // Get workspace ID - return empty if not selected
390
+ const workspaceLocator = this.getNodeParameter('workspace', 0, {});
391
+ const workspaceId = workspaceLocator?.value || '';
392
+ if (!workspaceId) {
393
+ return { results: [] };
394
+ }
358
395
  try {
359
396
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
360
397
  method: 'GET',
361
398
  url: `${baseUrl}/api/auths`,
399
+ qs: { workspace_id: workspaceId },
400
+ headers: API_CLIENT_HEADER,
362
401
  json: true,
363
402
  });
364
403
  // Handle various response formats
@@ -404,27 +443,14 @@ class PageCrawl {
404
443
  }
405
444
  return [];
406
445
  };
407
- // Helper to get workspace ID, auto-selecting if only one exists
408
- const getWorkspaceId = async (index) => {
446
+ // Helper to get workspace ID from the required workspace field
447
+ const getWorkspaceId = (index) => {
409
448
  const workspaceLocator = this.getNodeParameter('workspace', index, {});
410
449
  const workspaceId = workspaceLocator?.value || '';
411
- if (workspaceId) {
412
- return workspaceId;
413
- }
414
- // Fetch workspaces and auto-select if only one
415
- const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
416
- method: 'GET',
417
- url: `${baseUrl}/api/user`,
418
- json: true,
419
- });
420
- const workspaces = response.workspaces || response.user?.workspaces || [];
421
- if (workspaces.length === 1) {
422
- return String(workspaces[0].id);
450
+ if (!workspaceId) {
451
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Workspace is required', { itemIndex: index });
423
452
  }
424
- if (workspaces.length === 0) {
425
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No workspaces found', { itemIndex: index });
426
- }
427
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Please select a workspace - multiple workspaces available', { itemIndex: index });
453
+ return workspaceId;
428
454
  };
429
455
  for (let i = 0; i < items.length; i++) {
430
456
  const resource = this.getNodeParameter('resource', i);
@@ -435,8 +461,9 @@ class PageCrawl {
435
461
  if (resource === 'page') {
436
462
  if (operation === 'get') {
437
463
  const pageId = getPageId(i);
464
+ const workspaceId = getWorkspaceId(i);
438
465
  const options = this.getNodeParameter('options', i);
439
- const qs = {};
466
+ const qs = { workspace_id: workspaceId };
440
467
  if (options.simple) {
441
468
  qs.simple = 1;
442
469
  }
@@ -447,14 +474,16 @@ class PageCrawl {
447
474
  method: 'GET',
448
475
  url: `${baseUrl}/api/pages/${pageId}`,
449
476
  qs,
477
+ headers: API_CLIENT_HEADER,
450
478
  json: true,
451
479
  });
452
480
  }
453
481
  else if (operation === 'createSimple') {
454
482
  const url = this.getNodeParameter('url', i);
455
483
  const name = this.getNodeParameter('name', i, '');
484
+ const workspaceId = getWorkspaceId(i);
456
485
  const additionalFields = this.getNodeParameter('additionalFields', i);
457
- const body = { url };
486
+ const body = { url, workspace_id: workspaceId };
458
487
  if (name) {
459
488
  body.name = name;
460
489
  }
@@ -471,6 +500,7 @@ class PageCrawl {
471
500
  method: 'POST',
472
501
  url: `${baseUrl}/api/track-simple`,
473
502
  body,
503
+ headers: API_CLIENT_HEADER,
474
504
  json: true,
475
505
  });
476
506
  }
@@ -533,9 +563,9 @@ class PageCrawl {
533
563
  delete body.template_id;
534
564
  if (!body.auth_id || body.auth_id === 0)
535
565
  delete body.auth_id;
536
- // Auto-select workspace if not set
566
+ // Use selected workspace
537
567
  if (!body.workspace_id) {
538
- body.workspace_id = await getWorkspaceId(i);
568
+ body.workspace_id = getWorkspaceId(i);
539
569
  }
540
570
  // Transform fixedCollection fields to arrays
541
571
  if (body.actions && typeof body.actions === 'object') {
@@ -576,14 +606,17 @@ class PageCrawl {
576
606
  method: 'POST',
577
607
  url: `${baseUrl}/api/pages`,
578
608
  body,
609
+ headers: API_CLIENT_HEADER,
579
610
  json: true,
580
611
  });
581
612
  }
582
613
  else if (operation === 'update') {
583
614
  const pageId = getPageId(i);
615
+ const workspaceId = getWorkspaceId(i);
584
616
  const updateFields = this.getNodeParameter('updateFields', i);
585
617
  const additionalFields = this.getNodeParameter('additionalFields', i);
586
618
  const body = { ...updateFields, ...additionalFields };
619
+ const qs = { workspace_id: workspaceId };
587
620
  // Convert tags from comma-separated string to array
588
621
  if (typeof body.tags === 'string' && body.tags) {
589
622
  body.tags = body.tags.split(',').map((tag) => tag.trim()).filter(Boolean);
@@ -657,23 +690,29 @@ class PageCrawl {
657
690
  responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
658
691
  method: 'PUT',
659
692
  url: `${baseUrl}/api/pages/${pageId}`,
693
+ qs,
660
694
  body,
695
+ headers: API_CLIENT_HEADER,
661
696
  json: true,
662
697
  });
663
698
  }
664
699
  else if (operation === 'delete') {
665
700
  const pageId = getPageId(i);
701
+ const workspaceId = getWorkspaceId(i);
666
702
  responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
667
703
  method: 'DELETE',
668
704
  url: `${baseUrl}/api/pages/${pageId}`,
705
+ qs: { workspace_id: workspaceId },
706
+ headers: API_CLIENT_HEADER,
669
707
  json: true,
670
708
  });
671
709
  responseData = { success: true, deleted: pageId };
672
710
  }
673
711
  else if (operation === 'runCheckNow') {
674
712
  const pageId = getPageId(i);
713
+ const workspaceId = getWorkspaceId(i);
675
714
  const options = this.getNodeParameter('runCheckOptions', i);
676
- const qs = {};
715
+ const qs = { workspace_id: workspaceId };
677
716
  if (options.skip_first_notification) {
678
717
  qs.skip_first_notification = 1;
679
718
  }
@@ -681,6 +720,7 @@ class PageCrawl {
681
720
  method: 'PUT',
682
721
  url: `${baseUrl}/api/pages/${pageId}/check`,
683
722
  qs,
723
+ headers: API_CLIENT_HEADER,
684
724
  json: true,
685
725
  });
686
726
  responseData = { success: true, message: 'Check triggered', pageId };
@@ -688,20 +728,20 @@ class PageCrawl {
688
728
  }
689
729
  else if (resource === 'check') {
690
730
  const pageId = getPageId(i);
731
+ const workspaceId = getWorkspaceId(i);
691
732
  if (operation === 'getHistory') {
692
733
  const options = this.getNodeParameter('options', i);
693
- const qs = {};
734
+ const qs = { workspace_id: workspaceId };
694
735
  // Default to simple mode unless advanced is enabled
695
736
  if (!options.advanced) {
696
737
  qs.simple = 1;
697
738
  }
698
- if (options.take) {
699
- qs.take = options.take;
700
- }
739
+ qs.take = options.take ?? 2;
701
740
  responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
702
741
  method: 'GET',
703
742
  url: `${baseUrl}/api/pages/${pageId}/history`,
704
743
  qs,
744
+ headers: API_CLIENT_HEADER,
705
745
  json: true,
706
746
  });
707
747
  }
@@ -710,6 +750,8 @@ class PageCrawl {
710
750
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
711
751
  method: 'GET',
712
752
  url: `${baseUrl}/api/pages/${pageId}/checks/${checkId}/diff.png`,
753
+ qs: { workspace_id: workspaceId },
754
+ headers: API_CLIENT_HEADER,
713
755
  encoding: 'arraybuffer',
714
756
  });
715
757
  const binaryData = await this.helpers.prepareBinaryData(response, `diff-${pageId}-${checkId}.png`, 'image/png');
@@ -722,7 +764,9 @@ class PageCrawl {
722
764
  const htmlContent = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
723
765
  method: 'GET',
724
766
  url: `${baseUrl}/api/pages/${pageId}/checks/${checkId}/diff.html`,
767
+ qs: { workspace_id: workspaceId },
725
768
  headers: {
769
+ ...API_CLIENT_HEADER,
726
770
  Accept: 'text/html',
727
771
  },
728
772
  });
@@ -737,7 +781,9 @@ class PageCrawl {
737
781
  const markdownContent = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
738
782
  method: 'GET',
739
783
  url: `${baseUrl}/api/pages/${pageId}/checks/${checkId}/diff.markdown`,
784
+ qs: { workspace_id: workspaceId },
740
785
  headers: {
786
+ ...API_CLIENT_HEADER,
741
787
  Accept: 'text/markdown',
742
788
  },
743
789
  });
@@ -750,6 +796,7 @@ class PageCrawl {
750
796
  }
751
797
  else if (resource === 'screenshot') {
752
798
  const pageId = getPageId(i);
799
+ const workspaceId = getWorkspaceId(i);
753
800
  const checkId = this.getNodeParameter('checkId', i, 'latest');
754
801
  let endpoint = '';
755
802
  if (operation === 'getScreenshot') {
@@ -759,7 +806,7 @@ class PageCrawl {
759
806
  endpoint = `/pages/${pageId}/checks/${checkId}/diff`;
760
807
  }
761
808
  const previous = operation === 'getScreenshot' ? this.getNodeParameter('previous', i, false) : false;
762
- const qs = {};
809
+ const qs = { workspace_id: workspaceId };
763
810
  if (previous) {
764
811
  qs.previous = 1;
765
812
  }
@@ -767,6 +814,7 @@ class PageCrawl {
767
814
  method: 'GET',
768
815
  url: `${baseUrl}/api${endpoint}`,
769
816
  qs,
817
+ headers: API_CLIENT_HEADER,
770
818
  encoding: 'arraybuffer',
771
819
  });
772
820
  const binaryData = await this.helpers.prepareBinaryData(response, `screenshot-${pageId}.png`, 'image/png');
@@ -68,16 +68,20 @@ exports.checkFields = [
68
68
  },
69
69
  },
70
70
  {
71
- displayName: 'By Slug',
72
- name: 'slug',
71
+ displayName: 'By URL',
72
+ name: 'url',
73
73
  type: 'string',
74
- placeholder: 'e.g. my-page-name',
74
+ placeholder: 'e.g. https://pagecrawl.io/app/pages/example-domain',
75
+ extractValue: {
76
+ type: 'regex',
77
+ regex: 'https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/([a-z0-9_-]+)',
78
+ },
75
79
  validation: [
76
80
  {
77
81
  type: 'regex',
78
82
  properties: {
79
- regex: '^[a-z0-9-]+$',
80
- errorMessage: 'Slug must contain only lowercase letters, numbers, and hyphens',
83
+ regex: '^https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/[a-z0-9_-]+$',
84
+ errorMessage: 'Must be a valid PageCrawl page URL (e.g. https://pagecrawl.io/app/pages/my-page)',
81
85
  },
82
86
  },
83
87
  ],
@@ -28,10 +28,10 @@ exports.pageOperations = [
28
28
  action: 'Create an advanced page',
29
29
  },
30
30
  {
31
- name: 'Delete',
32
- value: 'delete',
33
- description: 'Delete a tracked page',
34
- action: 'Delete a page',
31
+ name: 'Run Check Now',
32
+ value: 'runCheckNow',
33
+ description: 'Trigger an immediate check for a page',
34
+ action: 'Run check now',
35
35
  },
36
36
  {
37
37
  name: 'Get',
@@ -39,18 +39,18 @@ exports.pageOperations = [
39
39
  description: 'Get a tracked page configuration',
40
40
  action: 'Get a page',
41
41
  },
42
- {
43
- name: 'Run Check Now',
44
- value: 'runCheckNow',
45
- description: 'Trigger an immediate check for a page',
46
- action: 'Run check now',
47
- },
48
42
  {
49
43
  name: 'Update',
50
44
  value: 'update',
51
45
  description: 'Update a tracked page',
52
46
  action: 'Update a page',
53
47
  },
48
+ {
49
+ name: 'Delete',
50
+ value: 'delete',
51
+ description: 'Delete a tracked page',
52
+ action: 'Delete a page',
53
+ },
54
54
  ],
55
55
  default: 'get',
56
56
  },
@@ -83,16 +83,20 @@ exports.pageFields = [
83
83
  },
84
84
  },
85
85
  {
86
- displayName: 'By Slug',
87
- name: 'slug',
86
+ displayName: 'By URL',
87
+ name: 'url',
88
88
  type: 'string',
89
- placeholder: 'e.g. my-page-name',
89
+ placeholder: 'e.g. https://pagecrawl.io/app/pages/example-domain',
90
+ extractValue: {
91
+ type: 'regex',
92
+ regex: 'https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/([a-z0-9_-]+)',
93
+ },
90
94
  validation: [
91
95
  {
92
96
  type: 'regex',
93
97
  properties: {
94
- regex: '^[a-z0-9-]+$',
95
- errorMessage: 'Slug must contain only lowercase letters, numbers, and hyphens',
98
+ regex: '^https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/[a-z0-9_-]+$',
99
+ errorMessage: 'Must be a valid PageCrawl page URL (e.g. https://pagecrawl.io/app/pages/my-page)',
96
100
  },
97
101
  },
98
102
  ],
@@ -183,6 +187,19 @@ exports.pageFields = [
183
187
  placeholder: 'https://example.com',
184
188
  description: 'The URL to track',
185
189
  },
190
+ {
191
+ displayName: 'Title',
192
+ name: 'name',
193
+ type: 'string',
194
+ displayOptions: {
195
+ show: {
196
+ resource: ['page'],
197
+ operation: ['createSimple'],
198
+ },
199
+ },
200
+ default: '',
201
+ description: 'Optional title for this page (defaults to page title if empty)',
202
+ },
186
203
  {
187
204
  displayName: 'Tracking Type',
188
205
  name: 'trackingType',
@@ -355,10 +372,9 @@ exports.pageFields = [
355
372
  description: 'The URL to track',
356
373
  },
357
374
  {
358
- displayName: 'Name',
375
+ displayName: 'Title',
359
376
  name: 'name',
360
377
  type: 'string',
361
- required: true,
362
378
  displayOptions: {
363
379
  show: {
364
380
  resource: ['page'],
@@ -366,7 +382,7 @@ exports.pageFields = [
366
382
  },
367
383
  },
368
384
  default: '',
369
- description: 'Label for the page',
385
+ description: 'Optional title for this page (defaults to page title if empty)',
370
386
  },
371
387
  {
372
388
  displayName: 'Tracked Elements',
@@ -1094,11 +1110,11 @@ exports.pageFields = [
1094
1110
  description: 'How often to check for changes',
1095
1111
  },
1096
1112
  {
1097
- displayName: 'Name',
1113
+ displayName: 'Title',
1098
1114
  name: 'name',
1099
1115
  type: 'string',
1100
1116
  default: '',
1101
- description: 'Label for the page',
1117
+ description: 'Title for this page',
1102
1118
  },
1103
1119
  {
1104
1120
  displayName: 'URL',
@@ -56,16 +56,20 @@ exports.screenshotFields = [
56
56
  },
57
57
  },
58
58
  {
59
- displayName: 'By Slug',
60
- name: 'slug',
59
+ displayName: 'By URL',
60
+ name: 'url',
61
61
  type: 'string',
62
- placeholder: 'e.g. my-page-name',
62
+ placeholder: 'e.g. https://pagecrawl.io/app/pages/example-domain',
63
+ extractValue: {
64
+ type: 'regex',
65
+ regex: 'https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/([a-z0-9_-]+)',
66
+ },
63
67
  validation: [
64
68
  {
65
69
  type: 'regex',
66
70
  properties: {
67
- regex: '^[a-z0-9-]+$',
68
- errorMessage: 'Slug must contain only lowercase letters, numbers, and hyphens',
71
+ regex: '^https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/[a-z0-9_-]+$',
72
+ errorMessage: 'Must be a valid PageCrawl page URL (e.g. https://pagecrawl.io/app/pages/my-page)',
69
73
  },
70
74
  },
71
75
  ],
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PageCrawlTrigger = void 0;
4
4
  const types_1 = require("../PageCrawl/types");
5
+ const package_json_1 = require("../../package.json");
6
+ const API_CLIENT_HEADER = { 'X-Api-Client': `n8n/${package_json_1.version}` };
5
7
  class PageCrawlTrigger {
6
8
  constructor() {
7
9
  this.description = {
@@ -74,16 +76,20 @@ class PageCrawlTrigger {
74
76
  },
75
77
  },
76
78
  {
77
- displayName: 'By Slug',
78
- name: 'slug',
79
+ displayName: 'By URL',
80
+ name: 'url',
79
81
  type: 'string',
80
- placeholder: 'e.g. my-page-name',
82
+ placeholder: 'e.g. https://pagecrawl.io/app/pages/example-domain',
83
+ extractValue: {
84
+ type: 'regex',
85
+ regex: 'https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/([a-z0-9_-]+)',
86
+ },
81
87
  validation: [
82
88
  {
83
89
  type: 'regex',
84
90
  properties: {
85
- regex: '^[a-z0-9-]+$',
86
- errorMessage: 'Slug must contain only lowercase letters, numbers, and hyphens',
91
+ regex: '^https:\\/\\/pagecrawl\\.io\\/app\\/pages\\/[a-z0-9_-]+$',
92
+ errorMessage: 'Must be a valid PageCrawl page URL (e.g. https://pagecrawl.io/app/pages/my-page)',
87
93
  },
88
94
  },
89
95
  ],
@@ -146,6 +152,7 @@ class PageCrawlTrigger {
146
152
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
147
153
  method: 'GET',
148
154
  url: `${baseUrl}/api/user`,
155
+ headers: API_CLIENT_HEADER,
149
156
  json: true,
150
157
  });
151
158
  // Extract workspaces from user response (may be nested under user)
@@ -169,9 +176,17 @@ class PageCrawlTrigger {
169
176
  },
170
177
  async pageSearch(filter) {
171
178
  const baseUrl = 'https://pagecrawl.io';
179
+ // Get workspace ID - return empty if not selected
180
+ const workspaceLocator = this.getNodeParameter('workspace', 0, {});
181
+ const workspaceId = workspaceLocator?.value || '';
182
+ if (!workspaceId) {
183
+ return { results: [] };
184
+ }
172
185
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
173
186
  method: 'GET',
174
187
  url: `${baseUrl}/api/pages`,
188
+ qs: { workspace_id: workspaceId },
189
+ headers: API_CLIENT_HEADER,
175
190
  json: true,
176
191
  });
177
192
  const pages = response.data || response;
@@ -196,6 +211,8 @@ class PageCrawlTrigger {
196
211
  const webhookData = this.getWorkflowStaticData('node');
197
212
  const webhookUrl = this.getNodeWebhookUrl('default');
198
213
  const baseUrl = 'https://pagecrawl.io';
214
+ const workspaceLocator = this.getNodeParameter('workspace', { mode: 'list', value: '' });
215
+ const workspaceId = workspaceLocator.value || '';
199
216
  if (!webhookData.webhookId) {
200
217
  return false;
201
218
  }
@@ -203,6 +220,8 @@ class PageCrawlTrigger {
203
220
  const response = await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
204
221
  method: 'GET',
205
222
  url: `${baseUrl}/api/hooks`,
223
+ qs: workspaceId ? { workspace_id: workspaceId } : {},
224
+ headers: API_CLIENT_HEADER,
206
225
  json: true,
207
226
  });
208
227
  const existingWebhook = response.find((webhook) => webhook.id === webhookData.webhookId);
@@ -254,6 +273,7 @@ class PageCrawlTrigger {
254
273
  method: 'POST',
255
274
  url: `${baseUrl}/api/hooks`,
256
275
  body,
276
+ headers: API_CLIENT_HEADER,
257
277
  json: true,
258
278
  });
259
279
  if (!response.id) {
@@ -266,6 +286,7 @@ class PageCrawlTrigger {
266
286
  await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
267
287
  method: 'PUT',
268
288
  url: `${baseUrl}/api/hooks/${response.id}/test`,
289
+ headers: API_CLIENT_HEADER,
269
290
  json: true,
270
291
  });
271
292
  }
@@ -291,6 +312,7 @@ class PageCrawlTrigger {
291
312
  await this.helpers.httpRequestWithAuthentication.call(this, 'pageCrawlApi', {
292
313
  method: 'DELETE',
293
314
  url: `${baseUrl}/api/hooks/${webhookData.webhookId}`,
315
+ headers: API_CLIENT_HEADER,
294
316
  json: true,
295
317
  });
296
318
  }
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@pagecrawl/n8n-nodes-pagecrawl",
3
+ "version": "0.3.4",
4
+ "description": "n8n node for PageCrawl.io - Website monitoring and change detection",
5
+ "keywords": [
6
+ "n8n",
7
+ "n8n-community-node-package",
8
+ "website-monitoring",
9
+ "change-detection",
10
+ "web-scraping",
11
+ "pagecrawl",
12
+ "automation"
13
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://pagecrawl.io",
16
+ "author": {
17
+ "name": "PageCrawl.io",
18
+ "email": "support@pagecrawl.io"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/pagecrawl/n8n-nodes-pagecrawl.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/pagecrawl/n8n-nodes-pagecrawl/issues"
26
+ },
27
+ "main": "index.js",
28
+ "scripts": {
29
+ "build": "tsc && cp nodes/PageCrawl/pagecrawl.svg dist/nodes/PageCrawl/ && cp nodes/PageCrawlTrigger/pagecrawl.svg dist/nodes/PageCrawlTrigger/",
30
+ "dev": "npm run build && npx n8n start",
31
+ "dev:build": "npm run build",
32
+ "n8n:start": "npx n8n start",
33
+ "lint": "eslint nodes credentials --ext .ts",
34
+ "lint:fix": "eslint nodes credentials --ext .ts --fix",
35
+ "link": "npm run build && npm link",
36
+ "test": "jest --testPathIgnorePatterns=integration",
37
+ "test:integration": "jest --testPathPattern=integration --testTimeout=60000"
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "n8n": {
43
+ "n8nNodesApiVersion": 1,
44
+ "credentials": [
45
+ "dist/credentials/PageCrawlApi.credentials.js"
46
+ ],
47
+ "nodes": [
48
+ "dist/nodes/PageCrawl/PageCrawl.node.js",
49
+ "dist/nodes/PageCrawlTrigger/PageCrawlTrigger.node.js"
50
+ ]
51
+ },
52
+ "devDependencies": {
53
+ "@types/jest": "^30.0.0",
54
+ "@types/node": "^20.0.0",
55
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
56
+ "@typescript-eslint/parser": "^7.0.0",
57
+ "axios": "^1.6.0",
58
+ "dotenv": "^17.2.3",
59
+ "eslint": "^8.56.0",
60
+ "eslint-plugin-n8n-nodes-base": "^1.16.0",
61
+ "jest": "^29.7.0",
62
+ "n8n": "^2.2.0",
63
+ "n8n-workflow": "^2.2.2",
64
+ "ts-jest": "^29.4.6",
65
+ "typescript": "^5.0.0"
66
+ },
67
+ "peerDependencies": {
68
+ "n8n-workflow": ">=1.0.0"
69
+ }
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagecrawl/n8n-nodes-pagecrawl",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "n8n node for PageCrawl.io - Website monitoring and change detection",
5
5
  "keywords": [
6
6
  "n8n",
@@ -60,7 +60,7 @@
60
60
  "eslint-plugin-n8n-nodes-base": "^1.16.0",
61
61
  "jest": "^29.7.0",
62
62
  "n8n": "^2.2.0",
63
- "n8n-workflow": "~2.2.0",
63
+ "n8n-workflow": "^2.2.2",
64
64
  "ts-jest": "^29.4.6",
65
65
  "typescript": "^5.0.0"
66
66
  },