0.1.4 • Published 13 days ago

@tanglemedia/svelte-starter-core v0.1.4

Weekly downloads
-
License
MIT
Repository
-
Last release
13 days ago

Core

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

optionrequirednote
config.loaderNoFunction that returns a ConfigLoaderInterface. Uses the yml loader by default
config.envNoFunction that returns the env payload. By default it uses svelte's $env/dynamic/public
config.aliasNoTrue by default. Assumes you have defined an alias for "$config" otherwise it will assume your configs are loaded from /src/config/*/.yml
service.provider.registerNo
service.provider.serviceOptionsNoThis will be applied to all services that was configured with configureService
service.provider.serviceOptions.pathYesThe path of the url for the api. Url is derived from the api.yml config baseUrl
service.provider.serviceOptions.pathPrefixNoNot supported
service.provider.serviceOptions.transformNoTransforms the response body
service.provider.serviceOptions.transformQueryNoTransforms the request query
service.provider.serviceOptions.transformMetaNoTransforms meta information
service.provider.serviceOptions.throwErrorNoDefaults 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.validateStatusNoOverwrite the method used to determine errors. (status: number) => boolean
service.provider.serviceOptions.errorHandlerNoBy 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.pluginNoAn 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.

0.1.4

13 days ago

0.1.3

2 months ago

0.1.0

3 months ago

0.1.2

3 months ago

0.1.1

3 months ago

0.0.19

3 months ago

0.0.18

3 months ago

0.0.17

3 months ago

0.0.16

4 months ago

0.0.15

4 months ago

0.0.14

4 months ago

0.0.12

4 months ago

0.0.13

4 months ago

0.0.11

5 months ago

0.0.10

5 months ago

0.0.9

5 months ago

0.0.8

5 months ago

0.0.7

5 months ago

0.0.6

5 months ago

0.0.3

5 months ago

0.0.2

6 months ago

0.0.5

5 months ago

0.0.4

5 months ago

0.0.1

6 months ago