# @xtrable-ltd/nanoesis

A static-first **publishing compiler**: author content in a calm editor, compile it to plain
static HTML at publish time, and ship files with no runtime, no server, and nothing of the
tooling surviving into production.

This one package contains everything an adopter needs (DESIGN §11c): the engine, the
mountable editor API, the storage / image / auth adapters, and the editor UI bundle, each
reachable as a subpath import.

```bash
npm install @xtrable-ltd/nanoesis
```

## What you import

```ts
import { compile, type BlobStore }     from '@xtrable-ltd/nanoesis';                 // the engine
import { handleApi, type ApiDeps }     from '@xtrable-ltd/nanoesis/editor-api';       // mount in your host
import { FsBlobStore, FsArtifactSink } from '@xtrable-ltd/nanoesis/adapter-fs';       // a local-folder store
import { createSharpEncoder }          from '@xtrable-ltd/nanoesis/adapter-sharp';    // responsive <picture>
import { ShellPreBuildHook }           from '@xtrable-ltd/nanoesis/adapter-shell';    // pre-publish build hook
import { editorDist }                  from '@xtrable-ltd/nanoesis/editor';           // path to the SPA you serve
```

Also available: `/adapter-azure-blob`, `/adapter-cloudflare`, `/adapter-local-jwt`,
`/adapter-trusted-header`.

## The whole integration

A nanoesis host is two things: a **store** the editor reads and writes through (`get` /
`put` / `delete`), and a **publish** step that compiles the working files to a static site.
Mount the API on any Node server and serve the bundled editor:

```ts
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';

import { handleApi } from '@xtrable-ltd/nanoesis/editor-api';
import { IndexedStore, contentTypeFor, publishSite } from '@xtrable-ltd/nanoesis';
import { FsBlobStore, FsArtifactSink } from '@xtrable-ltd/nanoesis/adapter-fs';
import { createSharpEncoder } from '@xtrable-ltd/nanoesis/adapter-sharp';
import { editorDist } from '@xtrable-ltd/nanoesis/editor';

const store = new IndexedStore(new FsBlobStore('site')); // your content lives in ./site
const sink = new FsArtifactSink('published'); // the compiled static site lands in ./published

const deps = {
  store,
  identity: { authenticate: async () => ({ userId: 'local', username: 'local', roles: ['admin'] }) },
  publish: async () => {
    await sink.wipe();
    return publishSite(store, sink, { imageEncoder: createSharpEncoder() });
  },
};

createServer((req, res) => {
  /* /api/* -> handleApi(deps, ...); everything else -> serve files from editorDist */
}).listen(7071);
```

A complete, runnable version of this is in the repo's `examples/minimal-host`. For a real
deployment swap `FsBlobStore` for `/adapter-azure-blob` (or your own `get`/`put`/`delete`
store), and the local-admin stub for `/adapter-local-jwt` or `/adapter-trusted-header`.

## How it ships

- **The editor** (`editorDist`) is a static SPA you serve from your host.
- **The published site** (whatever `publishSite` writes to your sink) is plain static files;
  point any static host or CDN at them. Production runs no nanoesis code.

MIT © Xtrable Ltd.
