@tanglemedia/svelte-starter-core
TypeScript icon, indicating that this package has built-in type declarations

0.5.0 • Public • Published

@tanglemedia/svelte-starter-core

TypeScript NPM Version Repo Bitbucket Bitbucket open pull requests NPM License

This package is experimental and is meant for internal use only.

Application

This is the entry point for initialing the application.

import { createApplication } from '@tanglemedia/svelte-starter-core';

const { application, configureService, getConfiguration, provideConfig, handle } =
  createApplication({
    /** options */
  });

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

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' } });

Global Error handling

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();
    }
  }
});

Authoring Api Adapters

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()] }
  }
});

Authoring plugins

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)
    }
  }
})

Name - Required

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.

Root - Optional

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.

Component and ComponentFactory

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)
})

Boot - optional

Called after the plugin is registerd

Handle - optional

This should return a function that conforms to svelte's hook function.

For mode demos and to check out all the components, please visit our documentation website

Readme

Keywords

none

Package Sidebar

Install

npm i @tanglemedia/svelte-starter-core

Weekly Downloads

71

Version

0.5.0

License

MIT

Unpacked Size

184 kB

Total Files

217

Last publish

Collaborators

  • tanglemedia-dev