@luanxdd/meowgallery 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,113 @@
1
- # Temporary Holding Version
1
+ # MeowGallery SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ TypeScript 7.0.2, ESM, Node.js 22+ e navegadores modernos com Fetch e AbortSignal. Sem dependências de produção.
4
+
5
+ ## Instalar
6
+
7
+ O pacote ainda não foi publicado. Instale o arquivo incluído nesta entrega:
8
+
9
+ ```bash
10
+ npm install ./meowgallery-sdk-0.2.0.tgz
11
+ ```
12
+
13
+ ## Obter uma publicação
14
+
15
+ ```typescript
16
+ import { MeowGallery } from "@meowgallery/sdk";
17
+
18
+ const gallery = new MeowGallery({
19
+ baseUrl: "https://gallery.seudominio.com",
20
+ token: process.env.MEOWGALLERY_KEY!,
21
+ });
22
+
23
+ const post = await gallery.capture("https://www.instagram.com/p/CODIGO_REAL/");
24
+
25
+ console.log(post.author.name);
26
+ console.log(post.caption);
27
+
28
+ for (const item of post.items) {
29
+ console.log(item.kind, item.width, item.height, item.delivery.expiresAt);
30
+ }
31
+ ```
32
+
33
+ `items` preserva a ordem do carrossel. `audio` contém a faixa de posts de fotos do TikTok quando disponível. Cada mídia tem um link próprio. URLs de origem e cookies não são retornados.
34
+
35
+ ## Baixar no consumidor Node
36
+
37
+ ```typescript
38
+ import { saveMedia } from "@meowgallery/sdk/node";
39
+
40
+ const item = post.items[0];
41
+
42
+ if (item) {
43
+ await saveMedia(gallery, item, "./publicacao.bin");
44
+ }
45
+ ```
46
+
47
+ A gravação acontece no consumidor. A API apenas repassa o stream. O helper limita bytes, remove arquivos parciais e não sobrescreve arquivos existentes. Escolha a extensão conforme o tipo real da mídia; `.bin` não altera o conteúdo.
48
+
49
+ Para encaminhar ao seu framework sem salvar:
50
+
51
+ ```typescript
52
+ const response = await gallery.download(post.items[0]!);
53
+ const stream = response.body;
54
+ ```
55
+
56
+ Consuma ou cancele o stream. Um intervalo pode ser solicitado com `gallery.download(item, { range: "bytes=0-1023" })`. A chave da API não é enviada no download: o próprio link é uma credencial temporária.
57
+
58
+ ## Fotos e áudio
59
+
60
+ Carrosséis preservam a ordem em `post.items`. Quando disponível, `post.audio`
61
+ contém a faixa separada, com seu próprio link temporário.
62
+
63
+ ```typescript
64
+ if (post.audio) {
65
+ await saveMedia(gallery, post.audio, "./audio.m4a");
66
+ }
67
+ ```
68
+
69
+ Confira `mimeType` para escolher a extensão. A SDK não converte fotos em vídeo
70
+ e não depende de FFmpeg. Na versão 0.2.0 foram removidos `createMotion`,
71
+ `MotionOptions` e o campo `composition`.
72
+
73
+ ## OAuth
74
+
75
+ ```typescript
76
+ const gallery = new MeowGallery({
77
+ baseUrl: "https://gallery.seudominio.com",
78
+ token: async () => auth.getAccessToken(),
79
+ });
80
+ ```
81
+
82
+ `auth` representa o SDK do seu provedor de identidade, responsável por login, PKCE e renovação. A função é chamada antes de cada operação autenticada. Não coloque chaves de bot ou Owner no código público do site.
83
+
84
+ ## Cancelamento e erros
85
+
86
+ ```typescript
87
+ import { MeowGalleryError } from "@meowgallery/sdk";
88
+
89
+ try {
90
+ const post = await gallery.capture(url, {
91
+ signal: AbortSignal.timeout(30_000),
92
+ });
93
+ } catch (error) {
94
+ if (error instanceof MeowGalleryError) {
95
+ console.error(error.code, error.message, error.requestId);
96
+ } else {
97
+ throw error;
98
+ }
99
+ }
100
+ ```
101
+
102
+ O erro contém `code`, `status`, `requestId` e `retryAfter`. POSTs não são repetidos automaticamente, para não duplicar emissões de links e consumo de cota. Ao expirar, solicite a publicação novamente. Se a origem expirar antes do link, use `gallery.capture(url, { refresh: true })` para ignorar o cache.
103
+
104
+ ## Desenvolvimento
105
+
106
+ ```bash
107
+ npm ci
108
+ npm run check
109
+ npm test
110
+ npm pack
111
+ ```
112
+
113
+ Os aliases internos estão declarados em `package.json#imports`; o consumidor não precisa configurar `tsconfig.paths`. A entrada `/node` não deve entrar em bundles de navegador.
@@ -0,0 +1,14 @@
1
+ import type { Capture, CreatedCredential, Credential, CredentialInput, Media, MeowGalleryOptions, RequestOptions } from "#types";
2
+ export declare class MeowGallery {
3
+ #private;
4
+ constructor(options: MeowGalleryOptions);
5
+ capture(url: string, options?: RequestOptions & {
6
+ readonly refresh?: boolean;
7
+ }): Promise<Capture>;
8
+ createCredential(input: CredentialInput, options?: RequestOptions): Promise<CreatedCredential>;
9
+ listCredentials(options?: RequestOptions): Promise<Credential[]>;
10
+ revokeCredential(id: string, options?: RequestOptions): Promise<void>;
11
+ download(media: Media, options?: RequestOptions & {
12
+ readonly range?: string;
13
+ }): Promise<Response>;
14
+ }
package/dist/client.js ADDED
@@ -0,0 +1,181 @@
1
+ import { MeowGalleryError } from "#error";
2
+ export class MeowGallery {
3
+ #origin;
4
+ #token;
5
+ #fetch;
6
+ #timeout;
7
+ constructor(options) {
8
+ const url = new URL(options.baseUrl);
9
+ const local = ["localhost", "127.0.0.1"].includes(url.hostname);
10
+ if ((url.protocol !== "https:" &&
11
+ !(options.allowLocalHttp && local && url.protocol === "http:")) ||
12
+ url.username ||
13
+ url.password ||
14
+ url.pathname !== "/" ||
15
+ url.search ||
16
+ url.hash) {
17
+ throw new TypeError("baseUrl deve ser uma origem HTTPS válida.");
18
+ }
19
+ this.#origin = url.origin;
20
+ this.#token = options.token;
21
+ this.#fetch = options.fetch ?? globalThis.fetch;
22
+ this.#timeout = options.timeoutMs ?? 35_000;
23
+ if (!Number.isSafeInteger(this.#timeout) ||
24
+ this.#timeout < 1 ||
25
+ this.#timeout > 300_000) {
26
+ throw new TypeError("timeoutMs deve estar entre 1 e 300000.");
27
+ }
28
+ }
29
+ capture(url, options = {}) {
30
+ return this.#request("POST", "/v1/captures", options.refresh ? { url, refresh: true } : { url }, options);
31
+ }
32
+ createCredential(input, options = {}) {
33
+ return this.#request("POST", "/v1/credentials", input, options);
34
+ }
35
+ listCredentials(options = {}) {
36
+ return this.#request("GET", "/v1/credentials", undefined, options);
37
+ }
38
+ revokeCredential(id, options = {}) {
39
+ if (!/^[0-9a-f-]{36}$/i.test(id)) {
40
+ throw new TypeError("Identificador de credencial inválido.");
41
+ }
42
+ return this.#request("DELETE", `/v1/credentials/${id}`, undefined, options);
43
+ }
44
+ async download(media, options = {}) {
45
+ const url = new URL(media.delivery.url);
46
+ if (url.origin !== this.#origin ||
47
+ !/^\/v1\/delivery\/[A-Za-z0-9_-]+$/.test(url.pathname) ||
48
+ url.username ||
49
+ url.password ||
50
+ url.search ||
51
+ url.hash) {
52
+ throw new TypeError("O link de entrega não pertence a esta MeowGallery.");
53
+ }
54
+ const expires = Date.parse(media.delivery.expiresAt);
55
+ if (!Number.isFinite(expires) || expires <= Date.now()) {
56
+ throw new MeowGalleryError("O link expirou. Solicite a publicação novamente.", { code: "delivery_expired", status: 410 });
57
+ }
58
+ const headers = new Headers();
59
+ if (options.range) {
60
+ headers.set("Range", options.range);
61
+ }
62
+ const response = await this.#send(url.href, {
63
+ headers,
64
+ signal: this.#signal(options.signal, 300_000),
65
+ });
66
+ if (!response.ok) {
67
+ throw await this.#failure(response);
68
+ }
69
+ return response;
70
+ }
71
+ async #request(method, path, body, options) {
72
+ const token = typeof this.#token === "function" ? await this.#token() : this.#token;
73
+ if (!token || /[\r\n]/.test(token)) {
74
+ throw new TypeError("Token de autenticação inválido.");
75
+ }
76
+ const headers = new Headers({
77
+ Accept: "application/json",
78
+ Authorization: `Bearer ${token}`,
79
+ });
80
+ const init = {
81
+ method,
82
+ headers,
83
+ signal: this.#signal(options.signal, this.#timeout),
84
+ };
85
+ if (body !== undefined) {
86
+ headers.set("Content-Type", "application/json");
87
+ init.body = JSON.stringify(body);
88
+ }
89
+ const response = await this.#send(`${this.#origin}${path}`, init);
90
+ if (!response.ok) {
91
+ throw await this.#failure(response);
92
+ }
93
+ if (response.status === 204) {
94
+ return undefined;
95
+ }
96
+ const envelope = await this.#json(response);
97
+ if (!envelope || typeof envelope !== "object" || !("data" in envelope)) {
98
+ throw new MeowGalleryError("Resposta inesperada do serviço.", {
99
+ code: "invalid_response",
100
+ status: response.status,
101
+ });
102
+ }
103
+ return envelope.data;
104
+ }
105
+ #signal(signal, timeout) {
106
+ const deadline = AbortSignal.timeout(timeout);
107
+ return signal ? AbortSignal.any([signal, deadline]) : deadline;
108
+ }
109
+ async #send(url, init) {
110
+ try {
111
+ return await this.#fetch(url, {
112
+ ...init,
113
+ redirect: "error",
114
+ credentials: "omit",
115
+ cache: "no-store",
116
+ });
117
+ }
118
+ catch {
119
+ throw new MeowGalleryError(init.signal?.aborted
120
+ ? "Solicitação cancelada ou expirada."
121
+ : "Não foi possível conectar à MeowGallery.", { code: init.signal?.aborted ? "request_aborted" : "network_error" });
122
+ }
123
+ }
124
+ async #failure(response) {
125
+ const body = (await this.#json(response).catch(() => null));
126
+ const retryHeader = response.headers.get("retry-after");
127
+ const retry = retryHeader === null ? NaN : Number(retryHeader);
128
+ return new MeowGalleryError(typeof body?.error?.message === "string"
129
+ ? body.error.message
130
+ : "A solicitação não foi concluída.", {
131
+ code: typeof body?.error?.code === "string"
132
+ ? body.error.code
133
+ : "http_error",
134
+ status: response.status,
135
+ requestId: response.headers.get("x-request-id"),
136
+ retryAfter: Number.isFinite(retry) && retry >= 0 ? retry : null,
137
+ });
138
+ }
139
+ async #json(response) {
140
+ const reader = response.body?.getReader();
141
+ if (!reader) {
142
+ throw new MeowGalleryError("Resposta vazia do serviço.", {
143
+ code: "invalid_response",
144
+ });
145
+ }
146
+ const chunks = [];
147
+ let size = 0;
148
+ try {
149
+ for (;;) {
150
+ const { done, value } = await reader.read();
151
+ if (done) {
152
+ break;
153
+ }
154
+ size += value.byteLength;
155
+ if (size > 1_048_576) {
156
+ await reader.cancel();
157
+ throw new Error("Response limit exceeded");
158
+ }
159
+ chunks.push(value);
160
+ }
161
+ const joined = new Uint8Array(size);
162
+ let offset = 0;
163
+ for (const chunk of chunks) {
164
+ joined.set(chunk, offset);
165
+ offset += chunk.byteLength;
166
+ }
167
+ return JSON.parse(new TextDecoder().decode(joined));
168
+ }
169
+ catch {
170
+ throw new MeowGalleryError("Resposta inválida ou interrompida do serviço.", {
171
+ code: "invalid_response",
172
+ status: response.status,
173
+ requestId: response.headers.get("x-request-id"),
174
+ });
175
+ }
176
+ finally {
177
+ reader.releaseLock();
178
+ }
179
+ }
180
+ }
181
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare class MeowGalleryError extends Error {
2
+ readonly code: string;
3
+ readonly status: number;
4
+ readonly requestId: string | null;
5
+ readonly retryAfter: number | null;
6
+ constructor(message: string, details: {
7
+ code: string;
8
+ status?: number;
9
+ requestId?: string | null;
10
+ retryAfter?: number | null;
11
+ });
12
+ }
package/dist/error.js ADDED
@@ -0,0 +1,15 @@
1
+ export class MeowGalleryError extends Error {
2
+ code;
3
+ status;
4
+ requestId;
5
+ retryAfter;
6
+ constructor(message, details) {
7
+ super(message);
8
+ this.name = "MeowGalleryError";
9
+ this.code = details.code;
10
+ this.status = details.status ?? 0;
11
+ this.requestId = details.requestId ?? null;
12
+ this.retryAfter = details.retryAfter ?? null;
13
+ }
14
+ }
15
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAS;IACb,MAAM,CAAS;IACf,SAAS,CAAgB;IACzB,UAAU,CAAgB;IAEnC,YACE,OAAe,EACf,OAKC;QAED,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAC/C,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export { MeowGallery } from "#client";
2
+ export { MeowGalleryError } from "#error";
3
+ export type { Capture, CreatedCredential, Credential, CredentialInput, Media, MeowGalleryOptions, RequestOptions, Scope, } from "#types";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { MeowGallery } from "#client";
2
+ export { MeowGalleryError } from "#error";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC"}
package/dist/node.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { MeowGallery } from "#client";
2
+ import type { Media, RequestOptions } from "#types";
3
+ export interface SaveOptions extends RequestOptions {
4
+ readonly maxBytes?: number;
5
+ }
6
+ export declare function saveMedia(client: MeowGallery, media: Media, destination: string, options?: SaveOptions): Promise<string>;
package/dist/node.js ADDED
@@ -0,0 +1,37 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { link, mkdtemp, rm } from "node:fs/promises";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { Readable, Transform } from "node:stream";
5
+ import { pipeline } from "node:stream/promises";
6
+ export async function saveMedia(client, media, destination, options = {}) {
7
+ const target = resolve(destination);
8
+ const directory = await mkdtemp(join(dirname(target), ".meowgallery-"));
9
+ const temporary = join(directory, "media");
10
+ const limit = options.maxBytes ?? 256 * 1024 * 1024;
11
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
12
+ await rm(directory, { recursive: true, force: true });
13
+ throw new TypeError("maxBytes deve ser um inteiro positivo.");
14
+ }
15
+ try {
16
+ const response = await client.download(media, options);
17
+ if (!response.body) {
18
+ throw new Error("O serviço retornou uma mídia vazia.");
19
+ }
20
+ let total = 0;
21
+ const bounded = new Transform({
22
+ transform(chunk, _encoding, callback) {
23
+ total += chunk.length;
24
+ callback(total > limit
25
+ ? new Error("A mídia excede o limite de tamanho.")
26
+ : null, chunk);
27
+ },
28
+ });
29
+ await pipeline(Readable.fromWeb(response.body), bounded, createWriteStream(temporary, { flags: "wx", mode: 0o600 }), { signal: options.signal });
30
+ await link(temporary, target);
31
+ return target;
32
+ }
33
+ finally {
34
+ await rm(directory, { recursive: true, force: true });
35
+ }
36
+ }
37
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAShD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAmB,EACnB,KAAY,EACZ,WAAmB,EACnB,OAAO,GAAgB,EAAE;IAEzB,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC;YAC5B,SAAS,CAAC,KAAa,EAAE,SAAS,EAAE,QAAQ;gBAC1C,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;gBACtB,QAAQ,CACN,KAAK,GAAG,KAAK;oBACX,CAAC,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC;oBAClD,CAAC,CAAC,IAAI,EACR,KAAK,CACN,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,QAAQ,CACZ,QAAQ,CAAC,OAAO,CACd,QAAQ,CAAC,IAA4D,CACtE,EACD,OAAO,EACP,iBAAiB,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAC1D,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAC3B,CAAC;QACF,MAAM,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC9B,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
@@ -0,0 +1,56 @@
1
+ export type Scope = "captures:read" | "delivery:read" | "sources:authenticated";
2
+ export interface Media {
3
+ readonly id: string;
4
+ readonly kind: "image" | "video" | "audio";
5
+ readonly mimeType: string;
6
+ readonly width: number | null;
7
+ readonly height: number | null;
8
+ readonly quality: {
9
+ readonly selection: "best_available";
10
+ readonly hd: boolean | null;
11
+ readonly verified: boolean;
12
+ };
13
+ readonly delivery: {
14
+ readonly url: string;
15
+ readonly expiresAt: string;
16
+ };
17
+ }
18
+ export interface Capture {
19
+ readonly id: string;
20
+ readonly platform: "instagram" | "tiktok";
21
+ readonly caption: string;
22
+ readonly author: {
23
+ readonly username: string;
24
+ readonly name: string;
25
+ };
26
+ readonly items: readonly Media[];
27
+ readonly audio: Media | null;
28
+ }
29
+ export interface CredentialInput {
30
+ readonly name: string;
31
+ readonly scopes: readonly Scope[];
32
+ readonly requestsPerMinute: number;
33
+ readonly subject?: string;
34
+ }
35
+ export interface Credential {
36
+ readonly id: string;
37
+ readonly name: string;
38
+ readonly scopes: readonly Scope[];
39
+ readonly requestsPerMinute: number;
40
+ readonly isAdmin: boolean;
41
+ }
42
+ export interface CreatedCredential extends Omit<CredentialInput, "subject"> {
43
+ readonly id: string;
44
+ readonly key: string | null;
45
+ readonly subject: string | null;
46
+ }
47
+ export interface RequestOptions {
48
+ readonly signal?: AbortSignal;
49
+ }
50
+ export interface MeowGalleryOptions {
51
+ readonly baseUrl: string;
52
+ readonly token: string | (() => string | Promise<string>);
53
+ readonly timeoutMs?: number;
54
+ readonly allowLocalHttp?: boolean;
55
+ readonly fetch?: typeof fetch;
56
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@luanxdd/meowgallery",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Uma interface simples para capturar e entregar mídias com MeowGallery.",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "engines": {
8
+ "node": ">=22"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ },
19
+ "./node": {
20
+ "types": "./dist/node.d.ts",
21
+ "import": "./dist/node.js"
22
+ }
23
+ },
24
+ "imports": {
25
+ "#types": {
26
+ "types": "./dist/types.d.ts",
27
+ "default": "./dist/types.js"
28
+ },
29
+ "#error": {
30
+ "types": "./dist/error.d.ts",
31
+ "default": "./dist/error.js"
32
+ },
33
+ "#client": {
34
+ "types": "./dist/client.d.ts",
35
+ "default": "./dist/client.js"
36
+ }
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "check": "tsc -p tsconfig.json --noEmit",
41
+ "test": "npm run build && node --test test/*.test.mjs",
42
+ "prepack": "npm run build"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^24.0.0",
46
+ "typescript": "7.0.2"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }