@startsimpli/api 0.2.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startsimpli/api",
3
- "version": "0.2.6",
3
+ "version": "0.4.0",
4
4
  "description": "Type-safe Django REST API client for StartSimpli apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -4,49 +4,49 @@
4
4
 
5
5
  export const ENDPOINTS = {
6
6
  // Contacts
7
- CONTACTS: 'contacts',
8
- CONTACTS_BULK: 'contacts/bulk',
9
- CONTACT: (id: string) => `contacts/${id}`,
7
+ CONTACTS: 'api/v1/contacts',
8
+ CONTACTS_BULK: 'api/v1/contacts/bulk',
9
+ CONTACT: (id: string) => `api/v1/contacts/${id}`,
10
10
 
11
11
  // Organizations
12
- ORGANIZATIONS: 'organizations',
13
- ORGANIZATIONS_BULK: 'organizations/bulk',
14
- ORGANIZATION: (id: string) => `organizations/${id}`,
12
+ ORGANIZATIONS: 'api/v1/organizations',
13
+ ORGANIZATIONS_BULK: 'api/v1/organizations/bulk',
14
+ ORGANIZATION: (id: string) => `api/v1/organizations/${id}`,
15
15
 
16
16
  // Core entities
17
- TAGS: 'core/tags',
18
- TAG: (id: string) => `core/tags/${id}`,
19
- ENTITY_TAGS: 'core/entity-tags',
20
- ENTITY_TAG: (id: string) => `core/entity-tags/${id}`,
21
- METRICS: 'core/metrics',
22
- METRIC: (id: string) => `core/metrics/${id}`,
23
- PROFILES: 'core/profiles',
24
- PROFILE: (id: string) => `core/profiles/${id}`,
25
- ATTRIBUTES: 'core/attributes',
26
- ATTRIBUTE: (id: string) => `core/attributes/${id}`,
27
- RELATIONSHIPS: 'core/relationships',
28
- RELATIONSHIP: (id: string) => `core/relationships/${id}`,
17
+ TAGS: 'api/v1/core/tags',
18
+ TAG: (id: string) => `api/v1/core/tags/${id}`,
19
+ ENTITY_TAGS: 'api/v1/core/entity-tags',
20
+ ENTITY_TAG: (id: string) => `api/v1/core/entity-tags/${id}`,
21
+ METRICS: 'api/v1/core/metrics',
22
+ METRIC: (id: string) => `api/v1/core/metrics/${id}`,
23
+ PROFILES: 'api/v1/core/profiles',
24
+ PROFILE: (id: string) => `api/v1/core/profiles/${id}`,
25
+ ATTRIBUTES: 'api/v1/core/attributes',
26
+ ATTRIBUTE: (id: string) => `api/v1/core/attributes/${id}`,
27
+ RELATIONSHIPS: 'api/v1/core/relationships',
28
+ RELATIONSHIP: (id: string) => `api/v1/core/relationships/${id}`,
29
29
 
30
30
  // Workflows
31
- WORKFLOWS: 'workflows',
32
- WORKFLOW: (id: string) => `workflows/${id}`,
33
- WORKFLOW_EXECUTE: (id: string) => `workflows/${id}/execute`,
31
+ WORKFLOWS: 'api/v1/workflows',
32
+ WORKFLOW: (id: string) => `api/v1/workflows/${id}`,
33
+ WORKFLOW_EXECUTE: (id: string) => `api/v1/workflows/${id}/execute`,
34
34
 
35
35
  // Messages
36
- MESSAGES: 'messages',
37
- MESSAGE: (id: string) => `messages/${id}`,
38
- MESSAGE_SEND: (id: string) => `messages/${id}/send`,
36
+ MESSAGES: 'api/v1/messages',
37
+ MESSAGE: (id: string) => `api/v1/messages/${id}`,
38
+ MESSAGE_SEND: (id: string) => `api/v1/messages/${id}/send`,
39
39
 
40
40
  // Funnels
41
- FUNNELS: 'funnels',
42
- FUNNEL: (id: string) => `funnels/${id}`,
43
- FUNNEL_RUN: (id: string) => `funnels/${id}/run`,
44
- FUNNEL_RUNS: (id: string) => `funnels/${id}/runs`,
45
- FUNNEL_RUN_ITEM: (funnelId: string, runId: string) => `funnels/${funnelId}/runs/${runId}`,
46
- FUNNEL_PREVIEW: (id: string) => `funnels/${id}/preview`,
47
- FUNNEL_RESULTS: (id: string) => `funnels/${id}/results`,
48
- FUNNEL_TEMPLATES: 'funnels/templates',
49
- FUNNEL_RUN_BY_ID: (runId: string) => `funnel-runs/${runId}`,
50
- FUNNEL_RUN_CANCEL: (runId: string) => `funnel-runs/${runId}/cancel`,
51
- FUNNEL_RUNS_GLOBAL: 'funnel-runs',
41
+ FUNNELS: 'api/v1/funnels',
42
+ FUNNEL: (id: string) => `api/v1/funnels/${id}`,
43
+ FUNNEL_RUN: (id: string) => `api/v1/funnels/${id}/run`,
44
+ FUNNEL_RUNS: (id: string) => `api/v1/funnels/${id}/runs`,
45
+ FUNNEL_RUN_ITEM: (funnelId: string, runId: string) => `api/v1/funnels/${funnelId}/runs/${runId}`,
46
+ FUNNEL_PREVIEW: (id: string) => `api/v1/funnels/${id}/preview`,
47
+ FUNNEL_RESULTS: (id: string) => `api/v1/funnels/${id}/results`,
48
+ FUNNEL_TEMPLATES: 'api/v1/funnels/templates',
49
+ FUNNEL_RUN_BY_ID: (runId: string) => `api/v1/funnel-runs/${runId}`,
50
+ FUNNEL_RUN_CANCEL: (runId: string) => `api/v1/funnel-runs/${runId}/cancel`,
51
+ FUNNEL_RUNS_GLOBAL: 'api/v1/funnel-runs',
52
52
  } as const;
@@ -75,12 +75,15 @@ export class FetchWrapper {
75
75
  const headers = await this.buildHeaders(customHeaders);
76
76
 
77
77
  // Execute request
78
+ const hasAuth = headers.has('Authorization');
79
+ console.log(`[FetchWrapper] ${method} ${url} (auth=${hasAuth})`);
78
80
  let response = await fetch(url, {
79
81
  method,
80
82
  headers,
81
83
  credentials: 'include',
82
84
  ...fetchOptions,
83
85
  });
86
+ console.log(`[FetchWrapper] ${method} ${url} → ${response.status} ${response.statusText}`);
84
87
 
85
88
  // Handle 401 Unauthorized - attempt token refresh
86
89
  if (response.status === 401) {
@@ -17,25 +17,39 @@ export function buildUrl({ baseUrl, endpoint, params, id }: UrlBuilderOptions):
17
17
  const cleanBase = baseUrl.replace(/\/$/, '');
18
18
  const cleanEndpoint = endpoint.replace(/^\//, '');
19
19
 
20
+ // Separate any inline query string from the endpoint path
21
+ const [pathPart, inlineQuery] = `${cleanBase}/${cleanEndpoint}`.split('?', 2);
22
+
20
23
  // Build path with optional ID
21
- let path = `${cleanBase}/${cleanEndpoint}`;
24
+ let path = pathPart;
22
25
  if (id !== undefined) {
23
26
  path += `/${id}`;
24
27
  }
25
28
 
26
- // Add trailing slash (Django convention)
27
- if (!path.endsWith('/')) {
29
+ // Add trailing slash (Django convention) — only when calling Django
30
+ // directly (baseUrl is set). When proxied through Next.js (baseUrl empty),
31
+ // the rewrite rule adds the slash; adding it here causes a 308 redirect
32
+ // that drops the Authorization header.
33
+ if (baseUrl && !path.endsWith('/')) {
28
34
  path += '/';
29
35
  }
30
36
 
31
- // Add query parameters
32
- if (params && Object.keys(params).length > 0) {
33
- const queryString = buildQueryString(params);
34
- if (queryString) {
35
- path += `?${queryString}`;
37
+ // Merge inline query params with explicit params
38
+ const mergedParams = new URLSearchParams(inlineQuery || '');
39
+ if (params) {
40
+ const explicit = buildQueryString(params);
41
+ if (explicit) {
42
+ for (const [k, v] of new URLSearchParams(explicit)) {
43
+ mergedParams.append(k, v);
44
+ }
36
45
  }
37
46
  }
38
47
 
48
+ const qs = mergedParams.toString();
49
+ if (qs) {
50
+ path += `?${qs}`;
51
+ }
52
+
39
53
  return path;
40
54
  }
41
55