@m3r/sdk 0.5.0

Ciclo de vida da extensão

Validate, build, deploy assinado, activate, deactivate e gestão no tenant.

A extensão não é um plugin dentro do processo do ERP. É um processo Node isolado. O Core guarda o contrato (tabelas, telas, IPs). O runtime chama activate e deactivate.

código → m3r validate → m3r build (.m3rx) → credencial RUN → Runtime Host → activate(ctx)
                                                                              │
                                                                    telas / IPs / records
                                                                              │
                                                              Stop / Ctrl+C → deactivate(ctx)

1. Escrever

Pasta com m3r.json + src/index.ts. defineExtension exporta activate e, se quiser, deactivate. O index orquestra — cada domínio registra o próprio contrato. Padrões.

No activate o domínio declara o que o tenant passa a ter, nesta ordem: tables.ensure → screens.define → nav.define → reports.define → entryPoints.on → events.on.

deactivate não apaga tabela, tela nem dado. Só libera o processo (subscriptions, log). Soft delete e FAX#### ficam no banco.

2. Validate

m3r validate / M3R: Validate Project.

  • Catálogo: permissions, events, entryPoints existem.
  • tables[] / screens[] / nav[] / reports[] do m3r.json batem 1:1 com o código.
  • Imports proibidos (SQL, TypeORM, React no ERP).

Ainda não cria tabela no Postgres.

3. Build

m3r build gera:

  • .m3r/build/bundle/index.js — só o seu código (@m3r/sdk fica externo)
  • metadata + integrity/sha256.json
  • <id>-<version>.m3rx — zip determinístico

Credencial BUILD (extension.build / extension.package). Não chama ctx.m3r. Não tem executionId de negócio.

O hash local vai em integrity/sha256.json. A assinatura do Core só nasce no deploy (integrity/signature.json depois do POST).

4. Run (desenvolvimento) = ativar

VS Code / Cursor: M3R: Run Extension, ou m3r run.

  1. Build de desenvolvimento + checagem de integridade.
  2. Core emite credencial RUN (scopes do m3r.json, TTL ~1h, executionId).
  3. Runtime Host sobe em processo filho (fork). A extensão não roda dentro do VS Code.
  4. Host cria ctx a partir da credencial — tenant/empresa vêm do token, não do seu código.
  5. Chama activate(ctx).
  6. Se você registrou IPs, o host sobe HTTP em 127.0.0.1 e grava o callback em FADEV04.

Estado: STARTING → RUNNING.

Aí o ensure acontece de verdade: JSONB em FADEV07 ou CREATE TABLE "FAX####". Telas aparecem em /apps/extensions/{code}. IPs passam a ser invocados no save/faturar.

activate
export default defineExtension({
  async activate(ctx) {
    const lote = await ctx.ext.tables.ensure({
      name: "demo_lote",
      storage: SdkTableStorage.Physical
      // physicalName omitido → Core devolve FAX0001 (ou o próximo livre)
    });
    ctx.logger.info(lote.physicalName);
    ctx.subscriptions.add(
      ctx.entryPoints.on(SdkEntryPoint.SalesOrderBeforeSave, () => ({ allow: true }))
    );
  },
  async deactivate(ctx) {
    ctx.logger.info("parando");
  }
});

5. Stop = desativar

M3R: Stop Extension ou Ctrl+C.

deactivate(ctx)          ← timeout; se travar, SIGKILL
ctx.subscriptions.disposeAll()
para o poll de eventos
revoga a credencial RUN
processo filho sai

O que não acontece no stop:

  • Não dropa FAX####.
  • Não apaga linhas.
  • Não remove tela/menu do ERP (o schema declarado continua no tenant até outro ensure ou ação de admin).
  • IPs deixam de responder: o callback local morreu. O Core trata timeout conforme failPolicy do IP (open segue, closed bloqueia).

6. Ambientes — o que isola de verdade

m3r run não é deploy. É “ligar o processo da extensão no laptop contra o tenant que você selecionou”. Qualquer um com Sign In + acesso à empresa consegue fazer isso. Não há fila, revisão nem versão instalada.

O seletor Development / Staging / Production no VS Code (FADEV02) não cria três bancos. Os três tipos nascem na mesma empresa. Tabela, tela, FAX#### e records são isolados por companyId + extensionId — não por environmentId. Rodar em “Production” da empresa X grava no mesmo lugar que “Development” da empresa X.

O que você escolheO que realmente isolaO que não isola
m3r.developerApiUrlStack (API/banco). Dev local ≠ HML ≠ prod.—
Organization (empresa)Tenant no banco daquela API—
Environment type (FADEV02)Política e rótulo (DEV / HML / PROD)Dado. Mesmo companyId = mesmo FADEV06 / FAX####
m3r runProcesso no seu laptop + callback 127.0.0.1Instalação permanente, versão, sandbox

Como distinguir os três mundos de verdade:

  1. Desenvolvimento — API local (ou stack de dev) + empresa de sandbox. Environment development. m3r run o dia inteiro.
  2. HML — outra API ou outra empresa, não o label Staging na empresa de prod. Environment staging. Run só se a URL do developer apontar para o Core de HML.
  3. Produção — Core de produção + empresa real. Obrigatório: m3r build → m3r deploy / M3R: Deploy Extension. O Core re-hash o .m3rx, assina (HMAC-SHA256) e grava em FADEV11/FADEV12. Sem esse install, a credencial RUN é recusada.

Regra prática: stack + empresa separam ambiente. O dropdown FADEV02 sozinho não separa.

7. Deploy — o Core toma conhecimento

m3r validate → m3r build (.m3rx + sha256)
       → POST /developer/packages
       → Core recalcula SHA-256
       → Core assina HMAC-SHA256 (segredo M3R_EXTENSION_SIGNING_SECRET)
       → FADEV11 pacote + FADEV12 install no ambiente selecionado
       → integrity/signature.json local

Comando no editor: M3R: Deploy Extension. CLI: m3r deploy com M3R_API_URL, M3R_DEVELOPER_TOKEN, M3R_ORGANIZATION_ID, M3R_ENVIRONMENT_ID.

Versão é imutável: mesmo version com SHA diferente é recusado — incremente m3r.json.

AmbienteRun sem deployDeploy
development / stagingpermitidoopcional (recomendado em HML)
productionbloqueado até existir installobrigatório

Ainda não é hosting permanente (container/microVM). Depois do deploy, o Core conhece o binário assinado. O processo no laptop só roda em prod se esse install existir.

No ERP, Configurações → Extensões lista todas (ativas e inativas), mostra logs de IP (FADEV05) e permite ativar/desativar (FADEV13). Desativar não apaga FAX#### nem records.

PeçaStatus
Registry FADEV11 / install FADEV12disponível
Assinatura HMAC-SHA256 pelo Coredisponível
m3r deploy / M3R: Deploy Extensiondisponível
Marketplace / billingnão
Host https 24/7 da extensãonão (próximo passo)

8. Credenciais

PurposePodeNão pode
BUILD / TESTvalidate, build, packagectx.m3r, ctx.ext.records
RUNscopes do manifest (sales.order.read, extension.storage.*)scopes de build
JWT developerm3r deploy / publishsubstitui um purpose DEPLOY dedicado — ainda não há credencial só de publish

O token RUN não entra no seu activate. O HttpTransport lê M3R_RUN_TOKEN no processo do host.

9. Tabela física nesse ciclo

  • ensure no activate é idempotente: mesma lógica, mesmo physicalName.
  • Sem physicalName: Core gera FAX0001… e devolve { storage, physicalName }.
  • Com physicalName: "FAX0042": usa esse código se estiver livre. Não muda depois.
  • ctx.ext.tables.list() relê o que o Core gravou (incluindo o nome atribuído).
  • Stop / deactivate não derruba a FAX####.