@tambo-ai/react 0.42.0 → 0.43.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 (95) hide show
  1. package/dist/context-helpers/__tests__/context-helpers-provider.test.d.ts +15 -0
  2. package/dist/context-helpers/__tests__/context-helpers-provider.test.d.ts.map +1 -0
  3. package/dist/context-helpers/__tests__/context-helpers-provider.test.js +147 -0
  4. package/dist/context-helpers/__tests__/context-helpers-provider.test.js.map +1 -0
  5. package/dist/context-helpers/__tests__/context-helpers.test.js +30 -35
  6. package/dist/context-helpers/__tests__/context-helpers.test.js.map +1 -1
  7. package/dist/context-helpers/current-page-context-helper.d.ts +7 -0
  8. package/dist/context-helpers/current-page-context-helper.d.ts.map +1 -0
  9. package/dist/context-helpers/current-page-context-helper.js +24 -0
  10. package/dist/context-helpers/current-page-context-helper.js.map +1 -0
  11. package/dist/context-helpers/current-time-context-helper.d.ts +7 -0
  12. package/dist/context-helpers/current-time-context-helper.d.ts.map +1 -0
  13. package/dist/context-helpers/current-time-context-helper.js +21 -0
  14. package/dist/context-helpers/current-time-context-helper.js.map +1 -0
  15. package/dist/context-helpers/index.d.ts +2 -7
  16. package/dist/context-helpers/index.d.ts.map +1 -1
  17. package/dist/context-helpers/index.js +2 -20
  18. package/dist/context-helpers/index.js.map +1 -1
  19. package/dist/context-helpers/registry.d.ts +35 -0
  20. package/dist/context-helpers/registry.d.ts.map +1 -0
  21. package/dist/context-helpers/registry.js +64 -0
  22. package/dist/context-helpers/registry.js.map +1 -0
  23. package/dist/context-helpers/types.d.ts +6 -16
  24. package/dist/context-helpers/types.d.ts.map +1 -1
  25. package/dist/context-helpers/types.js.map +1 -1
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -4
  29. package/dist/index.js.map +1 -1
  30. package/dist/providers/__tests__/tambo-context-helpers-provider.test.js +278 -137
  31. package/dist/providers/__tests__/tambo-context-helpers-provider.test.js.map +1 -1
  32. package/dist/providers/__tests__/tambo-thread-provider.test.js +29 -7
  33. package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  34. package/dist/providers/tambo-context-helpers-provider.d.ts +24 -15
  35. package/dist/providers/tambo-context-helpers-provider.d.ts.map +1 -1
  36. package/dist/providers/tambo-context-helpers-provider.js +56 -39
  37. package/dist/providers/tambo-context-helpers-provider.js.map +1 -1
  38. package/dist/providers/tambo-interactable-provider.js +1 -1
  39. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  40. package/esm/context-helpers/__tests__/context-helpers-provider.test.d.ts +15 -0
  41. package/esm/context-helpers/__tests__/context-helpers-provider.test.d.ts.map +1 -0
  42. package/esm/context-helpers/__tests__/context-helpers-provider.test.js +142 -0
  43. package/esm/context-helpers/__tests__/context-helpers-provider.test.js.map +1 -0
  44. package/esm/context-helpers/__tests__/context-helpers.test.js +31 -36
  45. package/esm/context-helpers/__tests__/context-helpers.test.js.map +1 -1
  46. package/esm/context-helpers/current-page-context-helper.d.ts +7 -0
  47. package/esm/context-helpers/current-page-context-helper.d.ts.map +1 -0
  48. package/esm/context-helpers/current-page-context-helper.js +20 -0
  49. package/esm/context-helpers/current-page-context-helper.js.map +1 -0
  50. package/esm/context-helpers/current-time-context-helper.d.ts +7 -0
  51. package/esm/context-helpers/current-time-context-helper.d.ts.map +1 -0
  52. package/esm/context-helpers/current-time-context-helper.js +17 -0
  53. package/esm/context-helpers/current-time-context-helper.js.map +1 -0
  54. package/esm/context-helpers/index.d.ts +2 -7
  55. package/esm/context-helpers/index.d.ts.map +1 -1
  56. package/esm/context-helpers/index.js +2 -19
  57. package/esm/context-helpers/index.js.map +1 -1
  58. package/esm/context-helpers/registry.d.ts +35 -0
  59. package/esm/context-helpers/registry.d.ts.map +1 -0
  60. package/esm/context-helpers/registry.js +57 -0
  61. package/esm/context-helpers/registry.js.map +1 -0
  62. package/esm/context-helpers/types.d.ts +6 -16
  63. package/esm/context-helpers/types.d.ts.map +1 -1
  64. package/esm/context-helpers/types.js.map +1 -1
  65. package/esm/index.d.ts +2 -2
  66. package/esm/index.d.ts.map +1 -1
  67. package/esm/index.js +1 -1
  68. package/esm/index.js.map +1 -1
  69. package/esm/providers/__tests__/tambo-context-helpers-provider.test.js +279 -105
  70. package/esm/providers/__tests__/tambo-context-helpers-provider.test.js.map +1 -1
  71. package/esm/providers/__tests__/tambo-thread-provider.test.js +29 -7
  72. package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  73. package/esm/providers/tambo-context-helpers-provider.d.ts +24 -15
  74. package/esm/providers/tambo-context-helpers-provider.d.ts.map +1 -1
  75. package/esm/providers/tambo-context-helpers-provider.js +57 -40
  76. package/esm/providers/tambo-context-helpers-provider.js.map +1 -1
  77. package/esm/providers/tambo-interactable-provider.js +1 -1
  78. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  79. package/package.json +6 -6
  80. package/dist/context-helpers/user-page.d.ts +0 -7
  81. package/dist/context-helpers/user-page.d.ts.map +0 -1
  82. package/dist/context-helpers/user-page.js +0 -31
  83. package/dist/context-helpers/user-page.js.map +0 -1
  84. package/dist/context-helpers/user-time.d.ts +0 -7
  85. package/dist/context-helpers/user-time.d.ts.map +0 -1
  86. package/dist/context-helpers/user-time.js +0 -20
  87. package/dist/context-helpers/user-time.js.map +0 -1
  88. package/esm/context-helpers/user-page.d.ts +0 -7
  89. package/esm/context-helpers/user-page.d.ts.map +0 -1
  90. package/esm/context-helpers/user-page.js +0 -28
  91. package/esm/context-helpers/user-page.js.map +0 -1
  92. package/esm/context-helpers/user-time.d.ts +0 -7
  93. package/esm/context-helpers/user-time.d.ts.map +0 -1
  94. package/esm/context-helpers/user-time.js +0 -17
  95. package/esm/context-helpers/user-time.js.map +0 -1
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.getUserTimeContext = exports.getUserPageContext = exports.DEFAULT_CONTEXT_HELPERS = exports.useTamboInteractable = exports.withInteractable = exports.GenerationStage = exports.useTamboThreadList = exports.useTamboThread = exports.useTamboStream = exports.useTamboContextHelpers = exports.useTamboClient = exports.useTambo = exports.TamboThreadProvider = exports.TamboStubProvider = exports.TamboProvider = exports.TamboPropStreamProvider = exports.TamboContextHelpersProvider = exports.TamboComponentProvider = exports.TamboClientProvider = exports.useTamboThreadInput = exports.useTamboStreamStatus = exports.useTamboStreamingProps = exports.useTamboMessageContext = exports.useTamboCurrentMessage = exports.TamboMessageProvider = exports.useTamboComponentState = void 0;
18
+ exports.currentTimeContextHelper = exports.currentPageContextHelper = exports.useTamboInteractable = exports.withInteractable = exports.GenerationStage = exports.useTamboThreadList = exports.useTamboThread = exports.useTamboStream = exports.useTamboContextHelpers = exports.useTamboClient = exports.useTambo = exports.TamboThreadProvider = exports.TamboStubProvider = exports.TamboProvider = exports.TamboPropStreamProvider = exports.TamboContextHelpersProvider = exports.TamboComponentProvider = exports.TamboClientProvider = exports.useTamboThreadInput = exports.useTamboStreamStatus = exports.useTamboStreamingProps = exports.useTamboMessageContext = exports.useTamboCurrentMessage = exports.TamboMessageProvider = exports.useTamboComponentState = void 0;
19
19
  var use_component_state_1 = require("./hooks/use-component-state");
20
20
  Object.defineProperty(exports, "useTamboComponentState", { enumerable: true, get: function () { return use_component_state_1.useTamboComponentState; } });
21
21
  var use_current_message_1 = require("./hooks/use-current-message");
@@ -53,7 +53,6 @@ var tambo_interactable_provider_1 = require("./providers/tambo-interactable-prov
53
53
  Object.defineProperty(exports, "useTamboInteractable", { enumerable: true, get: function () { return tambo_interactable_provider_1.useTamboInteractable; } });
54
54
  // Context helpers exports
55
55
  var context_helpers_1 = require("./context-helpers");
56
- Object.defineProperty(exports, "DEFAULT_CONTEXT_HELPERS", { enumerable: true, get: function () { return context_helpers_1.DEFAULT_CONTEXT_HELPERS; } });
57
- Object.defineProperty(exports, "getUserPageContext", { enumerable: true, get: function () { return context_helpers_1.getUserPageContext; } });
58
- Object.defineProperty(exports, "getUserTimeContext", { enumerable: true, get: function () { return context_helpers_1.getUserTimeContext; } });
56
+ Object.defineProperty(exports, "currentPageContextHelper", { enumerable: true, get: function () { return context_helpers_1.currentPageContextHelper; } });
57
+ Object.defineProperty(exports, "currentTimeContextHelper", { enumerable: true, get: function () { return context_helpers_1.currentTimeContextHelper; } });
59
58
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,wKAAwK;;;;;;;;;;;;;;;;;AAExK,mEAAqE;AAA5D,6HAAA,sBAAsB,OAAA;AAC/B,mEAIqC;AAHnC,2HAAA,oBAAoB,OAAA;AACpB,6HAAA,sBAAsB,OAAA;AACtB,6HAAA,sBAAsB,OAAA;AAExB,mEAAqE;AAA5D,6HAAA,sBAAsB,OAAA;AAC/B,0DAAwC;AACxC,2EAIyC;AAHvC,+HAAA,oBAAoB,OAAA;AAItB,6DAA+D;AAAtD,uHAAA,mBAAmB,OAAA;AAE5B,gCAAgC;AAChC,yCAkBqB;AAjBnB,gHAAA,mBAAmB,OAAA;AACnB,mHAAA,sBAAsB,OAAA;AACtB,wHAAA,2BAA2B,OAAA;AAC3B,oHAAA,uBAAuB,OAAA;AACvB,0GAAA,aAAa,OAAA;AACb,8GAAA,iBAAiB,OAAA;AACjB,gHAAA,mBAAmB,OAAA;AACnB,qGAAA,QAAQ,OAAA;AACR,2GAAA,cAAc,OAAA;AACd,mHAAA,sBAAsB,OAAA;AACtB,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AAoBhB,+DAA+D;AAAtD,uHAAA,kBAAkB,OAAA;AAQ3B,mFAG6C;AAF3C,8HAAA,eAAe,OAAA;AASjB,mFAIiD;AAH/C,2HAAA,qBAAqB,OAAoB;AAI3C,uFAA+E;AAAtE,mIAAA,oBAAoB,OAAA;AAE7B,0BAA0B;AAC1B,qDAI2B;AAHzB,0HAAA,uBAAuB,OAAA;AACvB,qHAAA,kBAAkB,OAAA;AAClB,qHAAA,kBAAkB,OAAA","sourcesContent":["/** Exports for the library. Only publically available exports are re-exported here. Anything not exported here is not supported and may change or break at any time. */\n\nexport { useTamboComponentState } from \"./hooks/use-component-state\";\nexport {\n TamboMessageProvider,\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./hooks/use-current-message\";\nexport { useTamboStreamingProps } from \"./hooks/use-streaming-props\";\nexport * from \"./hooks/use-suggestions\";\nexport {\n useTamboStreamStatus,\n type PropStatus,\n type StreamStatus,\n} from \"./hooks/use-tambo-stream-status\";\nexport { useTamboThreadInput } from \"./hooks/use-thread-input\";\n\n// Re-export provider components\nexport {\n TamboClientProvider,\n TamboComponentProvider,\n TamboContextHelpersProvider,\n TamboPropStreamProvider,\n TamboProvider,\n TamboStubProvider,\n TamboThreadProvider,\n useTambo,\n useTamboClient,\n useTamboContextHelpers,\n useTamboStream,\n useTamboThread,\n type TamboComponent,\n type TamboContextHelpersContextProps,\n type TamboContextHelpersProviderProps,\n type TamboRegistryContext,\n type TamboStubProviderProps,\n} from \"./providers\";\n\n// Re-export types from Tambo Node SDK\nexport type {\n APIError,\n RateLimitError,\n TamboAIError,\n} from \"@tambo-ai/typescript-sdk\";\nexport type {\n Suggestion,\n SuggestionGenerateParams,\n SuggestionGenerateResponse,\n SuggestionListResponse,\n} from \"@tambo-ai/typescript-sdk/resources/beta/threads/suggestions\";\nexport { useTamboThreadList } from \"./hooks/use-tambo-threads\";\nexport {\n type ComponentContextToolMetadata,\n type ComponentRegistry,\n type ParameterSpec,\n type RegisteredComponent,\n type TamboTool,\n} from \"./model/component-metadata\";\nexport {\n GenerationStage,\n type TamboThreadMessage,\n} from \"./model/generate-component-response\";\nexport { type TamboThread } from \"./model/tambo-thread\";\n\nexport type {\n TamboInteractableComponent as InteractableComponent,\n TamboInteractableContext,\n} from \"./model/tambo-interactable\";\nexport {\n withTamboInteractable as withInteractable,\n type InteractableConfig,\n type WithTamboInteractableProps,\n} from \"./providers/hoc/with-tambo-interactable\";\nexport { useTamboInteractable } from \"./providers/tambo-interactable-provider\";\n\n// Context helpers exports\nexport {\n DEFAULT_CONTEXT_HELPERS,\n getUserPageContext,\n getUserTimeContext,\n} from \"./context-helpers\";\nexport type {\n AdditionalContext,\n AdditionalContextHelper,\n ContextHelpersConfig,\n} from \"./context-helpers\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,wKAAwK;;;;;;;;;;;;;;;;;AAExK,mEAAqE;AAA5D,6HAAA,sBAAsB,OAAA;AAC/B,mEAIqC;AAHnC,2HAAA,oBAAoB,OAAA;AACpB,6HAAA,sBAAsB,OAAA;AACtB,6HAAA,sBAAsB,OAAA;AAExB,mEAAqE;AAA5D,6HAAA,sBAAsB,OAAA;AAC/B,0DAAwC;AACxC,2EAIyC;AAHvC,+HAAA,oBAAoB,OAAA;AAItB,6DAA+D;AAAtD,uHAAA,mBAAmB,OAAA;AAE5B,gCAAgC;AAChC,yCAkBqB;AAjBnB,gHAAA,mBAAmB,OAAA;AACnB,mHAAA,sBAAsB,OAAA;AACtB,wHAAA,2BAA2B,OAAA;AAC3B,oHAAA,uBAAuB,OAAA;AACvB,0GAAA,aAAa,OAAA;AACb,8GAAA,iBAAiB,OAAA;AACjB,gHAAA,mBAAmB,OAAA;AACnB,qGAAA,QAAQ,OAAA;AACR,2GAAA,cAAc,OAAA;AACd,mHAAA,sBAAsB,OAAA;AACtB,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AAoBhB,+DAA+D;AAAtD,uHAAA,kBAAkB,OAAA;AAQ3B,mFAG6C;AAF3C,8HAAA,eAAe,OAAA;AASjB,mFAIiD;AAH/C,2HAAA,qBAAqB,OAAoB;AAI3C,uFAA+E;AAAtE,mIAAA,oBAAoB,OAAA;AAE7B,0BAA0B;AAC1B,qDAG2B;AAFzB,2HAAA,wBAAwB,OAAA;AACxB,2HAAA,wBAAwB,OAAA","sourcesContent":["/** Exports for the library. Only publically available exports are re-exported here. Anything not exported here is not supported and may change or break at any time. */\n\nexport { useTamboComponentState } from \"./hooks/use-component-state\";\nexport {\n TamboMessageProvider,\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./hooks/use-current-message\";\nexport { useTamboStreamingProps } from \"./hooks/use-streaming-props\";\nexport * from \"./hooks/use-suggestions\";\nexport {\n useTamboStreamStatus,\n type PropStatus,\n type StreamStatus,\n} from \"./hooks/use-tambo-stream-status\";\nexport { useTamboThreadInput } from \"./hooks/use-thread-input\";\n\n// Re-export provider components\nexport {\n TamboClientProvider,\n TamboComponentProvider,\n TamboContextHelpersProvider,\n TamboPropStreamProvider,\n TamboProvider,\n TamboStubProvider,\n TamboThreadProvider,\n useTambo,\n useTamboClient,\n useTamboContextHelpers,\n useTamboStream,\n useTamboThread,\n type TamboComponent,\n type TamboContextHelpersContextProps,\n type TamboContextHelpersProviderProps,\n type TamboRegistryContext,\n type TamboStubProviderProps,\n} from \"./providers\";\n\n// Re-export types from Tambo Node SDK\nexport type {\n APIError,\n RateLimitError,\n TamboAIError,\n} from \"@tambo-ai/typescript-sdk\";\nexport type {\n Suggestion,\n SuggestionGenerateParams,\n SuggestionGenerateResponse,\n SuggestionListResponse,\n} from \"@tambo-ai/typescript-sdk/resources/beta/threads/suggestions\";\nexport { useTamboThreadList } from \"./hooks/use-tambo-threads\";\nexport {\n type ComponentContextToolMetadata,\n type ComponentRegistry,\n type ParameterSpec,\n type RegisteredComponent,\n type TamboTool,\n} from \"./model/component-metadata\";\nexport {\n GenerationStage,\n type TamboThreadMessage,\n} from \"./model/generate-component-response\";\nexport { type TamboThread } from \"./model/tambo-thread\";\n\nexport type {\n TamboInteractableComponent as InteractableComponent,\n TamboInteractableContext,\n} from \"./model/tambo-interactable\";\nexport {\n withTamboInteractable as withInteractable,\n type InteractableConfig,\n type WithTamboInteractableProps,\n} from \"./providers/hoc/with-tambo-interactable\";\nexport { useTamboInteractable } from \"./providers/tambo-interactable-provider\";\n\n// Context helpers exports\nexport {\n currentPageContextHelper,\n currentTimeContextHelper,\n} from \"./context-helpers\";\nexport type {\n AdditionalContext,\n ContextHelperFn,\n ContextHelpers,\n} from \"./context-helpers\";\n"]}
@@ -1,194 +1,335 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  const react_1 = require("@testing-library/react");
40
7
  const react_2 = __importDefault(require("react"));
41
- const contextHelpers = __importStar(require("../../context-helpers"));
42
8
  const context_helpers_1 = require("../../context-helpers");
9
+ const registry_1 = require("../../context-helpers/registry");
43
10
  const tambo_context_helpers_provider_1 = require("../tambo-context-helpers-provider");
11
+ /**
12
+ * Test suite for TamboContextHelpersProvider (simplified API, registry-backed)
13
+ *
14
+ * The simplified API:
15
+ * - Accepts a plain map of { key: () => any | null | undefined | Promise<any | null | undefined> }.
16
+ * - The key becomes the context name sent to the model.
17
+ * - Returning null/undefined from a helper skips inclusion.
18
+ * - Prebuilt helpers are just functions (e.g., prebuiltUserTime, prebuiltUserPage).
19
+ *
20
+ * The hook is now registry-backed and safe to call outside a provider. When used
21
+ * outside a provider, it proxies to a global registry and still works.
22
+ */
44
23
  describe("TamboContextHelpersProvider", () => {
45
- // Create a wrapper component that provides the context for testing hooks
24
+ // Ensure registry is clean for each test to avoid cross-test contamination
25
+ beforeEach(() => {
26
+ (0, registry_1.setHelpers)({});
27
+ jest.clearAllMocks();
28
+ });
29
+ // Base wrapper with no helpers provided
46
30
  const wrapper = ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, null, children));
47
31
  describe("useTamboContextHelpers", () => {
48
32
  /**
49
- * Test: Hook throws error when used outside of provider
50
- * This ensures developers get a clear error message if they forget to wrap
51
- * their components with the TamboContextHelpersProvider
33
+ * NOTE: The hook is registry-backed and safe outside provider. It should not throw.
34
+ * This replaces the previous behavior that threw without a provider.
52
35
  */
53
- it("should throw error when used outside provider", () => {
54
- // Mock console.error to prevent error output in test logs
36
+ it("should be safe outside provider (registry-backed no provider)", async () => {
55
37
  const consoleSpy = jest
56
38
  .spyOn(console, "error")
57
39
  .mockImplementation(() => { });
58
- // Verify that using the hook without a provider throws the expected error
59
- expect(() => {
60
- (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)());
61
- }).toThrow("useTamboContextHelpers must be used within a TamboContextHelpersProvider");
62
- // Restore console.error to its original implementation
40
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)());
41
+ // Should return callable functions
42
+ expect(typeof result.current.getAdditionalContext).toBe("function");
43
+ expect(typeof result.current.getContextHelpers).toBe("function");
44
+ expect(typeof result.current.addContextHelper).toBe("function");
45
+ expect(typeof result.current.removeContextHelper).toBe("function");
46
+ // Starts empty
47
+ expect(await result.current.getAdditionalContext()).toHaveLength(0);
48
+ // Add a helper and verify
49
+ (0, react_1.act)(() => {
50
+ result.current.addContextHelper("outsideHelper", () => ({ ok: true }));
51
+ });
52
+ const contexts = await result.current.getAdditionalContext();
53
+ expect(contexts).toContainEqual({
54
+ name: "outsideHelper",
55
+ context: { ok: true },
56
+ });
57
+ // Cleanup
58
+ (0, react_1.act)(() => {
59
+ result.current.removeContextHelper("outsideHelper");
60
+ });
63
61
  consoleSpy.mockRestore();
64
62
  });
65
63
  /**
66
- * Test: Hook provides the expected API functions
67
- * Verifies that all required functions are available when the hook is used
68
- * within the provider context
64
+ * Verifies that the hook returns the expected API functions when used within a provider.
69
65
  */
70
- it("should provide context helpers functions", () => {
66
+ it("should provide context helpers functions (inside provider)", () => {
71
67
  const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
72
68
  wrapper,
73
69
  });
74
- // Verify all expected functions are present in the hook's return value
75
70
  expect(result.current).toHaveProperty("getAdditionalContext");
76
71
  expect(result.current).toHaveProperty("getContextHelpers");
77
- expect(result.current).toHaveProperty("setContextHelperEnabled");
78
- });
79
- /**
80
- * Test: Default configuration is applied correctly
81
- * Ensures that the default context helpers are loaded with their
82
- * expected enabled/disabled states
83
- */
84
- it("should return default context helpers configuration", () => {
85
- const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
86
- wrapper,
87
- });
88
- const helpers = result.current.getContextHelpers();
89
- // Verify we have the expected number of default helpers
90
- expect(helpers).toHaveLength(context_helpers_1.DEFAULT_CONTEXT_HELPERS.length);
91
- // Check specific helpers have their expected default states
92
- const userTimeHelper = helpers.find((h) => h.name === "userTime");
93
- const userPageHelper = helpers.find((h) => h.name === "userPage");
94
- // userTime should be disabled by default
95
- expect(userTimeHelper?.enabled).toBe(false);
96
- // userPage should be disabled by default
97
- expect(userPageHelper?.enabled).toBe(false);
98
- });
99
- /**
100
- * Test: Context helper enabled state can be toggled
101
- * Verifies that the setContextHelperEnabled function correctly updates
102
- * the enabled state of individual helpers
103
- */
104
- it("should allow toggling context helper enabled state", () => {
105
- const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
106
- wrapper,
107
- });
108
- // Enable the userPage helper using the provided function
109
- (0, react_1.act)(() => {
110
- result.current.setContextHelperEnabled("userPage", true);
111
- });
112
- // Verify the helper's state was updated
113
- const helpers = result.current.getContextHelpers();
114
- const userPageHelper = helpers.find((h) => h.name === "userPage");
115
- expect(userPageHelper?.enabled).toBe(true);
72
+ expect(result.current).toHaveProperty("addContextHelper");
73
+ expect(result.current).toHaveProperty("removeContextHelper");
116
74
  });
117
75
  /**
118
- * Test: Only enabled helpers contribute to additional context
119
- * Verifies that getAdditionalContext only runs and returns data from
120
- * helpers that are currently enabled
76
+ * With no helpers provided, no additional context should be returned.
121
77
  */
122
- it("should get additional context from enabled helpers", async () => {
78
+ it("should return no additional context when no helpers are provided", async () => {
123
79
  const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
124
80
  wrapper,
125
81
  });
126
82
  const contexts = await result.current.getAdditionalContext();
127
- // By default, no context helpers are enabled, so we should get no contexts
128
83
  expect(contexts).toHaveLength(0);
129
84
  });
130
85
  /**
131
- * Test: Provider respects configuration prop
132
- * Verifies that initial configuration can be customized through the
133
- * contextHelpers prop on the provider
86
+ * When helpers are provided, getAdditionalContext should aggregate them.
87
+ * Note: prebuiltUserPage may be null in non-browser envs, so we only assert userTime when present.
134
88
  */
135
- it("should respect contextHelpers configuration prop", () => {
136
- // Create a custom wrapper with specific configuration
137
- const customWrapper = ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
138
- userTime: false, // Disable userTime
139
- userPage: true, // Enable userPage
89
+ it("should get additional context when helpers are provided", async () => {
90
+ const withHelpers = ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
91
+ userTime: context_helpers_1.currentTimeContextHelper,
92
+ userPage: context_helpers_1.currentPageContextHelper,
140
93
  } }, children));
141
94
  const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
142
- wrapper: customWrapper,
95
+ wrapper: withHelpers,
143
96
  });
144
- // Verify the custom configuration is applied
145
- const helpers = result.current.getContextHelpers();
146
- const userTimeHelper = helpers.find((h) => h.name === "userTime");
147
- const userPageHelper = helpers.find((h) => h.name === "userPage");
148
- expect(userTimeHelper?.enabled).toBe(false);
149
- expect(userPageHelper?.enabled).toBe(true);
97
+ const contexts = await result.current.getAdditionalContext();
98
+ const names = contexts.map((c) => c.name);
99
+ // userTime should be present from prebuilt helper
100
+ expect(names).toContain("userTime");
101
+ // userPage may be absent on server; do not assert strictly
150
102
  });
151
103
  /**
152
- * Test: Errors in context helpers are handled gracefully
153
- * Verifies that if a context helper throws an error, it doesn't crash
154
- * the entire system and other helpers continue to work
104
+ * Errors thrown by helper functions should be caught and skipped, not crash the system.
155
105
  */
156
106
  it("should handle errors in context helper functions gracefully", async () => {
157
- // Mock console.error to capture error logs
158
107
  const consoleErrorSpy = jest
159
108
  .spyOn(console, "error")
160
109
  .mockImplementation(() => { });
161
- // Create a helper that will throw an error when run
162
- const errorHelper = {
163
- name: "errorHelper",
164
- enabled: true,
165
- run: () => {
166
- throw new Error("Test error");
167
- },
110
+ const badHelper = () => {
111
+ throw new Error("Test error");
168
112
  };
169
- // Temporarily add the error helper to the default helpers
170
- // Store original helpers to restore later
171
- const originalHelpers = context_helpers_1.DEFAULT_CONTEXT_HELPERS;
172
- Object.defineProperty(contextHelpers, "DEFAULT_CONTEXT_HELPERS", {
173
- value: [...originalHelpers, errorHelper],
174
- writable: true,
175
- });
113
+ const withBadHelper = ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: { badHelper } }, children));
176
114
  const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
177
- wrapper,
115
+ wrapper: withBadHelper,
178
116
  });
179
- // Call getAdditionalContext, which should handle the error gracefully
180
117
  const contexts = await result.current.getAdditionalContext();
181
- // Should have no contexts because the error helper should be skipped
182
118
  expect(contexts.length).toBe(0);
183
- // Verify that the error was logged appropriately
184
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Error running context helper errorHelper"), expect.any(Error));
185
- // Restore the original helpers and console.error
186
- Object.defineProperty(contextHelpers, "DEFAULT_CONTEXT_HELPERS", {
187
- value: originalHelpers,
188
- writable: true,
189
- });
119
+ expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Error running context helper badHelper"), expect.any(Error));
190
120
  consoleErrorSpy.mockRestore();
191
121
  });
192
122
  });
193
123
  });
124
+ /**
125
+ * Test suite for Custom Context Helpers using the simplified API.
126
+ *
127
+ * Focuses on:
128
+ * - Passing custom helpers via the provider prop.
129
+ * - Overriding prebuilt helpers with custom implementations.
130
+ * - Dynamic add/remove helper management at runtime.
131
+ * - Supporting both sync and async helpers.
132
+ * - Using the config key as the context name.
133
+ * - Graceful error handling for custom helpers.
134
+ */
135
+ describe("Custom Context Helpers via contextHelpers config", () => {
136
+ beforeEach(() => {
137
+ (0, registry_1.setHelpers)({});
138
+ jest.clearAllMocks();
139
+ });
140
+ /**
141
+ * Custom helpers can be added through the provider configuration.
142
+ * The key becomes the context name and the function returns the raw value.
143
+ */
144
+ it("should accept custom helpers through contextHelpers config", async () => {
145
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
146
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
147
+ customData: async () => ({ custom: "value" }),
148
+ } }, children)),
149
+ });
150
+ const contexts = await result.current.getAdditionalContext();
151
+ expect(contexts).toContainEqual({
152
+ name: "customData",
153
+ context: { custom: "value" },
154
+ });
155
+ });
156
+ /**
157
+ * Built-in helpers can be overridden by providing a function under the same key.
158
+ */
159
+ it("should allow custom helpers to override built-in ones", async () => {
160
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
161
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
162
+ userTime: async () => ({ customTime: "override" }),
163
+ } }, children)),
164
+ });
165
+ const contexts = await result.current.getAdditionalContext();
166
+ const userTimeContext = contexts.find((c) => c.name === "userTime");
167
+ expect(userTimeContext?.context).toEqual({ customTime: "override" });
168
+ });
169
+ /**
170
+ * Helpers can be added dynamically at runtime via addContextHelper.
171
+ */
172
+ it("should handle dynamic addition of custom helpers", async () => {
173
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
174
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {} }, children)),
175
+ });
176
+ // Initially none
177
+ expect(await result.current.getAdditionalContext()).toHaveLength(0);
178
+ (0, react_1.act)(() => {
179
+ result.current.addContextHelper("dynamicHelper", async () => ({
180
+ dynamic: true,
181
+ }));
182
+ });
183
+ const contexts = await result.current.getAdditionalContext();
184
+ expect(contexts).toContainEqual({
185
+ name: "dynamicHelper",
186
+ context: { dynamic: true },
187
+ });
188
+ });
189
+ /**
190
+ * Helpers can be removed dynamically via removeContextHelper.
191
+ */
192
+ it("should handle dynamic removal of context helpers", async () => {
193
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
194
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
195
+ customHelper: async () => ({ test: "data" }),
196
+ } }, children)),
197
+ });
198
+ // Verify exists
199
+ let contexts = await result.current.getAdditionalContext();
200
+ expect(contexts.find((c) => c.name === "customHelper")).toBeDefined();
201
+ // Remove
202
+ (0, react_1.act)(() => {
203
+ result.current.removeContextHelper("customHelper");
204
+ });
205
+ // Verify removed
206
+ contexts = await result.current.getAdditionalContext();
207
+ expect(contexts.find((c) => c.name === "customHelper")).toBeUndefined();
208
+ });
209
+ /**
210
+ * Multiple custom helpers should be supported; helpers returning null/undefined are skipped.
211
+ */
212
+ it("should handle multiple custom helpers", async () => {
213
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
214
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
215
+ helper1: async () => ({ data: "one" }),
216
+ helper2: async () => ({ data: "two" }),
217
+ helper3: () => null, // disabled by returning null
218
+ } }, children)),
219
+ });
220
+ const contexts = await result.current.getAdditionalContext();
221
+ expect(contexts).toContainEqual({
222
+ name: "helper1",
223
+ context: { data: "one" },
224
+ });
225
+ expect(contexts).toContainEqual({
226
+ name: "helper2",
227
+ context: { data: "two" },
228
+ });
229
+ expect(contexts.find((c) => c.name === "helper3")).toBeUndefined();
230
+ });
231
+ /**
232
+ * Both synchronous and asynchronous helpers should be supported.
233
+ */
234
+ it("should handle sync and async custom helpers", async () => {
235
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
236
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
237
+ syncHelper: () => ({ sync: true }),
238
+ asyncHelper: async () => {
239
+ await new Promise((resolve) => setTimeout(resolve, 10));
240
+ return { async: true };
241
+ },
242
+ } }, children)),
243
+ });
244
+ const contexts = await result.current.getAdditionalContext();
245
+ expect(contexts).toContainEqual({
246
+ name: "syncHelper",
247
+ context: { sync: true },
248
+ });
249
+ expect(contexts).toContainEqual({
250
+ name: "asyncHelper",
251
+ context: { async: true },
252
+ });
253
+ });
254
+ /**
255
+ * The key used in the contextHelpers map becomes the context name.
256
+ */
257
+ it("should use key name as context name for custom helpers", async () => {
258
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
259
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
260
+ myCustomKey: async () => ({ value: "test" }),
261
+ } }, children)),
262
+ });
263
+ const contexts = await result.current.getAdditionalContext();
264
+ expect(contexts).toContainEqual({
265
+ name: "myCustomKey",
266
+ context: { value: "test" },
267
+ });
268
+ });
269
+ /**
270
+ * Errors thrown by custom helpers should be logged and skipped, not crash the system.
271
+ */
272
+ it("should handle errors in custom helpers gracefully", async () => {
273
+ const consoleErrorSpy = jest
274
+ .spyOn(console, "error")
275
+ .mockImplementation(() => { });
276
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
277
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
278
+ errorHelper: async () => {
279
+ throw new Error("Custom helper error");
280
+ },
281
+ goodHelper: async () => ({ good: "data" }),
282
+ } }, children)),
283
+ });
284
+ const contexts = await result.current.getAdditionalContext();
285
+ expect(contexts).toContainEqual({
286
+ name: "goodHelper",
287
+ context: { good: "data" },
288
+ });
289
+ expect(contexts.find((c) => c.name === "errorHelper")).toBeUndefined();
290
+ expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("Error running context helper errorHelper"), expect.any(Error));
291
+ consoleErrorSpy.mockRestore();
292
+ });
293
+ /**
294
+ * Removing non-existent helpers should be a no-op without throwing errors.
295
+ */
296
+ it("should handle removing non-existent helper gracefully", () => {
297
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
298
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {} }, children)),
299
+ });
300
+ expect(() => {
301
+ (0, react_1.act)(() => {
302
+ result.current.removeContextHelper("nonExistent");
303
+ });
304
+ }).not.toThrow();
305
+ });
306
+ /**
307
+ * Adding with an existing name should update/replace the helper implementation.
308
+ */
309
+ it("should allow updating existing helper via addContextHelper", async () => {
310
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_context_helpers_provider_1.useTamboContextHelpers)(), {
311
+ wrapper: ({ children }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
312
+ testHelper: async () => ({ original: true }),
313
+ } }, children)),
314
+ });
315
+ // Verify original helper
316
+ let contexts = await result.current.getAdditionalContext();
317
+ expect(contexts).toContainEqual({
318
+ name: "testHelper",
319
+ context: { original: true },
320
+ });
321
+ // Update the helper
322
+ (0, react_1.act)(() => {
323
+ result.current.addContextHelper("testHelper", async () => ({
324
+ updated: true,
325
+ }));
326
+ });
327
+ // Verify updated helper
328
+ contexts = await result.current.getAdditionalContext();
329
+ expect(contexts).toContainEqual({
330
+ name: "testHelper",
331
+ context: { updated: true },
332
+ });
333
+ });
334
+ });
194
335
  //# sourceMappingURL=tambo-context-helpers-provider.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-context-helpers-provider.test.js","sourceRoot":"","sources":["../../../src/providers/__tests__/tambo-context-helpers-provider.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook } from \"@testing-library/react\";\nimport React from \"react\";\nimport * as contextHelpers from \"../../context-helpers\";\nimport { DEFAULT_CONTEXT_HELPERS } from \"../../context-helpers\";\nimport {\n TamboContextHelpersProvider,\n useTamboContextHelpers,\n} from \"../tambo-context-helpers-provider\";\n\ndescribe(\"TamboContextHelpersProvider\", () => {\n // Create a wrapper component that provides the context for testing hooks\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboContextHelpersProvider>{children}</TamboContextHelpersProvider>\n );\n\n describe(\"useTamboContextHelpers\", () => {\n /**\n * Test: Hook throws error when used outside of provider\n * This ensures developers get a clear error message if they forget to wrap\n * their components with the TamboContextHelpersProvider\n */\n it(\"should throw error when used outside provider\", () => {\n // Mock console.error to prevent error output in test logs\n const consoleSpy = jest\n .spyOn(console, \"error\")\n .mockImplementation(() => {});\n\n // Verify that using the hook without a provider throws the expected error\n expect(() => {\n renderHook(() => useTamboContextHelpers());\n }).toThrow(\n \"useTamboContextHelpers must be used within a TamboContextHelpersProvider\",\n );\n\n // Restore console.error to its original implementation\n consoleSpy.mockRestore();\n });\n\n /**\n * Test: Hook provides the expected API functions\n * Verifies that all required functions are available when the hook is used\n * within the provider context\n */\n it(\"should provide context helpers functions\", () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n\n // Verify all expected functions are present in the hook's return value\n expect(result.current).toHaveProperty(\"getAdditionalContext\");\n expect(result.current).toHaveProperty(\"getContextHelpers\");\n expect(result.current).toHaveProperty(\"setContextHelperEnabled\");\n });\n\n /**\n * Test: Default configuration is applied correctly\n * Ensures that the default context helpers are loaded with their\n * expected enabled/disabled states\n */\n it(\"should return default context helpers configuration\", () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n\n const helpers = result.current.getContextHelpers();\n\n // Verify we have the expected number of default helpers\n expect(helpers).toHaveLength(DEFAULT_CONTEXT_HELPERS.length);\n\n // Check specific helpers have their expected default states\n const userTimeHelper = helpers.find((h) => h.name === \"userTime\");\n const userPageHelper = helpers.find((h) => h.name === \"userPage\");\n\n // userTime should be disabled by default\n expect(userTimeHelper?.enabled).toBe(false);\n // userPage should be disabled by default\n expect(userPageHelper?.enabled).toBe(false);\n });\n\n /**\n * Test: Context helper enabled state can be toggled\n * Verifies that the setContextHelperEnabled function correctly updates\n * the enabled state of individual helpers\n */\n it(\"should allow toggling context helper enabled state\", () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n\n // Enable the userPage helper using the provided function\n act(() => {\n result.current.setContextHelperEnabled(\"userPage\", true);\n });\n\n // Verify the helper's state was updated\n const helpers = result.current.getContextHelpers();\n const userPageHelper = helpers.find((h) => h.name === \"userPage\");\n\n expect(userPageHelper?.enabled).toBe(true);\n });\n\n /**\n * Test: Only enabled helpers contribute to additional context\n * Verifies that getAdditionalContext only runs and returns data from\n * helpers that are currently enabled\n */\n it(\"should get additional context from enabled helpers\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n\n const contexts = await result.current.getAdditionalContext();\n\n // By default, no context helpers are enabled, so we should get no contexts\n expect(contexts).toHaveLength(0);\n });\n\n /**\n * Test: Provider respects configuration prop\n * Verifies that initial configuration can be customized through the\n * contextHelpers prop on the provider\n */\n it(\"should respect contextHelpers configuration prop\", () => {\n // Create a custom wrapper with specific configuration\n const customWrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n userTime: false, // Disable userTime\n userPage: true, // Enable userPage\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n );\n\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: customWrapper,\n });\n\n // Verify the custom configuration is applied\n const helpers = result.current.getContextHelpers();\n const userTimeHelper = helpers.find((h) => h.name === \"userTime\");\n const userPageHelper = helpers.find((h) => h.name === \"userPage\");\n\n expect(userTimeHelper?.enabled).toBe(false);\n expect(userPageHelper?.enabled).toBe(true);\n });\n\n /**\n * Test: Errors in context helpers are handled gracefully\n * Verifies that if a context helper throws an error, it doesn't crash\n * the entire system and other helpers continue to work\n */\n it(\"should handle errors in context helper functions gracefully\", async () => {\n // Mock console.error to capture error logs\n const consoleErrorSpy = jest\n .spyOn(console, \"error\")\n .mockImplementation(() => {});\n\n // Create a helper that will throw an error when run\n const errorHelper = {\n name: \"errorHelper\",\n enabled: true,\n run: () => {\n throw new Error(\"Test error\");\n },\n };\n\n // Temporarily add the error helper to the default helpers\n // Store original helpers to restore later\n const originalHelpers = DEFAULT_CONTEXT_HELPERS;\n Object.defineProperty(contextHelpers, \"DEFAULT_CONTEXT_HELPERS\", {\n value: [...originalHelpers, errorHelper],\n writable: true,\n });\n\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n\n // Call getAdditionalContext, which should handle the error gracefully\n const contexts = await result.current.getAdditionalContext();\n\n // Should have no contexts because the error helper should be skipped\n expect(contexts.length).toBe(0);\n\n // Verify that the error was logged appropriately\n expect(consoleErrorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"Error running context helper errorHelper\"),\n expect.any(Error),\n );\n\n // Restore the original helpers and console.error\n Object.defineProperty(contextHelpers, \"DEFAULT_CONTEXT_HELPERS\", {\n value: originalHelpers,\n writable: true,\n });\n consoleErrorSpy.mockRestore();\n });\n });\n});\n"]}
1
+ {"version":3,"file":"tambo-context-helpers-provider.test.js","sourceRoot":"","sources":["../../../src/providers/__tests__/tambo-context-helpers-provider.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook } from \"@testing-library/react\";\nimport React from \"react\";\nimport {\n currentPageContextHelper,\n currentTimeContextHelper,\n} from \"../../context-helpers\";\nimport { setHelpers } from \"../../context-helpers/registry\";\nimport {\n TamboContextHelpersProvider,\n useTamboContextHelpers,\n} from \"../tambo-context-helpers-provider\";\n\n/**\n * Test suite for TamboContextHelpersProvider (simplified API, registry-backed)\n *\n * The simplified API:\n * - Accepts a plain map of { key: () => any | null | undefined | Promise<any | null | undefined> }.\n * - The key becomes the context name sent to the model.\n * - Returning null/undefined from a helper skips inclusion.\n * - Prebuilt helpers are just functions (e.g., prebuiltUserTime, prebuiltUserPage).\n *\n * The hook is now registry-backed and safe to call outside a provider. When used\n * outside a provider, it proxies to a global registry and still works.\n */\ndescribe(\"TamboContextHelpersProvider\", () => {\n // Ensure registry is clean for each test to avoid cross-test contamination\n beforeEach(() => {\n setHelpers({});\n jest.clearAllMocks();\n });\n\n // Base wrapper with no helpers provided\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboContextHelpersProvider>{children}</TamboContextHelpersProvider>\n );\n\n describe(\"useTamboContextHelpers\", () => {\n /**\n * NOTE: The hook is registry-backed and safe outside provider. It should not throw.\n * This replaces the previous behavior that threw without a provider.\n */\n it(\"should be safe outside provider (registry-backed no provider)\", async () => {\n const consoleSpy = jest\n .spyOn(console, \"error\")\n .mockImplementation(() => {});\n const { result } = renderHook(() => useTamboContextHelpers());\n\n // Should return callable functions\n expect(typeof result.current.getAdditionalContext).toBe(\"function\");\n expect(typeof result.current.getContextHelpers).toBe(\"function\");\n expect(typeof result.current.addContextHelper).toBe(\"function\");\n expect(typeof result.current.removeContextHelper).toBe(\"function\");\n\n // Starts empty\n expect(await result.current.getAdditionalContext()).toHaveLength(0);\n\n // Add a helper and verify\n act(() => {\n result.current.addContextHelper(\"outsideHelper\", () => ({ ok: true }));\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"outsideHelper\",\n context: { ok: true },\n });\n\n // Cleanup\n act(() => {\n result.current.removeContextHelper(\"outsideHelper\");\n });\n consoleSpy.mockRestore();\n });\n\n /**\n * Verifies that the hook returns the expected API functions when used within a provider.\n */\n it(\"should provide context helpers functions (inside provider)\", () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n expect(result.current).toHaveProperty(\"getAdditionalContext\");\n expect(result.current).toHaveProperty(\"getContextHelpers\");\n expect(result.current).toHaveProperty(\"addContextHelper\");\n expect(result.current).toHaveProperty(\"removeContextHelper\");\n });\n\n /**\n * With no helpers provided, no additional context should be returned.\n */\n it(\"should return no additional context when no helpers are provided\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toHaveLength(0);\n });\n\n /**\n * When helpers are provided, getAdditionalContext should aggregate them.\n * Note: prebuiltUserPage may be null in non-browser envs, so we only assert userTime when present.\n */\n it(\"should get additional context when helpers are provided\", async () => {\n const withHelpers = ({ children }: { children: React.ReactNode }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n userTime: currentTimeContextHelper,\n userPage: currentPageContextHelper,\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n );\n\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: withHelpers,\n });\n\n const contexts = await result.current.getAdditionalContext();\n const names = contexts.map((c) => c.name);\n\n // userTime should be present from prebuilt helper\n expect(names).toContain(\"userTime\");\n // userPage may be absent on server; do not assert strictly\n });\n\n /**\n * Errors thrown by helper functions should be caught and skipped, not crash the system.\n */\n it(\"should handle errors in context helper functions gracefully\", async () => {\n const consoleErrorSpy = jest\n .spyOn(console, \"error\")\n .mockImplementation(() => {});\n\n const badHelper = () => {\n throw new Error(\"Test error\");\n };\n\n const withBadHelper = ({ children }: { children: React.ReactNode }) => (\n <TamboContextHelpersProvider contextHelpers={{ badHelper }}>\n {children}\n </TamboContextHelpersProvider>\n );\n\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: withBadHelper,\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts.length).toBe(0);\n expect(consoleErrorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"Error running context helper badHelper\"),\n expect.any(Error),\n );\n consoleErrorSpy.mockRestore();\n });\n });\n});\n\n/**\n * Test suite for Custom Context Helpers using the simplified API.\n *\n * Focuses on:\n * - Passing custom helpers via the provider prop.\n * - Overriding prebuilt helpers with custom implementations.\n * - Dynamic add/remove helper management at runtime.\n * - Supporting both sync and async helpers.\n * - Using the config key as the context name.\n * - Graceful error handling for custom helpers.\n */\ndescribe(\"Custom Context Helpers via contextHelpers config\", () => {\n beforeEach(() => {\n setHelpers({});\n jest.clearAllMocks();\n });\n\n /**\n * Custom helpers can be added through the provider configuration.\n * The key becomes the context name and the function returns the raw value.\n */\n it(\"should accept custom helpers through contextHelpers config\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n customData: async () => ({ custom: \"value\" }),\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"customData\",\n context: { custom: \"value\" },\n });\n });\n\n /**\n * Built-in helpers can be overridden by providing a function under the same key.\n */\n it(\"should allow custom helpers to override built-in ones\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n userTime: async () => ({ customTime: \"override\" }),\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n const contexts = await result.current.getAdditionalContext();\n const userTimeContext = contexts.find((c) => c.name === \"userTime\");\n expect(userTimeContext?.context).toEqual({ customTime: \"override\" });\n });\n\n /**\n * Helpers can be added dynamically at runtime via addContextHelper.\n */\n it(\"should handle dynamic addition of custom helpers\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider contextHelpers={{}}>\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n // Initially none\n expect(await result.current.getAdditionalContext()).toHaveLength(0);\n\n act(() => {\n result.current.addContextHelper(\"dynamicHelper\", async () => ({\n dynamic: true,\n }));\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"dynamicHelper\",\n context: { dynamic: true },\n });\n });\n\n /**\n * Helpers can be removed dynamically via removeContextHelper.\n */\n it(\"should handle dynamic removal of context helpers\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n customHelper: async () => ({ test: \"data\" }),\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n // Verify exists\n let contexts = await result.current.getAdditionalContext();\n expect(contexts.find((c) => c.name === \"customHelper\")).toBeDefined();\n\n // Remove\n act(() => {\n result.current.removeContextHelper(\"customHelper\");\n });\n\n // Verify removed\n contexts = await result.current.getAdditionalContext();\n expect(contexts.find((c) => c.name === \"customHelper\")).toBeUndefined();\n });\n\n /**\n * Multiple custom helpers should be supported; helpers returning null/undefined are skipped.\n */\n it(\"should handle multiple custom helpers\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n helper1: async () => ({ data: \"one\" }),\n helper2: async () => ({ data: \"two\" }),\n helper3: () => null, // disabled by returning null\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"helper1\",\n context: { data: \"one\" },\n });\n expect(contexts).toContainEqual({\n name: \"helper2\",\n context: { data: \"two\" },\n });\n expect(contexts.find((c) => c.name === \"helper3\")).toBeUndefined();\n });\n\n /**\n * Both synchronous and asynchronous helpers should be supported.\n */\n it(\"should handle sync and async custom helpers\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n syncHelper: () => ({ sync: true }),\n asyncHelper: async () => {\n await new Promise((resolve) => setTimeout(resolve, 10));\n return { async: true };\n },\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"syncHelper\",\n context: { sync: true },\n });\n expect(contexts).toContainEqual({\n name: \"asyncHelper\",\n context: { async: true },\n });\n });\n\n /**\n * The key used in the contextHelpers map becomes the context name.\n */\n it(\"should use key name as context name for custom helpers\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n myCustomKey: async () => ({ value: \"test\" }),\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"myCustomKey\",\n context: { value: \"test\" },\n });\n });\n\n /**\n * Errors thrown by custom helpers should be logged and skipped, not crash the system.\n */\n it(\"should handle errors in custom helpers gracefully\", async () => {\n const consoleErrorSpy = jest\n .spyOn(console, \"error\")\n .mockImplementation(() => {});\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n errorHelper: async () => {\n throw new Error(\"Custom helper error\");\n },\n goodHelper: async () => ({ good: \"data\" }),\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n const contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"goodHelper\",\n context: { good: \"data\" },\n });\n expect(contexts.find((c) => c.name === \"errorHelper\")).toBeUndefined();\n expect(consoleErrorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"Error running context helper errorHelper\"),\n expect.any(Error),\n );\n consoleErrorSpy.mockRestore();\n });\n\n /**\n * Removing non-existent helpers should be a no-op without throwing errors.\n */\n it(\"should handle removing non-existent helper gracefully\", () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider contextHelpers={{}}>\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n expect(() => {\n act(() => {\n result.current.removeContextHelper(\"nonExistent\");\n });\n }).not.toThrow();\n });\n\n /**\n * Adding with an existing name should update/replace the helper implementation.\n */\n it(\"should allow updating existing helper via addContextHelper\", async () => {\n const { result } = renderHook(() => useTamboContextHelpers(), {\n wrapper: ({ children }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n testHelper: async () => ({ original: true }),\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n ),\n });\n\n // Verify original helper\n let contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"testHelper\",\n context: { original: true },\n });\n\n // Update the helper\n act(() => {\n result.current.addContextHelper(\"testHelper\", async () => ({\n updated: true,\n }));\n });\n\n // Verify updated helper\n contexts = await result.current.getAdditionalContext();\n expect(contexts).toContainEqual({\n name: \"testHelper\",\n context: { updated: true },\n });\n });\n});\n"]}