@newschools/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/nuxt/src/index.ts +46 -0
- package/nuxt/src/module.ts +98 -0
- package/nuxt/src/runtime/client.ts +150 -0
- package/nuxt/src/runtime/components/OrganizationHero.vue +201 -0
- package/nuxt/src/runtime/composables/useAsyncNewSchools.ts +184 -0
- package/nuxt/src/runtime/composables/useOrganization.ts +74 -0
- package/nuxt/src/runtime/plugin.ts +104 -0
- package/nuxt/src/runtime/styles/tokens.css +112 -0
- package/nuxt/src/runtime/utils/pathResolver.ts +164 -0
- package/nuxt/src/types/api.ts +44 -0
- package/nuxt/src/types/entities.ts +197 -0
- package/nuxt/src/types/index.ts +24 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 New Schools
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# @newschools/nuxt
|
|
2
|
+
|
|
3
|
+
Nuxt module for integrating New Schools learning content into your applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✨ **Auto-imported composables** - `useAsyncNewSchools` available everywhere
|
|
8
|
+
🔒 **Type-safe** - Full TypeScript support with entity types
|
|
9
|
+
⚡ **SSR-compatible** - Built on Nuxt's `useAsyncData`
|
|
10
|
+
🎯 **Simple API** - Fetch journeys, waypoints, and activities with ease
|
|
11
|
+
🔑 **API key authentication** - Secure access to your organisation's content
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Using npm
|
|
17
|
+
npm install @newschools/nuxt
|
|
18
|
+
|
|
19
|
+
# Using yarn
|
|
20
|
+
yarn add @newschools/nuxt
|
|
21
|
+
|
|
22
|
+
# Using pnpm
|
|
23
|
+
pnpm add @newschools/nuxt
|
|
24
|
+
|
|
25
|
+
# Using bun
|
|
26
|
+
bun add @newschools/nuxt
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
### 1. Add module to `nuxt.config.ts`
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
export default defineNuxtConfig({
|
|
35
|
+
modules: ["@newschools/nuxt"],
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Configure API key
|
|
40
|
+
|
|
41
|
+
Add your New Schools API key to `.env`:
|
|
42
|
+
|
|
43
|
+
```env
|
|
44
|
+
NUXT_PUBLIC_NEWSCHOOLS_API_KEY=ns_live_xxxxxxxxxxxxx
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or configure directly in `nuxt.config.ts`:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
export default defineNuxtConfig({
|
|
51
|
+
modules: ["@newschools/nuxt"],
|
|
52
|
+
|
|
53
|
+
newschools: {
|
|
54
|
+
apiKey: "ns_live_xxxxxxxxxxxxx",
|
|
55
|
+
baseUrl: "https://newschools.ai", // optional, defaults to production
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
The `useAsyncNewSchools` composable is auto-imported and available in all components, pages, and composables.
|
|
63
|
+
|
|
64
|
+
### List Journeys
|
|
65
|
+
|
|
66
|
+
```vue
|
|
67
|
+
<script setup lang="ts">
|
|
68
|
+
const {
|
|
69
|
+
data: journeys,
|
|
70
|
+
pending,
|
|
71
|
+
error,
|
|
72
|
+
refresh,
|
|
73
|
+
} = await useAsyncNewSchools("journeys");
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<template>
|
|
77
|
+
<div>
|
|
78
|
+
<div v-if="pending">Loading journeys...</div>
|
|
79
|
+
<div v-else-if="error">Error: {{ error.message }}</div>
|
|
80
|
+
<div v-else>
|
|
81
|
+
<div v-for="journey in journeys" :key="journey.id">
|
|
82
|
+
<h2>{{ journey.title }}</h2>
|
|
83
|
+
<p>{{ journey.description }}</p>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Get Single Journey
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<script setup lang="ts">
|
|
94
|
+
const route = useRoute();
|
|
95
|
+
const slug = route.params.slug as string;
|
|
96
|
+
|
|
97
|
+
const { data: journey } = await useAsyncNewSchools("journey", { id: slug });
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<template>
|
|
101
|
+
<div>
|
|
102
|
+
<h1>{{ journey?.title }}</h1>
|
|
103
|
+
<p>{{ journey?.summary }}</p>
|
|
104
|
+
<img v-if="journey?.coverImageUrl" :src="journey.coverImageUrl" />
|
|
105
|
+
</div>
|
|
106
|
+
</template>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### List Waypoints for Journey
|
|
110
|
+
|
|
111
|
+
```vue
|
|
112
|
+
<script setup lang="ts">
|
|
113
|
+
const route = useRoute();
|
|
114
|
+
const journeyId = route.params.journeyId as string;
|
|
115
|
+
|
|
116
|
+
const { data: waypoints } = await useAsyncNewSchools("waypoints", {
|
|
117
|
+
journeyId,
|
|
118
|
+
});
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<template>
|
|
122
|
+
<div>
|
|
123
|
+
<div v-for="waypoint in waypoints" :key="waypoint.id">
|
|
124
|
+
<h3>{{ waypoint.title }}</h3>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</template>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Get Single Waypoint
|
|
131
|
+
|
|
132
|
+
```vue
|
|
133
|
+
<script setup lang="ts">
|
|
134
|
+
const { data: waypoint } = await useAsyncNewSchools("waypoint", {
|
|
135
|
+
journeyId: "journey-123",
|
|
136
|
+
waypointId: "waypoint-456",
|
|
137
|
+
});
|
|
138
|
+
</script>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### List Activities
|
|
142
|
+
|
|
143
|
+
```vue
|
|
144
|
+
<script setup lang="ts">
|
|
145
|
+
const { data: activities } = await useAsyncNewSchools("activities", {
|
|
146
|
+
journeyId: "journey-123",
|
|
147
|
+
waypointId: "waypoint-456",
|
|
148
|
+
});
|
|
149
|
+
</script>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Get Single Activity
|
|
153
|
+
|
|
154
|
+
```vue
|
|
155
|
+
<script setup lang="ts">
|
|
156
|
+
const { data: activity } = await useAsyncNewSchools("activity", {
|
|
157
|
+
journeyId: "journey-123",
|
|
158
|
+
waypointId: "waypoint-456",
|
|
159
|
+
activityId: "activity-789",
|
|
160
|
+
});
|
|
161
|
+
</script>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## API Reference
|
|
165
|
+
|
|
166
|
+
### `useAsyncNewSchools(resource, params?)`
|
|
167
|
+
|
|
168
|
+
Fetch data from New Schools v1 API with SSR support.
|
|
169
|
+
|
|
170
|
+
**Parameters:**
|
|
171
|
+
|
|
172
|
+
- `resource` - Resource type to fetch:
|
|
173
|
+
- `'journeys'` - List all journeys
|
|
174
|
+
- `'journey'` - Get single journey
|
|
175
|
+
- `'waypoints'` - List waypoints for journey
|
|
176
|
+
- `'waypoint'` - Get single waypoint
|
|
177
|
+
- `'activities'` - List activities for waypoint
|
|
178
|
+
- `'activity'` - Get single activity
|
|
179
|
+
|
|
180
|
+
- `params` - Resource-specific parameters (optional for lists):
|
|
181
|
+
- Journey: `{ id: string }`
|
|
182
|
+
- Waypoint: `{ journeyId: string, waypointId?: string }`
|
|
183
|
+
- Activity: `{ journeyId: string, waypointId: string, activityId?: string }`
|
|
184
|
+
|
|
185
|
+
**Returns:**
|
|
186
|
+
|
|
187
|
+
`AsyncData` object with:
|
|
188
|
+
|
|
189
|
+
- `data` - Fetched data (reactive)
|
|
190
|
+
- `pending` - Loading state (reactive)
|
|
191
|
+
- `error` - Error object if request failed (reactive)
|
|
192
|
+
- `refresh()` - Function to refetch data
|
|
193
|
+
|
|
194
|
+
## TypeScript Support
|
|
195
|
+
|
|
196
|
+
Full type safety for all entities:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import type {
|
|
200
|
+
Journey,
|
|
201
|
+
Waypoint,
|
|
202
|
+
Activity,
|
|
203
|
+
JourneyTheme,
|
|
204
|
+
WaypointType,
|
|
205
|
+
LayoutMode,
|
|
206
|
+
} from "@newschools/nuxt";
|
|
207
|
+
|
|
208
|
+
// Types are automatically inferred in useAsyncNewSchools
|
|
209
|
+
const { data: journeys } = await useAsyncNewSchools("journeys");
|
|
210
|
+
// journeys is typed as Journey[] | null
|
|
211
|
+
|
|
212
|
+
const { data: journey } = await useAsyncNewSchools("journey", { id: "slug" });
|
|
213
|
+
// journey is typed as Journey | null
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Advanced Usage
|
|
217
|
+
|
|
218
|
+
### Manual Client (Without Nuxt Composables)
|
|
219
|
+
|
|
220
|
+
For use in server routes, plugins, or non-component contexts:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { createClient } from "@newschools/nuxt";
|
|
224
|
+
|
|
225
|
+
const client = createClient({
|
|
226
|
+
apiKey: "ns_live_xxxxxxxxxxxxx",
|
|
227
|
+
baseUrl: "https://newschools.ai", // optional
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const journeys = await client.get<Journey[]>("/journeys");
|
|
231
|
+
const journey = await client.get<Journey>("/journeys/my-slug");
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Custom Path Resolution
|
|
235
|
+
|
|
236
|
+
For advanced integrations:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { NewSchoolsPathResolver } from "@newschools/nuxt";
|
|
240
|
+
|
|
241
|
+
const path = NewSchoolsPathResolver.resolve("journey", "list");
|
|
242
|
+
// Returns: '/journeys'
|
|
243
|
+
|
|
244
|
+
const path2 = NewSchoolsPathResolver.resolve("waypoint", "get", {
|
|
245
|
+
journeyId: "123",
|
|
246
|
+
waypointId: "456",
|
|
247
|
+
});
|
|
248
|
+
// Returns: '/journeys/123/waypoints/456'
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Local Development (Testing with bun link)
|
|
252
|
+
|
|
253
|
+
For testing the SDK locally before publishing:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# In the SDK package directory
|
|
257
|
+
cd packages/nuxt
|
|
258
|
+
bun link
|
|
259
|
+
|
|
260
|
+
# In your test project
|
|
261
|
+
cd ~/my-test-project
|
|
262
|
+
bun link @newschools/nuxt
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Add to your test project's `nuxt.config.ts`:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
export default defineNuxtConfig({
|
|
269
|
+
modules: ["@newschools/nuxt"],
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Environment Variables
|
|
274
|
+
|
|
275
|
+
| Variable | Description | Required |
|
|
276
|
+
| --------------------------------- | ------------------------------------- | -------- |
|
|
277
|
+
| `NUXT_PUBLIC_NEWSCHOOLS_API_KEY` | Your New Schools API key | Yes |
|
|
278
|
+
| `NUXT_PUBLIC_NEWSCHOOLS_BASE_URL` | API base URL (defaults to production) | No |
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT
|
|
283
|
+
|
|
284
|
+
## Support
|
|
285
|
+
|
|
286
|
+
For issues or questions:
|
|
287
|
+
|
|
288
|
+
- API documentation: https://newschools.ai/docs
|
|
289
|
+
- Contact: support@newschools.ai
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* New Schools SDK - Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Exports module, types, and utilities
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Export Nuxt module
|
|
8
|
+
export { default } from "./module";
|
|
9
|
+
|
|
10
|
+
// Export types
|
|
11
|
+
export type {
|
|
12
|
+
Journey,
|
|
13
|
+
JourneyTheme,
|
|
14
|
+
JourneyStatus,
|
|
15
|
+
Waypoint,
|
|
16
|
+
StandardWaypoint,
|
|
17
|
+
ProfileEnrichmentWaypoint,
|
|
18
|
+
WaypointType,
|
|
19
|
+
WaypointStatus,
|
|
20
|
+
LayoutMode,
|
|
21
|
+
Question,
|
|
22
|
+
Activity,
|
|
23
|
+
ActivityStatus,
|
|
24
|
+
GridPosition,
|
|
25
|
+
SuccessResponse,
|
|
26
|
+
ErrorResponse,
|
|
27
|
+
ApiResponse,
|
|
28
|
+
} from "./types";
|
|
29
|
+
|
|
30
|
+
// Export composable type for advanced usage
|
|
31
|
+
export type { useAsyncNewSchools } from "./runtime/composables/useAsyncNewSchools";
|
|
32
|
+
|
|
33
|
+
// Export client for manual usage (advanced)
|
|
34
|
+
export { createClient, NewSchoolsClient } from "./runtime/client";
|
|
35
|
+
export type { NewSchoolsClientConfig, RequestOptions } from "./runtime/client";
|
|
36
|
+
|
|
37
|
+
// Export path resolver for custom integrations (advanced)
|
|
38
|
+
export { NewSchoolsPathResolver } from "./runtime/utils/pathResolver";
|
|
39
|
+
export type {
|
|
40
|
+
ResourceAction,
|
|
41
|
+
ResourceType,
|
|
42
|
+
ResourceParams,
|
|
43
|
+
JourneyParams,
|
|
44
|
+
WaypointParams,
|
|
45
|
+
ActivityParams,
|
|
46
|
+
} from "./runtime/utils/pathResolver";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* New Schools Nuxt Module
|
|
3
|
+
*
|
|
4
|
+
* Nuxt module that provides composables and configuration for the New Schools SDK
|
|
5
|
+
* Auto-registers plugin and makes composables available globally
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
defineNuxtModule,
|
|
10
|
+
addPlugin,
|
|
11
|
+
createResolver,
|
|
12
|
+
addImportsDir,
|
|
13
|
+
addComponentsDir,
|
|
14
|
+
} from "@nuxt/kit";
|
|
15
|
+
|
|
16
|
+
export interface ModuleOptions {
|
|
17
|
+
/**
|
|
18
|
+
* New Schools API key
|
|
19
|
+
* Can also be set via NUXT_PUBLIC_NEWSCHOOLS_API_KEY env variable
|
|
20
|
+
*/
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Base URL for New Schools API
|
|
25
|
+
* Defaults to https://newschools.ai
|
|
26
|
+
*/
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Organization slug for auto-fetch and theming
|
|
31
|
+
* When set, SDK will automatically fetch organization data on init
|
|
32
|
+
* and apply brand colors as CSS variables
|
|
33
|
+
*
|
|
34
|
+
* @example 'my-organization'
|
|
35
|
+
*/
|
|
36
|
+
organizationSlug?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default defineNuxtModule<ModuleOptions>({
|
|
40
|
+
meta: {
|
|
41
|
+
name: "@newschools/nuxt",
|
|
42
|
+
configKey: "newschools",
|
|
43
|
+
compatibility: {
|
|
44
|
+
nuxt: "^3.0.0 || ^4.0.0",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
defaults: {
|
|
49
|
+
baseUrl: "https://newschools.ai",
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
setup(options, nuxt) {
|
|
53
|
+
const resolver = createResolver(import.meta.url);
|
|
54
|
+
|
|
55
|
+
// Vite optimizations (following Storyblok pattern)
|
|
56
|
+
if (nuxt.options.vite.optimizeDeps) {
|
|
57
|
+
nuxt.options.vite.optimizeDeps.exclude =
|
|
58
|
+
nuxt.options.vite.optimizeDeps.exclude || [];
|
|
59
|
+
nuxt.options.vite.optimizeDeps.exclude.push("fsevents");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Transpile runtime for proper bundling
|
|
63
|
+
nuxt.options.build.transpile.push(resolver.resolve("./runtime"));
|
|
64
|
+
nuxt.options.build.transpile.push("@newschools/nuxt");
|
|
65
|
+
|
|
66
|
+
// Add runtime config
|
|
67
|
+
nuxt.options.runtimeConfig.public.newschools = {
|
|
68
|
+
apiKey:
|
|
69
|
+
options.apiKey || process.env.NUXT_PUBLIC_NEWSCHOOLS_API_KEY || "",
|
|
70
|
+
baseUrl: options.baseUrl || "https://newschools.ai",
|
|
71
|
+
organizationSlug: options.organizationSlug || "",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Register plugin
|
|
75
|
+
addPlugin(resolver.resolve("./runtime/plugin"));
|
|
76
|
+
|
|
77
|
+
// Auto-import all composables from directory (Storyblok pattern)
|
|
78
|
+
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
79
|
+
|
|
80
|
+
// Auto-register public components globally
|
|
81
|
+
// Private components: prefix with _ or place in internal/ subdirectory
|
|
82
|
+
addComponentsDir({
|
|
83
|
+
path: resolver.resolve("./runtime/components"),
|
|
84
|
+
global: true,
|
|
85
|
+
pattern: "**/*.vue", // All .vue files
|
|
86
|
+
ignore: ["**/internal/**", "**/_*.vue"], // Exclude internal/ and _-prefixed
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Add CSS tokens to Nuxt's CSS array
|
|
90
|
+
nuxt.options.css.push(resolver.resolve("./runtime/styles/tokens.css"));
|
|
91
|
+
|
|
92
|
+
// Log configuration in development
|
|
93
|
+
if (nuxt.options.dev) {
|
|
94
|
+
console.log("[NewSchools Module] Registered");
|
|
95
|
+
console.log("[NewSchools Module] CSS tokens loaded");
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* New Schools API Client
|
|
3
|
+
*
|
|
4
|
+
* Core client for making authenticated requests to the New Schools v1 API
|
|
5
|
+
* Handles API key authentication, error handling, and response parsing
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ofetch } from "ofetch";
|
|
9
|
+
import type { SuccessResponse, ErrorResponse } from "../types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Client configuration options
|
|
13
|
+
*/
|
|
14
|
+
export interface NewSchoolsClientConfig {
|
|
15
|
+
/** API key for authentication (e.g., ns_live_xxxxx) */
|
|
16
|
+
apiKey: string;
|
|
17
|
+
|
|
18
|
+
/** Base URL for API requests (defaults to production) */
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
|
|
21
|
+
/** Optional origin for CORS validation */
|
|
22
|
+
origin?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Request options for API calls
|
|
27
|
+
*/
|
|
28
|
+
export interface RequestOptions {
|
|
29
|
+
/** HTTP method (defaults to GET) */
|
|
30
|
+
method?: "GET" | "POST" | "PUT" | "DELETE";
|
|
31
|
+
|
|
32
|
+
/** Request body for POST/PUT requests */
|
|
33
|
+
body?: unknown;
|
|
34
|
+
|
|
35
|
+
/** Additional headers */
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* New Schools API Client
|
|
41
|
+
* Provides type-safe methods for fetching learning content
|
|
42
|
+
*/
|
|
43
|
+
export class NewSchoolsClient {
|
|
44
|
+
private apiKey: string;
|
|
45
|
+
private baseUrl: string;
|
|
46
|
+
private origin?: string;
|
|
47
|
+
|
|
48
|
+
constructor(config: NewSchoolsClientConfig) {
|
|
49
|
+
this.apiKey = config.apiKey;
|
|
50
|
+
this.baseUrl = config.baseUrl || "https://newschools.ai";
|
|
51
|
+
this.origin = config.origin;
|
|
52
|
+
|
|
53
|
+
if (!this.apiKey) {
|
|
54
|
+
throw new Error("[NewSchools] API key is required");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Make authenticated request to v1 API
|
|
60
|
+
*
|
|
61
|
+
* @param endpoint - API endpoint path (e.g., '/journeys' or '/journeys/123')
|
|
62
|
+
* @param options - Request options
|
|
63
|
+
* @returns Unwrapped data from SuccessResponse
|
|
64
|
+
* @throws Error with message from ErrorResponse
|
|
65
|
+
*/
|
|
66
|
+
async request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
|
|
67
|
+
const url = `${this.baseUrl}/api/v1${endpoint}`;
|
|
68
|
+
|
|
69
|
+
const headers: Record<string, string> = {
|
|
70
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
71
|
+
"Content-Type": "application/json",
|
|
72
|
+
...options.headers,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Add origin if provided (for CORS validation)
|
|
76
|
+
if (this.origin) {
|
|
77
|
+
headers["Origin"] = this.origin;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const response = await ofetch<SuccessResponse<T> | ErrorResponse>(url, {
|
|
82
|
+
method: options.method || "GET",
|
|
83
|
+
headers,
|
|
84
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Handle error response
|
|
88
|
+
if (!response.success) {
|
|
89
|
+
const errorResponse = response as ErrorResponse;
|
|
90
|
+
throw new Error(errorResponse.error.message);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Return unwrapped data from success response
|
|
94
|
+
return (response as SuccessResponse<T>).data;
|
|
95
|
+
} catch (error: any) {
|
|
96
|
+
// Re-throw with improved error message
|
|
97
|
+
if (error.data && !error.data.success) {
|
|
98
|
+
const errorResponse = error.data as ErrorResponse;
|
|
99
|
+
throw new Error(`[NewSchools API] ${errorResponse.error.message}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw new Error(`[NewSchools API] ${error.message || "Request failed"}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* GET request helper
|
|
108
|
+
*/
|
|
109
|
+
async get<T>(endpoint: string): Promise<T> {
|
|
110
|
+
return this.request<T>(endpoint, { method: "GET" });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* POST request helper
|
|
115
|
+
*/
|
|
116
|
+
async post<T>(endpoint: string, body: unknown): Promise<T> {
|
|
117
|
+
return this.request<T>(endpoint, { method: "POST", body });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* PUT request helper
|
|
122
|
+
*/
|
|
123
|
+
async put<T>(endpoint: string, body: unknown): Promise<T> {
|
|
124
|
+
return this.request<T>(endpoint, { method: "PUT", body });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* DELETE request helper
|
|
129
|
+
*/
|
|
130
|
+
async delete<T>(endpoint: string): Promise<T> {
|
|
131
|
+
return this.request<T>(endpoint, { method: "DELETE" });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Create a new New Schools API client instance
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const client = createClient({
|
|
141
|
+
* apiKey: 'ns_live_xxxxx',
|
|
142
|
+
* baseUrl: 'https://newschools.ai' // optional
|
|
143
|
+
* });
|
|
144
|
+
*
|
|
145
|
+
* const journeys = await client.get('/journeys');
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function createClient(config: NewSchoolsClientConfig): NewSchoolsClient {
|
|
149
|
+
return new NewSchoolsClient(config);
|
|
150
|
+
}
|