This package is experimental and is meant for internal use only.
This is the entry point for initialing the application.
import { createApplication } from '@tanglemedia/svelte-starter-core';
const { application, configureService, getConfiguration, provideConfig, handle } =
createApplication({
/** options */
});
option | required | note |
---|---|---|
config.loader | No | Function that returns a ConfigLoaderInterface. Uses the yml loader by default |
config.env | No | Function that returns the env payload. By default it uses svelte's $env/dynamic/public |
config.alias | No | True by default. Assumes you have defined an alias for "$config" otherwise it will assume your configs are loaded from /src/config/*/.yml |
service.provider.register | No | |
service.provider.serviceOptions | No | This will be applied to all services that was configured with configureService |
service.provider.serviceOptions.path | Yes | The path of the url for the api. Url is derived from the api.yml config baseUrl |
service.provider.serviceOptions.pathPrefix | No | Not supported |
service.provider.serviceOptions.transform | No | Transforms the response body |
service.provider.serviceOptions.transformQuery | No | Transforms the request query |
service.provider.serviceOptions.transformMeta | No | Transforms meta information |
service.provider.serviceOptions.throwError | No | Defaults to true. If it isn't a falsy value (null/0/false etc) then validateStatus will determine if the response is invalid |
service.provider.serviceOptions.validateStatus | No | Overwrite the method used to determine errors. (status: number) => boolean
|
service.provider.serviceOptions.errorHandler | No | By default, the system will throw an exception. If you want to catch a specific error code such as a 401 to redirect to login, you can specify this here. For ALL errors, use 0 as the status code. |
application.plugin | No | An array of plugin providers |
Services is a simple abstraction layer that makes some assumptions around common api requests and responses. Sevices rely on an underlying adapter which is responsible for transforming the request/response payloads and handling the actual requests to an api. Once you application has been configured, you can easily configure services like so. Note that boot exports the return values from createApplication
.
import { ServiceAbstract } from '@tanglemedia/svelte-starter-core';
import type { User } from './schema/animal-color.interface';
import { configureService } from '../boot';
export class UserService extends ServiceAbstract<User> {}
const userService = configureService<User>(UserService, {
path: 'users',
transform: (payload) => {
/** do something to your payload */
return { ...payload };
}
// choose the adapter if it's a custom api
// adapterKey: 'tenant'
});
// now you can query the api
userService.find({ filter: { email: 'user@example.com' } });
You can catch all service errors via the configs in createApplication
. This is useful for catching a 401
status and redirecting to the log in screen.
import { createApplication } from '@tanglemedia/svelte-starter-core';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
createApplication({
/**
* Handle global redirects
*/
errorHandler: {
// 0: () => console.log('Catch all errors')
401: () => {
let redirectUrl: string | null = null;
const unsub = page.subscribe((page) => {
redirectUrl = `/auth/login?_redirect=${page.url.pathname}`;
});
if (redirectUrl) {
goto(redirectUrl);
}
unsub();
}
}
});
Api adapters run the underlying logic for services. If your api uses an SDK you might need to create a custom adapter. Start by creating an api provider function.
import type {
AdapterProviderRegister,
ApiAdapterInterface,
ApiAdapterProviderInterface
} from '@tanglemedia/svelte-starter-core';
import { MyCustomApiAdapter } from './my-adapter.ts';
type MyApiConfig = {};
class MyCustomApiProvider implements ApiAdapterProviderInterface<MyApiConfig> {
async loadAdapter(key?: string, config?: MyApiConfig): Promise<ApiAdapterInterface<MyApiConfig>> {
if (!config) {
throw new Error('Missing adapter configuration');
}
const client = this.createDirectusClient<SchemaShape>(config);
return new MyCustomApiAdapter(config, client);
}
}
export const customAdapter = (): AdapterProviderRegister => ({
name: 'my-custom-adapter',
provider: new MyCustomApiProvider()
});
Next, define the actual api adapter. Make sure the class conforms with the ApiAdapterInterface
interface.
import { ApiAdapterAbstract } from '@tanglemedia/svelte-starter-core';
export class MyCustomApiAdapter extends ApiAdapterAbstract<MyApiConfig> {
/** Implement the necessary interface methods and abstracts */
}
Users can register your api adapter like so
import { createApplication } from '@tanglemedia/svelte-starter-core';
import { customAdapter } from 'my-custom-api';
createApplication({
service: {
provider: { register: [customAdapter()] }
}
});
The main reason to author your own plugin is to add and enrich functionality of the application with the use of the configuration setup. To get started you'll need to define an function which provides the following
import MyComponent from './MyComponent.svelte'
export const myPlugin = () => ({
name: 'my-plugin'
root: 'parent-plugin',
component: MyComponent,
boot: () => {
// after plugin is registered
},
handle: () => {
return (event, resolve) => {
// this hook is called server side
return event(resolve)
}
}
})
An arbitrary name for your plugin. This and the "root" needs to be unique because a combination of both is used as a key for lookup.
If you are writing an "adapter" for a specific plugin, you may specify the root plugin name. You plugin will now be scoped under this root plugin.
Components requires an app
property to be declared as a prop. It also requires a <slot />
. If the slot isn't there, your application WILL not load properly
<script>
import { type ConfiguredApplication } from '@tanglemedia/svelte-starter-core';
export let app: ConfiguredApplication;
</script>
<slot />
Provide a component to be loaded with your application. There are two ways to specify how you want to load the component. You MUST specify one or the other and NOT both. The example above shows loading the component by providing it directly. You may also load the component via a factory which is an async function that returns the compoent.
export const myPlugin = () => ({
name: 'my-plugin'
root: 'parent-plugin',
componentFactory: () => import('./MyComponent.svelte').then((m) => m.default)
})
Called after the plugin is registerd
This should return a function that conforms to svelte's hook function.