@ezcoder.dev/sdk 1.1.0 → 1.2.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.
Files changed (41) hide show
  1. package/dist/analytics/index.js +7 -5
  2. package/dist/analytics/index.js.map +1 -1
  3. package/dist/auth/index.d.ts +1 -0
  4. package/dist/auth/index.js +25 -6
  5. package/dist/auth/index.js.map +1 -1
  6. package/dist/chunk-CQKYANAW.js +44 -0
  7. package/dist/chunk-CQKYANAW.js.map +1 -0
  8. package/dist/{chunk-GPF4AYNG.js → chunk-HJ2EIZ4S.js} +2 -2
  9. package/dist/{chunk-2WG4O4J2.js → chunk-I2YGB7Z6.js} +14 -50
  10. package/dist/chunk-I2YGB7Z6.js.map +1 -0
  11. package/dist/chunk-LIUE7M7K.js +72 -0
  12. package/dist/chunk-LIUE7M7K.js.map +1 -0
  13. package/dist/{chunk-AWU47M6N.js → chunk-QHB7LGCA.js} +91 -8
  14. package/dist/chunk-QHB7LGCA.js.map +1 -0
  15. package/dist/{chunk-7VGYFCQC.js → chunk-R4MDAYFO.js} +7 -5
  16. package/dist/{chunk-7VGYFCQC.js.map → chunk-R4MDAYFO.js.map} +1 -1
  17. package/dist/cms/index.js +4 -2
  18. package/dist/cms/index.js.map +1 -1
  19. package/dist/cron/index.d.ts +17 -0
  20. package/dist/cron/index.js +26 -12
  21. package/dist/cron/index.js.map +1 -1
  22. package/dist/database/index.js +4 -3
  23. package/dist/email/index.d.ts +18 -0
  24. package/dist/email/index.js +27 -12
  25. package/dist/email/index.js.map +1 -1
  26. package/dist/index.d.ts +167 -1
  27. package/dist/index.js +263 -7
  28. package/dist/index.js.map +1 -1
  29. package/dist/notifications/index.d.ts +27 -2
  30. package/dist/notifications/index.js +53 -18
  31. package/dist/notifications/index.js.map +1 -1
  32. package/dist/payments/index.js +6 -4
  33. package/dist/payments/index.js.map +1 -1
  34. package/dist/roles/index.js +6 -4
  35. package/dist/roles/index.js.map +1 -1
  36. package/dist/storage/index.js +7 -5
  37. package/dist/storage/index.js.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-2WG4O4J2.js.map +0 -1
  40. package/dist/chunk-AWU47M6N.js.map +0 -1
  41. /package/dist/{chunk-GPF4AYNG.js.map → chunk-HJ2EIZ4S.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cron/registerJob.ts"],"sourcesContent":["import { supabase } from '../core/supabase';\r\nimport { env } from '../core/config';\r\n\r\ninterface RegisterJobOptions {\r\n httpMethod?: string;\r\n headers?: Record<string, string>;\r\n body?: Record<string, unknown>;\r\n timezone?: string;\r\n timeoutSeconds?: number;\r\n}\r\n\r\ninterface RegisterJobResult {\r\n success: boolean;\r\n job?: Record<string, unknown>;\r\n error?: string;\r\n}\r\n\r\nexport async function registerCronJob(\r\n name: string,\r\n cronExpression: string,\r\n endpointUrl: string,\r\n options: RegisterJobOptions = {},\r\n): Promise<RegisterJobResult> {\r\n const apiUrl = env.EZCODER_API_URL;\r\n const projectId = env.EZC_PROJECT_ID;\r\n\r\n if (!apiUrl || !projectId) {\r\n return { success: false, error: 'EZCODER_API_URL and EZC_PROJECT_ID are required' };\r\n }\r\n\r\n const session = await supabase.auth.getSession();\r\n const token = session.data.session?.access_token;\r\n\r\n if (!token) {\r\n return { success: false, error: 'Authentication required' };\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiUrl}/api/project-cron/register`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${token}`,\r\n },\r\n body: JSON.stringify({\r\n projectId,\r\n name,\r\n cronExpression,\r\n endpointUrl,\r\n httpMethod: options.httpMethod || 'POST',\r\n headers: options.headers,\r\n body: options.body,\r\n timezone: options.timezone,\r\n timeoutSeconds: options.timeoutSeconds,\r\n }),\r\n });\r\n\r\n const data = await res.json();\r\n\r\n if (!res.ok) {\r\n return { success: false, error: data.error || `HTTP ${res.status}` };\r\n }\r\n\r\n return { success: true, job: data.job };\r\n } catch (err) {\r\n return { success: false, error: err instanceof Error ? err.message : 'Network error' };\r\n }\r\n}\r\n"],"mappings":";;;;;;AAiBA,eAAsB,gBACpB,MACA,gBACA,aACA,UAA8B,CAAC,GACH;AAC5B,QAAM,SAAS,IAAI;AACnB,QAAM,YAAY,IAAI;AAEtB,MAAI,CAAC,UAAU,CAAC,WAAW;AACzB,WAAO,EAAE,SAAS,OAAO,OAAO,kDAAkD;AAAA,EACpF;AAEA,QAAM,UAAU,MAAM,SAAS,KAAK,WAAW;AAC/C,QAAM,QAAQ,QAAQ,KAAK,SAAS;AAEpC,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,SAAS,OAAO,OAAO,0BAA0B;AAAA,EAC5D;AAEA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,8BAA8B;AAAA,MAC7D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK;AAAA,MAChC;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY,QAAQ,cAAc;AAAA,QAClC,SAAS,QAAQ;AAAA,QACjB,MAAM,QAAQ;AAAA,QACd,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH,CAAC;AAED,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,CAAC,IAAI,IAAI;AACX,aAAO,EAAE,SAAS,OAAO,OAAO,KAAK,SAAS,QAAQ,IAAI,MAAM,GAAG;AAAA,IACrE;AAEA,WAAO,EAAE,SAAS,MAAM,KAAK,KAAK,IAAI;AAAA,EACxC,SAAS,KAAK;AACZ,WAAO,EAAE,SAAS,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,gBAAgB;AAAA,EACvF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/cron/registerJob.ts"],"sourcesContent":["/**\r\n * SDK registerCronJob (B1).\r\n *\r\n * Routes to /api/project-cron/register using the SERVER-class project token.\r\n *\r\n * BEFORE B1, this function read the Supabase auth session and sent the\r\n * resulting JWT as `Authorization: Bearer`. The server endpoint expected\r\n * a NextAuth session — they never matched, so every register returned 401.\r\n *\r\n * AFTER B1:\r\n * - Reads EZC_PROJECT_TOKEN_SERVER from server-side env.\r\n * - Sends via `X-EzCoder-Server-Token` header.\r\n * - Refuses to run from browser bundles with a clear, actionable error.\r\n * - The server endpoint now uses verifyProjectServerToken (project-scoped\r\n * auth) instead of NextAuth getServerSession (user-scoped auth).\r\n */\r\n\r\nimport { env } from '../core/config';\r\nimport { resolveServerToken } from '../core/projectToken';\r\n\r\ninterface RegisterJobOptions {\r\n httpMethod?: string;\r\n headers?: Record<string, string>;\r\n body?: Record<string, unknown>;\r\n timezone?: string;\r\n timeoutSeconds?: number;\r\n}\r\n\r\ninterface RegisterJobResult {\r\n success: boolean;\r\n job?: Record<string, unknown>;\r\n error?: string;\r\n code?: string;\r\n}\r\n\r\nexport async function registerCronJob(\r\n name: string,\r\n cronExpression: string,\r\n endpointUrl: string,\r\n options: RegisterJobOptions = {},\r\n): Promise<RegisterJobResult> {\r\n const apiUrl = env.EZCODER_API_URL;\r\n const projectId = env.EZC_PROJECT_ID;\r\n\r\n if (!apiUrl || !projectId) {\r\n return {\r\n success: false,\r\n error: 'EZCODER_API_URL and EZC_PROJECT_ID are required',\r\n code: 'sdk_misconfigured',\r\n };\r\n }\r\n\r\n const tokenResolution = resolveServerToken();\r\n if (!tokenResolution.ok) {\r\n return { success: false, error: tokenResolution.reason, code: 'no_server_token' };\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiUrl}/api/project-cron/register`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-EzCoder-Server-Token': tokenResolution.token,\r\n },\r\n body: JSON.stringify({\r\n projectId,\r\n name,\r\n cronExpression,\r\n endpointUrl,\r\n httpMethod: options.httpMethod || 'POST',\r\n headers: options.headers,\r\n body: options.body,\r\n timezone: options.timezone,\r\n timeoutSeconds: options.timeoutSeconds,\r\n }),\r\n });\r\n\r\n const raw: unknown = await res.json().catch(() => ({}));\r\n const data = (raw && typeof raw === 'object' ? raw : {}) as {\r\n job?: Record<string, unknown>;\r\n error?: string;\r\n code?: string;\r\n };\r\n\r\n if (!res.ok) {\r\n return {\r\n success: false,\r\n error: data.error ?? `HTTP ${res.status}`,\r\n code: data.code,\r\n };\r\n }\r\n\r\n return { success: true, job: data.job };\r\n } catch (err: unknown) {\r\n return {\r\n success: false,\r\n error: err instanceof Error ? err.message : 'Network error',\r\n code: 'network_error',\r\n };\r\n }\r\n}\r\n"],"mappings":";;;;;;;;AAmCA,eAAsB,gBACpB,MACA,gBACA,aACA,UAA8B,CAAC,GACH;AAC5B,QAAM,SAAS,IAAI;AACnB,QAAM,YAAY,IAAI;AAEtB,MAAI,CAAC,UAAU,CAAC,WAAW;AACzB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,kBAAkB,mBAAmB;AAC3C,MAAI,CAAC,gBAAgB,IAAI;AACvB,WAAO,EAAE,SAAS,OAAO,OAAO,gBAAgB,QAAQ,MAAM,kBAAkB;AAAA,EAClF;AAEA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,8BAA8B;AAAA,MAC7D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,0BAA0B,gBAAgB;AAAA,MAC5C;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY,QAAQ,cAAc;AAAA,QAClC,SAAS,QAAQ;AAAA,QACjB,MAAM,QAAQ;AAAA,QACd,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH,CAAC;AAED,UAAM,MAAe,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACtD,UAAM,OAAQ,OAAO,OAAO,QAAQ,WAAW,MAAM,CAAC;AAMtD,QAAI,CAAC,IAAI,IAAI;AACX,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK,SAAS,QAAQ,IAAI,MAAM;AAAA,QACvC,MAAM,KAAK;AAAA,MACb;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,MAAM,KAAK,KAAK,IAAI;AAAA,EACxC,SAAS,KAAc;AACrB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,eAAe,QAAQ,IAAI,UAAU;AAAA,MAC5C,MAAM;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
@@ -11,9 +11,10 @@ import {
11
11
  useDatabaseOptional,
12
12
  useIsDatabaseConfigured,
13
13
  useRealtime
14
- } from "../chunk-7VGYFCQC.js";
15
- import "../chunk-GPF4AYNG.js";
16
- import "../chunk-2WG4O4J2.js";
14
+ } from "../chunk-R4MDAYFO.js";
15
+ import "../chunk-HJ2EIZ4S.js";
16
+ import "../chunk-I2YGB7Z6.js";
17
+ import "../chunk-LIUE7M7K.js";
17
18
  export {
18
19
  ConnectionError,
19
20
  DatabaseClient,
@@ -1,10 +1,28 @@
1
+ /**
2
+ * SDK sendEmail (B1).
3
+ *
4
+ * Routes to /api/project-email/send using the SERVER-class project token.
5
+ *
6
+ * BEFORE B1, this function read the Supabase auth session and sent the
7
+ * resulting JWT as `Authorization: Bearer`. The server endpoint expected
8
+ * the project token (ezc_) — they never matched, so every send returned 401.
9
+ *
10
+ * AFTER B1:
11
+ * - Reads EZC_PROJECT_TOKEN_SERVER from server-side env.
12
+ * - Sends via `X-EzCoder-Server-Token` header.
13
+ * - Refuses to run from browser bundles with a clear, actionable error.
14
+ * - Falls back to the legacy single-class token through Phase 1, with a
15
+ * one-shot deprecation warning.
16
+ */
1
17
  interface SendEmailOptions {
2
18
  from?: string;
19
+ text?: string;
3
20
  }
4
21
  interface SendEmailResult {
5
22
  success: boolean;
6
23
  id?: string;
7
24
  error?: string;
25
+ code?: string;
8
26
  }
9
27
  declare function sendEmail(to: string, subject: string, html: string, options?: SendEmailOptions): Promise<SendEmailResult>;
10
28
 
@@ -1,42 +1,57 @@
1
1
  import {
2
- env,
3
- supabase
4
- } from "../chunk-2WG4O4J2.js";
2
+ resolveServerToken
3
+ } from "../chunk-CQKYANAW.js";
4
+ import {
5
+ env
6
+ } from "../chunk-LIUE7M7K.js";
5
7
 
6
8
  // src/email/sendEmail.ts
7
9
  async function sendEmail(to, subject, html, options = {}) {
8
10
  const apiUrl = env.EZCODER_API_URL;
9
11
  const projectId = env.EZC_PROJECT_ID;
10
12
  if (!apiUrl || !projectId) {
11
- return { success: false, error: "EZCODER_API_URL and EZC_PROJECT_ID are required" };
13
+ return {
14
+ success: false,
15
+ error: "EZCODER_API_URL and EZC_PROJECT_ID are required",
16
+ code: "sdk_misconfigured"
17
+ };
12
18
  }
13
- const session = await supabase.auth.getSession();
14
- const token = session.data.session?.access_token;
15
- if (!token) {
16
- return { success: false, error: "Authentication required" };
19
+ const tokenResolution = resolveServerToken();
20
+ if (!tokenResolution.ok) {
21
+ return { success: false, error: tokenResolution.reason, code: "no_server_token" };
17
22
  }
18
23
  try {
19
24
  const res = await fetch(`${apiUrl}/api/project-email/send`, {
20
25
  method: "POST",
21
26
  headers: {
22
27
  "Content-Type": "application/json",
23
- Authorization: `Bearer ${token}`
28
+ "X-EzCoder-Server-Token": tokenResolution.token
24
29
  },
25
30
  body: JSON.stringify({
26
31
  projectId,
27
32
  to,
28
33
  subject,
29
34
  html,
35
+ text: options.text,
30
36
  from: options.from
31
37
  })
32
38
  });
33
- const data = await res.json();
39
+ const raw = await res.json().catch(() => ({}));
40
+ const data = raw && typeof raw === "object" ? raw : {};
34
41
  if (!res.ok) {
35
- return { success: false, error: data.error || `HTTP ${res.status}` };
42
+ return {
43
+ success: false,
44
+ error: data.error ?? `HTTP ${res.status}`,
45
+ code: data.code
46
+ };
36
47
  }
37
48
  return { success: true, id: data.id };
38
49
  } catch (err) {
39
- return { success: false, error: err instanceof Error ? err.message : "Network error" };
50
+ return {
51
+ success: false,
52
+ error: err instanceof Error ? err.message : "Network error",
53
+ code: "network_error"
54
+ };
40
55
  }
41
56
  }
42
57
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/email/sendEmail.ts"],"sourcesContent":["import { supabase } from '../core/supabase';\r\nimport { env } from '../core/config';\r\n\r\ninterface SendEmailOptions {\r\n from?: string;\r\n}\r\n\r\ninterface SendEmailResult {\r\n success: boolean;\r\n id?: string;\r\n error?: string;\r\n}\r\n\r\nexport async function sendEmail(\r\n to: string,\r\n subject: string,\r\n html: string,\r\n options: SendEmailOptions = {},\r\n): Promise<SendEmailResult> {\r\n const apiUrl = env.EZCODER_API_URL;\r\n const projectId = env.EZC_PROJECT_ID;\r\n\r\n if (!apiUrl || !projectId) {\r\n return { success: false, error: 'EZCODER_API_URL and EZC_PROJECT_ID are required' };\r\n }\r\n\r\n const session = await supabase.auth.getSession();\r\n const token = session.data.session?.access_token;\r\n\r\n if (!token) {\r\n return { success: false, error: 'Authentication required' };\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiUrl}/api/project-email/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${token}`,\r\n },\r\n body: JSON.stringify({\r\n projectId,\r\n to,\r\n subject,\r\n html,\r\n from: options.from,\r\n }),\r\n });\r\n\r\n const data = await res.json();\r\n\r\n if (!res.ok) {\r\n return { success: false, error: data.error || `HTTP ${res.status}` };\r\n }\r\n\r\n return { success: true, id: data.id };\r\n } catch (err) {\r\n return { success: false, error: err instanceof Error ? err.message : 'Network error' };\r\n }\r\n}\r\n"],"mappings":";;;;;;AAaA,eAAsB,UACpB,IACA,SACA,MACA,UAA4B,CAAC,GACH;AAC1B,QAAM,SAAS,IAAI;AACnB,QAAM,YAAY,IAAI;AAEtB,MAAI,CAAC,UAAU,CAAC,WAAW;AACzB,WAAO,EAAE,SAAS,OAAO,OAAO,kDAAkD;AAAA,EACpF;AAEA,QAAM,UAAU,MAAM,SAAS,KAAK,WAAW;AAC/C,QAAM,QAAQ,QAAQ,KAAK,SAAS;AAEpC,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,SAAS,OAAO,OAAO,0BAA0B;AAAA,EAC5D;AAEA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,2BAA2B;AAAA,MAC1D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK;AAAA,MAChC;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,QAAQ;AAAA,MAChB,CAAC;AAAA,IACH,CAAC;AAED,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,CAAC,IAAI,IAAI;AACX,aAAO,EAAE,SAAS,OAAO,OAAO,KAAK,SAAS,QAAQ,IAAI,MAAM,GAAG;AAAA,IACrE;AAEA,WAAO,EAAE,SAAS,MAAM,IAAI,KAAK,GAAG;AAAA,EACtC,SAAS,KAAK;AACZ,WAAO,EAAE,SAAS,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,gBAAgB;AAAA,EACvF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/email/sendEmail.ts"],"sourcesContent":["/**\r\n * SDK sendEmail (B1).\r\n *\r\n * Routes to /api/project-email/send using the SERVER-class project token.\r\n *\r\n * BEFORE B1, this function read the Supabase auth session and sent the\r\n * resulting JWT as `Authorization: Bearer`. The server endpoint expected\r\n * the project token (ezc_) — they never matched, so every send returned 401.\r\n *\r\n * AFTER B1:\r\n * - Reads EZC_PROJECT_TOKEN_SERVER from server-side env.\r\n * - Sends via `X-EzCoder-Server-Token` header.\r\n * - Refuses to run from browser bundles with a clear, actionable error.\r\n * - Falls back to the legacy single-class token through Phase 1, with a\r\n * one-shot deprecation warning.\r\n */\r\n\r\nimport { env } from '../core/config';\r\nimport { resolveServerToken } from '../core/projectToken';\r\n\r\ninterface SendEmailOptions {\r\n from?: string;\r\n text?: string;\r\n}\r\n\r\ninterface SendEmailResult {\r\n success: boolean;\r\n id?: string;\r\n error?: string;\r\n code?: string;\r\n}\r\n\r\nexport async function sendEmail(\r\n to: string,\r\n subject: string,\r\n html: string,\r\n options: SendEmailOptions = {},\r\n): Promise<SendEmailResult> {\r\n const apiUrl = env.EZCODER_API_URL;\r\n const projectId = env.EZC_PROJECT_ID;\r\n\r\n if (!apiUrl || !projectId) {\r\n return {\r\n success: false,\r\n error: 'EZCODER_API_URL and EZC_PROJECT_ID are required',\r\n code: 'sdk_misconfigured',\r\n };\r\n }\r\n\r\n const tokenResolution = resolveServerToken();\r\n if (!tokenResolution.ok) {\r\n return { success: false, error: tokenResolution.reason, code: 'no_server_token' };\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiUrl}/api/project-email/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-EzCoder-Server-Token': tokenResolution.token,\r\n },\r\n body: JSON.stringify({\r\n projectId,\r\n to,\r\n subject,\r\n html,\r\n text: options.text,\r\n from: options.from,\r\n }),\r\n });\r\n\r\n const raw: unknown = await res.json().catch(() => ({}));\r\n const data = (raw && typeof raw === 'object' ? raw : {}) as {\r\n id?: string;\r\n error?: string;\r\n code?: string;\r\n };\r\n\r\n if (!res.ok) {\r\n return {\r\n success: false,\r\n error: data.error ?? `HTTP ${res.status}`,\r\n code: data.code,\r\n };\r\n }\r\n\r\n return { success: true, id: data.id };\r\n } catch (err: unknown) {\r\n return {\r\n success: false,\r\n error: err instanceof Error ? err.message : 'Network error',\r\n code: 'network_error',\r\n };\r\n }\r\n}\r\n"],"mappings":";;;;;;;;AAgCA,eAAsB,UACpB,IACA,SACA,MACA,UAA4B,CAAC,GACH;AAC1B,QAAM,SAAS,IAAI;AACnB,QAAM,YAAY,IAAI;AAEtB,MAAI,CAAC,UAAU,CAAC,WAAW;AACzB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,kBAAkB,mBAAmB;AAC3C,MAAI,CAAC,gBAAgB,IAAI;AACvB,WAAO,EAAE,SAAS,OAAO,OAAO,gBAAgB,QAAQ,MAAM,kBAAkB;AAAA,EAClF;AAEA,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,2BAA2B;AAAA,MAC1D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,0BAA0B,gBAAgB;AAAA,MAC5C;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ;AAAA,MAChB,CAAC;AAAA,IACH,CAAC;AAED,UAAM,MAAe,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACtD,UAAM,OAAQ,OAAO,OAAO,QAAQ,WAAW,MAAM,CAAC;AAMtD,QAAI,CAAC,IAAI,IAAI;AACX,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK,SAAS,QAAQ,IAAI,MAAM;AAAA,QACvC,MAAM,KAAK;AAAA,MACb;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,MAAM,IAAI,KAAK,GAAG;AAAA,EACtC,SAAS,KAAc;AACrB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,eAAe,QAAQ,IAAI,UAAU;AAAA,MAC5C,MAAM;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -14,6 +14,9 @@ declare const env: {
14
14
  EZC_PROJECT_ID: string;
15
15
  EZCODER_API_URL: string;
16
16
  EZC_SECRET_KEY: string;
17
+ EZC_PROJECT_TOKEN_PUBLIC: string;
18
+ EZC_PROJECT_TOKEN_SERVER: string;
19
+ EZC_PROJECT_TOKEN_LEGACY: string;
17
20
  S3_BUCKET: string;
18
21
  CLOUDINARY_URL: string;
19
22
  STORAGE_BUCKET_PUBLIC: string;
@@ -34,4 +37,167 @@ declare const isSupabaseConfigured: boolean;
34
37
  declare const ezcoder: EzcoderClient;
35
38
  declare const ezcoderAuthIntegration: AuthIntegration;
36
39
 
37
- export { AuthIntegration, EzcoderClient, env, ezcoder, ezcoderAuthIntegration, features, isFeatureConfigured, isSupabaseConfigured, supabase };
40
+ /**
41
+ * @module manifest-consumer
42
+ * @description Runtime + build-time consumer of the EzCoder Capability Manifest.
43
+ *
44
+ * Track B (B7) — the user-app SDK reads the project's Capability Manifest so
45
+ * it knows which feature modules are actually required (auth, storage,
46
+ * payments, email, cron, notifications, database) and which env refs to
47
+ * expect. The SDK historically auto-registered everything regardless of
48
+ * project needs; this consumer adds *opt-in* awareness without breaking
49
+ * existing consumers.
50
+ *
51
+ * Failure mode is deliberately permissive: any fetch/parse error falls
52
+ * through to "all modules considered required" so a flaky network blip on
53
+ * the user's deployed site never silently disables a feature.
54
+ */
55
+ /**
56
+ * The seven modules the SDK can register. Mirrors the keys of
57
+ * `service_requirements` in lib/manifest/schema.ts plus `database` derived
58
+ * from the `database` requirement block.
59
+ */
60
+ interface RequiredModules {
61
+ auth: boolean;
62
+ storage: boolean;
63
+ payments: boolean;
64
+ email: boolean;
65
+ cron: boolean;
66
+ database: boolean;
67
+ notifications: boolean;
68
+ }
69
+ /**
70
+ * Public + (publicly-visible names of) secret env refs. The server-side
71
+ * endpoint deliberately strips `secret` before returning, but we keep the
72
+ * field on the consumer type as an empty array for forward-compat — once a
73
+ * privileged consumer path exists it can populate it.
74
+ */
75
+ interface EnvRefs {
76
+ public: ReadonlyArray<string>;
77
+ secret: ReadonlyArray<string>;
78
+ }
79
+ /**
80
+ * The shape the SDK actually consumes. A trimmed projection of the full
81
+ * Capability Manifest; the server endpoint enforces the projection so the
82
+ * SDK can never see secret-ref names even if it asks for them.
83
+ */
84
+ interface SanitizedManifest {
85
+ manifest_version: number;
86
+ project_id: string;
87
+ service_requirements: {
88
+ database?: {
89
+ provider: string;
90
+ rls_required: boolean;
91
+ tables: ReadonlyArray<string>;
92
+ };
93
+ auth?: {
94
+ provider: string;
95
+ flows: ReadonlyArray<string>;
96
+ };
97
+ storage?: {
98
+ buckets: ReadonlyArray<string>;
99
+ };
100
+ payments?: {
101
+ mode: string;
102
+ };
103
+ email?: {
104
+ provider: string;
105
+ };
106
+ cron?: {
107
+ jobs: ReadonlyArray<{
108
+ name: string;
109
+ schedule: string;
110
+ }>;
111
+ };
112
+ };
113
+ env_refs: {
114
+ public: ReadonlyArray<string>;
115
+ };
116
+ validation_gates: ReadonlyArray<string>;
117
+ }
118
+ /**
119
+ * Wraps a sanitized manifest payload and exposes module/env-ref queries.
120
+ * Instances are immutable — `manifest` is captured at construction time and
121
+ * never mutated. Use the static `load()` / `loadFromBuildOutput()` factories
122
+ * rather than calling `new` directly so the page-lifetime cache is honoured.
123
+ */
124
+ declare class EzCoderManifest {
125
+ private readonly manifest;
126
+ private readonly source;
127
+ constructor(manifest: SanitizedManifest | null, source: 'runtime' | 'build' | 'fallback');
128
+ /**
129
+ * Fetch the manifest at runtime from the platform API. Returns a
130
+ * cached instance for the lifetime of the page; concurrent callers share
131
+ * a single in-flight fetch. Falls back to a permissive (all-modules-on)
132
+ * instance on any error.
133
+ */
134
+ static load(): Promise<EzCoderManifest>;
135
+ /**
136
+ * Read a manifest written by the build-time codegen step (see
137
+ * scripts/fetch-build-manifest.js). Used by SSG callers that want to
138
+ * avoid a runtime network round-trip. Returns null when no build-time
139
+ * manifest is present — callers should fall back to `load()`.
140
+ *
141
+ * Implementation note: we deliberately avoid a static `import` because
142
+ * (a) the file may not exist, which would be a hard module-resolution
143
+ * error, and (b) browser bundles must not pull in the JSON. We probe
144
+ * `process` to detect Node, then read the file from disk via the eval'd
145
+ * dynamic require — the eval indirection keeps bundlers from following
146
+ * the path at build time.
147
+ */
148
+ static loadFromBuildOutput(): EzCoderManifest | null;
149
+ /**
150
+ * Returns the boolean enablement map for each SDK module. When no
151
+ * manifest is loaded, returns the permissive default (everything on) so
152
+ * legacy behaviour is preserved.
153
+ */
154
+ getRequiredModules(): RequiredModules;
155
+ /**
156
+ * Returns the env refs declared by the manifest. The `secret` array is
157
+ * always empty when the manifest came from the public SDK endpoint —
158
+ * server-only refs are stripped before transit (see pages/api/sdk/manifest.js).
159
+ */
160
+ getEnvRefs(): EnvRefs;
161
+ /**
162
+ * Convenience: is a given module required by this app?
163
+ */
164
+ has(module: keyof RequiredModules): boolean;
165
+ /**
166
+ * Where the manifest was loaded from. Useful for debugging and for
167
+ * surface-level telemetry (`fallback` means we never got the real one).
168
+ */
169
+ getSource(): 'runtime' | 'build' | 'fallback';
170
+ /**
171
+ * Test-only helper: wipe the page-lifetime cache. Never call this in
172
+ * production code.
173
+ */
174
+ static __resetCacheForTests(): void;
175
+ }
176
+
177
+ /**
178
+ * @module useEzCoderManifest
179
+ * @description React hook that exposes the loaded Capability Manifest to
180
+ * components so they can branch on whether a feature module is required by
181
+ * the current project.
182
+ *
183
+ * Importing this file pulls `react` as a peer dep — kept in a dedicated
184
+ * file so SSR / non-React entries can consume `manifest-consumer.ts`
185
+ * without touching React.
186
+ */
187
+
188
+ interface UseEzCoderManifestResult {
189
+ manifest: EzCoderManifest | null;
190
+ modules: RequiredModules | null;
191
+ loading: boolean;
192
+ source: 'runtime' | 'build' | 'fallback' | null;
193
+ }
194
+ /**
195
+ * Synchronously prefers a build-time manifest (avoids a render-blocking
196
+ * fetch on SSG/SSR); otherwise kicks off the async runtime load. Until the
197
+ * load resolves, `modules` is null and callers should treat features as
198
+ * unknown (recommended: render the historical default, then re-render once
199
+ * `modules` settles).
200
+ */
201
+ declare function useEzCoderManifest(): UseEzCoderManifestResult;
202
+
203
+ export { AuthIntegration, type EnvRefs, EzCoderManifest, EzcoderClient, type RequiredModules, type SanitizedManifest, type UseEzCoderManifestResult, env, ezcoder, ezcoderAuthIntegration, features, isFeatureConfigured, isSupabaseConfigured, supabase, useEzCoderManifest };
package/dist/index.js CHANGED
@@ -2,21 +2,276 @@ import {
2
2
  DatabaseClient,
3
3
  DatabaseProvider,
4
4
  useDatabase
5
- } from "./chunk-7VGYFCQC.js";
5
+ } from "./chunk-R4MDAYFO.js";
6
6
  import {
7
7
  ezcoder,
8
8
  ezcoderAuthIntegration
9
- } from "./chunk-GPF4AYNG.js";
9
+ } from "./chunk-HJ2EIZ4S.js";
10
10
  import {
11
- env,
12
- features,
13
- isFeatureConfigured,
14
11
  isSupabaseConfigured,
15
12
  supabase
16
- } from "./chunk-2WG4O4J2.js";
13
+ } from "./chunk-I2YGB7Z6.js";
14
+ import {
15
+ env,
16
+ features,
17
+ isFeatureConfigured
18
+ } from "./chunk-LIUE7M7K.js";
19
+
20
+ // src/manifest-consumer.ts
21
+ var manifestCache = /* @__PURE__ */ new Map();
22
+ var inflightLoads = /* @__PURE__ */ new Map();
23
+ var PERMISSIVE_DEFAULT_MODULES = Object.freeze({
24
+ auth: true,
25
+ storage: true,
26
+ payments: true,
27
+ email: true,
28
+ cron: true,
29
+ database: true,
30
+ notifications: true
31
+ });
32
+ var EzCoderManifest = class _EzCoderManifest {
33
+ constructor(manifest, source) {
34
+ this.manifest = manifest;
35
+ this.source = source;
36
+ }
37
+ /**
38
+ * Fetch the manifest at runtime from the platform API. Returns a
39
+ * cached instance for the lifetime of the page; concurrent callers share
40
+ * a single in-flight fetch. Falls back to a permissive (all-modules-on)
41
+ * instance on any error.
42
+ */
43
+ static async load() {
44
+ const projectId = env.EZC_PROJECT_ID;
45
+ const apiUrl = env.EZCODER_API_URL;
46
+ const publicToken = readPublicToken();
47
+ if (!projectId || !apiUrl) {
48
+ return new _EzCoderManifest(null, "fallback");
49
+ }
50
+ const cacheKey = `${projectId}::${apiUrl}`;
51
+ const cached = manifestCache.get(cacheKey);
52
+ if (cached) return cached;
53
+ const inflight = inflightLoads.get(cacheKey);
54
+ if (inflight) return inflight;
55
+ const loadPromise = fetchManifest(apiUrl, publicToken).then((m) => {
56
+ const instance = new _EzCoderManifest(m, "runtime");
57
+ manifestCache.set(cacheKey, instance);
58
+ return instance;
59
+ }).catch((err) => {
60
+ const message = err instanceof Error ? err.message : "unknown";
61
+ if (typeof console !== "undefined") {
62
+ console.warn(`[EzCoder SDK] manifest fetch failed (${message}); falling back to permissive mode.`);
63
+ }
64
+ const fallback = new _EzCoderManifest(null, "fallback");
65
+ manifestCache.set(cacheKey, fallback);
66
+ return fallback;
67
+ }).finally(() => {
68
+ inflightLoads.delete(cacheKey);
69
+ });
70
+ inflightLoads.set(cacheKey, loadPromise);
71
+ return loadPromise;
72
+ }
73
+ /**
74
+ * Read a manifest written by the build-time codegen step (see
75
+ * scripts/fetch-build-manifest.js). Used by SSG callers that want to
76
+ * avoid a runtime network round-trip. Returns null when no build-time
77
+ * manifest is present — callers should fall back to `load()`.
78
+ *
79
+ * Implementation note: we deliberately avoid a static `import` because
80
+ * (a) the file may not exist, which would be a hard module-resolution
81
+ * error, and (b) browser bundles must not pull in the JSON. We probe
82
+ * `process` to detect Node, then read the file from disk via the eval'd
83
+ * dynamic require — the eval indirection keeps bundlers from following
84
+ * the path at build time.
85
+ */
86
+ static loadFromBuildOutput() {
87
+ try {
88
+ if (typeof process === "undefined" || !process.versions?.node) {
89
+ return null;
90
+ }
91
+ const getRequire = new Function(
92
+ 'return typeof require === "function" ? require : null;'
93
+ );
94
+ const dynamicRequire = getRequire();
95
+ if (!dynamicRequire) return null;
96
+ const fs = dynamicRequire("fs");
97
+ const path = dynamicRequire("path");
98
+ const cwd = process.cwd();
99
+ const full = path.resolve(cwd, "node_modules", ".ezcoder", "manifest.json");
100
+ const raw = fs.readFileSync(full, "utf8");
101
+ const payload = JSON.parse(raw);
102
+ const parsed = parseManifestPayload(payload);
103
+ if (!parsed) return null;
104
+ return new _EzCoderManifest(parsed, "build");
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+ /**
110
+ * Returns the boolean enablement map for each SDK module. When no
111
+ * manifest is loaded, returns the permissive default (everything on) so
112
+ * legacy behaviour is preserved.
113
+ */
114
+ getRequiredModules() {
115
+ if (!this.manifest) {
116
+ return { ...PERMISSIVE_DEFAULT_MODULES };
117
+ }
118
+ const sr = this.manifest.service_requirements;
119
+ return {
120
+ auth: Boolean(sr.auth),
121
+ storage: Boolean(sr.storage),
122
+ payments: Boolean(sr.payments),
123
+ email: Boolean(sr.email),
124
+ cron: Boolean(sr.cron),
125
+ database: Boolean(sr.database),
126
+ // notifications is not in service_requirements v0; derive from email
127
+ // OR auth so the existing notifications module behaves sensibly.
128
+ notifications: Boolean(sr.email || sr.auth)
129
+ };
130
+ }
131
+ /**
132
+ * Returns the env refs declared by the manifest. The `secret` array is
133
+ * always empty when the manifest came from the public SDK endpoint —
134
+ * server-only refs are stripped before transit (see pages/api/sdk/manifest.js).
135
+ */
136
+ getEnvRefs() {
137
+ if (!this.manifest) {
138
+ return { public: [], secret: [] };
139
+ }
140
+ return {
141
+ public: this.manifest.env_refs.public,
142
+ secret: []
143
+ };
144
+ }
145
+ /**
146
+ * Convenience: is a given module required by this app?
147
+ */
148
+ has(module) {
149
+ return this.getRequiredModules()[module];
150
+ }
151
+ /**
152
+ * Where the manifest was loaded from. Useful for debugging and for
153
+ * surface-level telemetry (`fallback` means we never got the real one).
154
+ */
155
+ getSource() {
156
+ return this.source;
157
+ }
158
+ /**
159
+ * Test-only helper: wipe the page-lifetime cache. Never call this in
160
+ * production code.
161
+ */
162
+ static __resetCacheForTests() {
163
+ manifestCache.clear();
164
+ inflightLoads.clear();
165
+ }
166
+ };
167
+ function readPublicToken() {
168
+ const fromVite = safeEnv("VITE_EZC_PROJECT_TOKEN_PUBLIC");
169
+ if (fromVite) return fromVite;
170
+ const fromNext = safeEnv("NEXT_PUBLIC_EZC_PROJECT_TOKEN_PUBLIC");
171
+ if (fromNext) return fromNext;
172
+ return safeEnv("EZC_PROJECT_TOKEN_PUBLIC");
173
+ }
174
+ function safeEnv(key) {
175
+ try {
176
+ if (typeof import.meta !== "undefined" && import.meta.env) {
177
+ const v = import.meta.env[key];
178
+ if (typeof v === "string") return v;
179
+ }
180
+ } catch {
181
+ }
182
+ try {
183
+ if (typeof process !== "undefined" && process.env) {
184
+ return process.env[key] || "";
185
+ }
186
+ } catch {
187
+ }
188
+ return "";
189
+ }
190
+ async function fetchManifest(apiUrl, publicToken) {
191
+ const headers = {
192
+ "Accept": "application/json"
193
+ };
194
+ if (publicToken) {
195
+ headers["X-EzCoder-Public-Token"] = publicToken;
196
+ }
197
+ const res = await fetch(`${apiUrl}/api/sdk/manifest`, {
198
+ method: "GET",
199
+ headers,
200
+ // Browsers will fold the 5-minute cache header automatically; this hint
201
+ // is here for non-browser runtimes that respect it.
202
+ cache: "default"
203
+ });
204
+ if (!res.ok) {
205
+ throw new Error(`HTTP ${res.status}`);
206
+ }
207
+ const body = await res.json();
208
+ const parsed = parseManifestPayload(body);
209
+ if (!parsed) {
210
+ throw new Error("manifest payload failed shape validation");
211
+ }
212
+ return parsed;
213
+ }
214
+ function parseManifestPayload(input) {
215
+ if (!isRecord(input)) return null;
216
+ const projectId = input.project_id;
217
+ const serviceReqs = input.service_requirements;
218
+ const envRefs = input.env_refs;
219
+ const gates = input.validation_gates;
220
+ if (typeof projectId !== "string" || projectId.length === 0) return null;
221
+ if (!isRecord(serviceReqs)) return null;
222
+ if (!isRecord(envRefs) || !Array.isArray(envRefs.public)) return null;
223
+ if (!Array.isArray(gates)) return null;
224
+ const manifestVersion = typeof input.manifest_version === "number" ? input.manifest_version : 0;
225
+ return {
226
+ manifest_version: manifestVersion,
227
+ project_id: projectId,
228
+ service_requirements: serviceReqs,
229
+ env_refs: { public: envRefs.public.filter((v) => typeof v === "string") },
230
+ validation_gates: gates.filter((v) => typeof v === "string")
231
+ };
232
+ }
233
+ function isRecord(value) {
234
+ return typeof value === "object" && value !== null && !Array.isArray(value);
235
+ }
236
+
237
+ // src/useEzCoderManifest.ts
238
+ import { useEffect, useState } from "react";
239
+ function useEzCoderManifest() {
240
+ const [manifest, setManifest] = useState(
241
+ () => EzCoderManifest.loadFromBuildOutput()
242
+ );
243
+ const [loading, setLoading] = useState(manifest === null);
244
+ useEffect(() => {
245
+ if (manifest) return;
246
+ let cancelled = false;
247
+ EzCoderManifest.load().then((m) => {
248
+ if (cancelled) return;
249
+ setManifest(m);
250
+ setLoading(false);
251
+ }).catch(() => {
252
+ if (cancelled) return;
253
+ setLoading(false);
254
+ });
255
+ return () => {
256
+ cancelled = true;
257
+ };
258
+ }, []);
259
+ return {
260
+ manifest,
261
+ modules: manifest ? manifest.getRequiredModules() : null,
262
+ loading,
263
+ source: manifest ? manifest.getSource() : null
264
+ };
265
+ }
266
+
267
+ // src/index.ts
268
+ if (typeof window !== "undefined") {
269
+ void EzCoderManifest.load();
270
+ }
17
271
  export {
18
272
  DatabaseClient,
19
273
  DatabaseProvider,
274
+ EzCoderManifest,
20
275
  env,
21
276
  ezcoder,
22
277
  ezcoderAuthIntegration,
@@ -24,6 +279,7 @@ export {
24
279
  isFeatureConfigured,
25
280
  isSupabaseConfigured,
26
281
  supabase,
27
- useDatabase
282
+ useDatabase,
283
+ useEzCoderManifest
28
284
  };
29
285
  //# sourceMappingURL=index.js.map