@venturekit-pro/social 0.0.0-dev.20260602192622
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/LICENSE +191 -0
- package/README.md +134 -0
- package/dist/adapter.d.ts +46 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +15 -0
- package/dist/adapter.js.map +1 -0
- package/dist/adapters/facebook.d.ts +25 -0
- package/dist/adapters/facebook.d.ts.map +1 -0
- package/dist/adapters/facebook.js +95 -0
- package/dist/adapters/facebook.js.map +1 -0
- package/dist/adapters/index.d.ts +23 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +19 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/instagram.d.ts +25 -0
- package/dist/adapters/instagram.d.ts.map +1 -0
- package/dist/adapters/instagram.js +113 -0
- package/dist/adapters/instagram.js.map +1 -0
- package/dist/adapters/linkedin.d.ts +28 -0
- package/dist/adapters/linkedin.d.ts.map +1 -0
- package/dist/adapters/linkedin.js +121 -0
- package/dist/adapters/linkedin.js.map +1 -0
- package/dist/adapters/x.d.ts +21 -0
- package/dist/adapters/x.d.ts.map +1 -0
- package/dist/adapters/x.js +80 -0
- package/dist/adapters/x.js.map +1 -0
- package/dist/http.d.ts +40 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +127 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/vk_social_0001_init.sql +121 -0
- package/dist/path.d.ts +9 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +17 -0
- package/dist/path.js.map +1 -0
- package/dist/registry.d.ts +32 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +42 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +57 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +17 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +157 -0
- package/dist/validation.js.map +1 -0
- package/package.json +78 -0
- package/src/migrations/vk_social_0001_init.sql +121 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
-- @venturekit-pro/social — initial schema.
|
|
2
|
+
--
|
|
3
|
+
-- Two tables:
|
|
4
|
+
--
|
|
5
|
+
-- social_platforms — platform-admin-owned catalog of every
|
|
6
|
+
-- social network the package supports.
|
|
7
|
+
-- One row per (provider, adapter version).
|
|
8
|
+
-- The four v1 platforms are seeded by the
|
|
9
|
+
-- bottom of this migration.
|
|
10
|
+
--
|
|
11
|
+
-- tenant_social_platforms — per-tenant enable/disable + author-ref
|
|
12
|
+
-- pointer. NO cadence / fan-out / locale
|
|
13
|
+
-- fields here — those are calling-app
|
|
14
|
+
-- concerns. Apps that want per-platform
|
|
15
|
+
-- cadence settings model that in their
|
|
16
|
+
-- own schema.
|
|
17
|
+
--
|
|
18
|
+
-- Append-only fields are not used here; the rows are tenant-mutable
|
|
19
|
+
-- (toggle enabled, swap author_ref). Audit-worthy changes flow into
|
|
20
|
+
-- `@venturekit-pro/audit`'s `audit_events` separately.
|
|
21
|
+
|
|
22
|
+
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
23
|
+
|
|
24
|
+
-- ─── social_platforms (catalog) ─────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
DO $$
|
|
27
|
+
BEGIN
|
|
28
|
+
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'vk_social_platform_status') THEN
|
|
29
|
+
CREATE TYPE vk_social_platform_status AS ENUM (
|
|
30
|
+
'available',
|
|
31
|
+
'deprecated',
|
|
32
|
+
'disabled'
|
|
33
|
+
);
|
|
34
|
+
END IF;
|
|
35
|
+
END
|
|
36
|
+
$$;
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS social_platforms (
|
|
39
|
+
key varchar(64) PRIMARY KEY,
|
|
40
|
+
display_name varchar(255) NOT NULL,
|
|
41
|
+
-- Lifecycle: `available` rows render in pickers; `deprecated` ones
|
|
42
|
+
-- are still callable but the UI nudges a swap; `disabled` rows
|
|
43
|
+
-- refuse to publish at the adapter resolution boundary.
|
|
44
|
+
status vk_social_platform_status NOT NULL DEFAULT 'available',
|
|
45
|
+
-- Human-readable description for picker rows.
|
|
46
|
+
description text,
|
|
47
|
+
-- Free-form metadata (icon name, brand color, etc). Apps consume
|
|
48
|
+
-- this for UI rendering without the package needing to enumerate
|
|
49
|
+
-- every visual property.
|
|
50
|
+
meta jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
51
|
+
-- When the row was first introduced + lifecycle timestamps. Used
|
|
52
|
+
-- by the platform-admin UI to render badges.
|
|
53
|
+
introduced_at timestamptz NOT NULL DEFAULT now(),
|
|
54
|
+
deprecated_at timestamptz,
|
|
55
|
+
disabled_at timestamptz,
|
|
56
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
57
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
-- ─── tenant_social_platforms (per-tenant) ───────────────────────────
|
|
61
|
+
--
|
|
62
|
+
-- Apps that want per-platform-per-tenant settings (cadence, locales,
|
|
63
|
+
-- fan-out weeks) extend THIS table in their own migration. We keep
|
|
64
|
+
-- the package-owned columns to the strict minimum: enabled flag,
|
|
65
|
+
-- author ref (the platform-side id we publish into), and optional
|
|
66
|
+
-- vendor metadata.
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS tenant_social_platforms (
|
|
69
|
+
tenant_id uuid NOT NULL,
|
|
70
|
+
platform_key varchar(64) NOT NULL REFERENCES social_platforms(key) ON DELETE RESTRICT,
|
|
71
|
+
enabled boolean NOT NULL DEFAULT TRUE,
|
|
72
|
+
-- Platform-side identifier this tenant publishes to. URN / page id /
|
|
73
|
+
-- ig user id. Required when enabled = TRUE; the application enforces
|
|
74
|
+
-- that invariant at the service boundary (we don't put a CHECK here
|
|
75
|
+
-- because the empty string is sometimes used as a placeholder
|
|
76
|
+
-- during onboarding wizards).
|
|
77
|
+
author_ref varchar(512) NOT NULL DEFAULT '',
|
|
78
|
+
-- Free-form metadata stamped by the tenant ops flow (locale,
|
|
79
|
+
-- scopes, etc). Adapters ignore unless documented.
|
|
80
|
+
meta jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
81
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
82
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
83
|
+
PRIMARY KEY (tenant_id, platform_key)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
CREATE INDEX IF NOT EXISTS tenant_social_platforms_enabled_idx
|
|
87
|
+
ON tenant_social_platforms (tenant_id)
|
|
88
|
+
WHERE enabled;
|
|
89
|
+
|
|
90
|
+
-- ─── Catalog seed ───────────────────────────────────────────────────
|
|
91
|
+
--
|
|
92
|
+
-- Idempotent (ON CONFLICT DO NOTHING) so re-running the migration on
|
|
93
|
+
-- an already-seeded DB is a no-op. Tenants who want to add custom
|
|
94
|
+
-- adapters (e.g. Mastodon, Bluesky) insert their own row at app boot.
|
|
95
|
+
|
|
96
|
+
INSERT INTO social_platforms (key, display_name, description, meta) VALUES
|
|
97
|
+
(
|
|
98
|
+
'linkedin',
|
|
99
|
+
'LinkedIn',
|
|
100
|
+
'Professional network. UGC Posts API (v2). Author URN required.',
|
|
101
|
+
jsonb_build_object('iconKey', 'linkedin', 'brandColor', '#0A66C2')
|
|
102
|
+
),
|
|
103
|
+
(
|
|
104
|
+
'x',
|
|
105
|
+
'X',
|
|
106
|
+
'Microblogging (formerly Twitter). v2 Tweets API. 280-char body.',
|
|
107
|
+
jsonb_build_object('iconKey', 'x', 'brandColor', '#000000')
|
|
108
|
+
),
|
|
109
|
+
(
|
|
110
|
+
'facebook',
|
|
111
|
+
'Facebook',
|
|
112
|
+
'Meta Graph API for Pages. Page access token required.',
|
|
113
|
+
jsonb_build_object('iconKey', 'facebook', 'brandColor', '#1877F2')
|
|
114
|
+
),
|
|
115
|
+
(
|
|
116
|
+
'instagram',
|
|
117
|
+
'Instagram',
|
|
118
|
+
'Meta Graph Content Publishing API (Business / Creator accounts).',
|
|
119
|
+
jsonb_build_object('iconKey', 'instagram', 'brandColor', '#E4405F')
|
|
120
|
+
)
|
|
121
|
+
ON CONFLICT (key) DO NOTHING;
|
package/dist/path.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrations-dir helper — same shape as `@venturekit-pro/audit`.
|
|
3
|
+
*
|
|
4
|
+
* Wired into `vk.config.ts:extraMigrationsDirs` so `vk migrate` picks
|
|
5
|
+
* up the package's `vk_social_*.sql` files alongside the project's
|
|
6
|
+
* own migrations.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getSocialMigrationsDir(): string;
|
|
9
|
+
//# sourceMappingURL=path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,wBAAgB,sBAAsB,IAAI,MAAM,CAM/C"}
|
package/dist/path.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrations-dir helper — same shape as `@venturekit-pro/audit`.
|
|
3
|
+
*
|
|
4
|
+
* Wired into `vk.config.ts:extraMigrationsDirs` so `vk migrate` picks
|
|
5
|
+
* up the package's `vk_social_*.sql` files alongside the project's
|
|
6
|
+
* own migrations.
|
|
7
|
+
*/
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
export function getSocialMigrationsDir() {
|
|
11
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
// After build: dist/path.js, migrations under dist/migrations/.
|
|
13
|
+
// From source (workspace symlink): src/path.ts, migrations under
|
|
14
|
+
// src/migrations/. Both resolve to the same relative subdir.
|
|
15
|
+
return path.join(here, 'migrations');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=path.js.map
|
package/dist/path.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,sBAAsB;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,gEAAgE;IAChE,iEAAiE;IACjE,6DAA6D;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter registry — a small lookup table consumers populate once at
|
|
3
|
+
* boot time with the adapters they want to enable.
|
|
4
|
+
*
|
|
5
|
+
* const registry = createAdapterRegistry([
|
|
6
|
+
* linkedinAdapter,
|
|
7
|
+
* xAdapter,
|
|
8
|
+
* facebookAdapter,
|
|
9
|
+
* instagramAdapter,
|
|
10
|
+
* ]);
|
|
11
|
+
*
|
|
12
|
+
* const adapter = registry.resolve('linkedin');
|
|
13
|
+
* const result = await adapter.publish(post, credentials);
|
|
14
|
+
*
|
|
15
|
+
* The registry is a thin Map — no resolution policy beyond exact
|
|
16
|
+
* key match. Apps that need fallback (e.g. "instagram" → "meta") can
|
|
17
|
+
* compose a wrapper.
|
|
18
|
+
*/
|
|
19
|
+
import type { SocialAdapter } from './adapter.js';
|
|
20
|
+
import type { SocialPlatformKey } from './types.js';
|
|
21
|
+
export interface AdapterRegistry {
|
|
22
|
+
/** All registered adapters in registration order. */
|
|
23
|
+
list(): SocialAdapter[];
|
|
24
|
+
/** Look up by key; `undefined` if not registered. */
|
|
25
|
+
find(key: SocialPlatformKey): SocialAdapter | undefined;
|
|
26
|
+
/** Look up by key or throw — for hot paths where the row MUST exist. */
|
|
27
|
+
resolve(key: SocialPlatformKey): SocialAdapter;
|
|
28
|
+
/** Set of supported keys. */
|
|
29
|
+
keys(): SocialPlatformKey[];
|
|
30
|
+
}
|
|
31
|
+
export declare function createAdapterRegistry(adapters: SocialAdapter[]): AdapterRegistry;
|
|
32
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,IAAI,IAAI,aAAa,EAAE,CAAC;IACxB,qDAAqD;IACrD,IAAI,CAAC,GAAG,EAAE,iBAAiB,GAAG,aAAa,GAAG,SAAS,CAAC;IACxD,wEAAwE;IACxE,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,aAAa,CAAC;IAC/C,6BAA6B;IAC7B,IAAI,IAAI,iBAAiB,EAAE,CAAC;CAC7B;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,eAAe,CAwBhF"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter registry — a small lookup table consumers populate once at
|
|
3
|
+
* boot time with the adapters they want to enable.
|
|
4
|
+
*
|
|
5
|
+
* const registry = createAdapterRegistry([
|
|
6
|
+
* linkedinAdapter,
|
|
7
|
+
* xAdapter,
|
|
8
|
+
* facebookAdapter,
|
|
9
|
+
* instagramAdapter,
|
|
10
|
+
* ]);
|
|
11
|
+
*
|
|
12
|
+
* const adapter = registry.resolve('linkedin');
|
|
13
|
+
* const result = await adapter.publish(post, credentials);
|
|
14
|
+
*
|
|
15
|
+
* The registry is a thin Map — no resolution policy beyond exact
|
|
16
|
+
* key match. Apps that need fallback (e.g. "instagram" → "meta") can
|
|
17
|
+
* compose a wrapper.
|
|
18
|
+
*/
|
|
19
|
+
export function createAdapterRegistry(adapters) {
|
|
20
|
+
const byKey = new Map();
|
|
21
|
+
for (const a of adapters) {
|
|
22
|
+
if (byKey.has(a.key)) {
|
|
23
|
+
throw new Error(`[social/registry] Duplicate adapter key: '${a.key}'`);
|
|
24
|
+
}
|
|
25
|
+
byKey.set(a.key, a);
|
|
26
|
+
}
|
|
27
|
+
const all = adapters.slice();
|
|
28
|
+
return {
|
|
29
|
+
list: () => all.slice(),
|
|
30
|
+
find: (key) => byKey.get(key),
|
|
31
|
+
resolve(key) {
|
|
32
|
+
const a = byKey.get(key);
|
|
33
|
+
if (!a) {
|
|
34
|
+
throw new Error(`[social/registry] No adapter registered for key '${key}'. ` +
|
|
35
|
+
`Registered: [${Array.from(byKey.keys()).join(', ')}]`);
|
|
36
|
+
}
|
|
37
|
+
return a;
|
|
38
|
+
},
|
|
39
|
+
keys: () => Array.from(byKey.keys()),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgBH,MAAM,UAAU,qBAAqB,CAAC,QAAyB;IAC7D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoC,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC7B,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE;QACvB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;QAC7B,OAAO,CAAC,GAAG;YACT,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM,IAAI,KAAK,CACb,oDAAoD,GAAG,KAAK;oBAC1D,gBAAgB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACzD,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;KACrC,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic social-publishing types.
|
|
3
|
+
*
|
|
4
|
+
* Intentionally domain-neutral: this package knows about platforms,
|
|
5
|
+
* posts, media, OAuth credentials, and publish results. It does not
|
|
6
|
+
* know about cadence, fan-out, angles, AI generation, or any other
|
|
7
|
+
* concept a calling app might build on top.
|
|
8
|
+
*
|
|
9
|
+
* The catalog of supported platforms is a database row (`social_platforms`,
|
|
10
|
+
* shipped by `migrations/vk_social_0001_init.sql`); the package
|
|
11
|
+
* provides an adapter implementation per row.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Platform identifier — stable string keyed against the
|
|
15
|
+
* `social_platforms.key` column. Adding a new platform requires:
|
|
16
|
+
* 1. A row in `social_platforms`.
|
|
17
|
+
* 2. An adapter under `adapters/<key>.ts`.
|
|
18
|
+
* 3. A registration in the consumer's `createAdapterRegistry()` call.
|
|
19
|
+
*
|
|
20
|
+
* The four v1 platforms ship in this package. Apps with their own
|
|
21
|
+
* adapter (e.g. Mastodon, Bluesky) register them at runtime.
|
|
22
|
+
*/
|
|
23
|
+
export type SocialPlatformKey = 'linkedin' | 'x' | 'facebook' | 'instagram' | string;
|
|
24
|
+
/**
|
|
25
|
+
* OAuth credentials a tenant supplies for a given platform. The
|
|
26
|
+
* package never reads or writes these; the caller fetches them from
|
|
27
|
+
* its own secret store (KMS-encrypted DB jsonb in the CMS) and
|
|
28
|
+
* passes them per call.
|
|
29
|
+
*
|
|
30
|
+
* `accessToken` is the bearer token used on every API call.
|
|
31
|
+
* Refresh-token logic lives in `oauth.ts` for platforms that
|
|
32
|
+
* support it; expired-token detection surfaces as a typed error so
|
|
33
|
+
* the caller can re-fetch from its secret store.
|
|
34
|
+
*/
|
|
35
|
+
export interface OAuthCredentials {
|
|
36
|
+
/** Platform-issued access token (bearer-style). */
|
|
37
|
+
accessToken: string;
|
|
38
|
+
/** Optional refresh token; passed to `refreshTokens()` when present. */
|
|
39
|
+
refreshToken?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Optional ISO timestamp when the access token expires. When
|
|
42
|
+
* present, adapters MAY refuse to call upstream if the timestamp
|
|
43
|
+
* is in the past + ask the caller to refresh.
|
|
44
|
+
*/
|
|
45
|
+
expiresAt?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Platform-specific identifier for *what* to publish to. Examples:
|
|
48
|
+
* - LinkedIn: `'urn:li:organization:12345'` (the org's URN)
|
|
49
|
+
* - X: the user/page handle
|
|
50
|
+
* - Facebook: `'page_<pageId>'`
|
|
51
|
+
* - Instagram: `'ig_user_<igUserId>'`
|
|
52
|
+
*
|
|
53
|
+
* Stored alongside the access token so a single tenant can have
|
|
54
|
+
* different (token, target) pairs per platform.
|
|
55
|
+
*/
|
|
56
|
+
authorRef: string;
|
|
57
|
+
/**
|
|
58
|
+
* Free-form vendor metadata stamped onto the credential pair (e.g.
|
|
59
|
+
* scopes, locale). Optional; adapters ignore it unless documented
|
|
60
|
+
* to read it.
|
|
61
|
+
*/
|
|
62
|
+
meta?: Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One piece of media attached to a post.
|
|
66
|
+
*
|
|
67
|
+
* `url` must be publicly fetchable by the platform's upload API.
|
|
68
|
+
* Pre-signed URLs are fine; private S3 keys are not.
|
|
69
|
+
*/
|
|
70
|
+
export interface SocialMedia {
|
|
71
|
+
url: string;
|
|
72
|
+
/** MIME type — used by the upload step to short-circuit on unsupported types. */
|
|
73
|
+
mimeType: string;
|
|
74
|
+
/** Bytes; helps the adapter pick chunked-upload vs single-shot. */
|
|
75
|
+
sizeBytes?: number;
|
|
76
|
+
/** Optional alt text — passed through where supported. */
|
|
77
|
+
altText?: string;
|
|
78
|
+
width?: number;
|
|
79
|
+
height?: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A platform-neutral social post. Adapters convert this to the
|
|
83
|
+
* vendor's expected request shape inside `publish()`.
|
|
84
|
+
*/
|
|
85
|
+
export interface SocialPost {
|
|
86
|
+
/** Free-text body. Length / hashtag rules enforced by `validate()`. */
|
|
87
|
+
body: string;
|
|
88
|
+
/** Attached media (images / videos). */
|
|
89
|
+
media?: SocialMedia[];
|
|
90
|
+
/** Optional canonical link forwarded to platforms with link-preview unfurling. */
|
|
91
|
+
link?: string;
|
|
92
|
+
/** First-comment companion thread (LinkedIn, X). Ignored on platforms that don't support it. */
|
|
93
|
+
firstComment?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Idempotency token forwarded to vendor APIs that support it (LinkedIn,
|
|
96
|
+
* Meta Graph). Used to make retries safe.
|
|
97
|
+
*/
|
|
98
|
+
idempotencyKey?: string;
|
|
99
|
+
/** Caller-side correlation id (audit + log fan-out). */
|
|
100
|
+
correlationId?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Reasons `validate()` may surface. */
|
|
103
|
+
export type SocialValidationCode = 'body_empty' | 'body_too_long' | 'body_too_short' | 'hashtag_count_low' | 'hashtag_count_high' | 'media_required' | 'media_count_high' | 'aspect_ratio_unsupported' | 'media_too_large' | 'media_mime_unsupported' | 'link_invalid';
|
|
104
|
+
export interface SocialValidationIssue {
|
|
105
|
+
code: SocialValidationCode;
|
|
106
|
+
message: string;
|
|
107
|
+
/** Severity — `error` blocks publish; `warning` is informational. */
|
|
108
|
+
severity: 'error' | 'warning';
|
|
109
|
+
}
|
|
110
|
+
export interface SocialValidationResult {
|
|
111
|
+
ok: boolean;
|
|
112
|
+
issues: SocialValidationIssue[];
|
|
113
|
+
}
|
|
114
|
+
/** Constraints used by `validate()`. Adapters override at construction. */
|
|
115
|
+
export interface SocialPlatformConstraints {
|
|
116
|
+
maxBodyChars: number;
|
|
117
|
+
/** Set to 0 to disable the body-required check. */
|
|
118
|
+
minBodyChars: number;
|
|
119
|
+
minHashtags: number;
|
|
120
|
+
maxHashtags: number;
|
|
121
|
+
/** Required media types (or `[]` to allow text-only). */
|
|
122
|
+
allowedMimeTypes: string[];
|
|
123
|
+
/** Max attachments per post; 0 = no media allowed; -1 = no cap. */
|
|
124
|
+
maxMediaCount: number;
|
|
125
|
+
/** Allowed aspect-ratio strings (`'1:1' | '4:5' | '16:9' | …`). */
|
|
126
|
+
allowedAspectRatios: string[];
|
|
127
|
+
/** Cap per attachment in bytes. -1 = no cap. */
|
|
128
|
+
maxMediaSizeBytes: number;
|
|
129
|
+
/** Whether `first_comment` is supported. */
|
|
130
|
+
supportsFirstComment: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Whether the platform refuses text-only posts (Instagram is the
|
|
133
|
+
* canonical case). Defaults to `false` — most platforms accept a
|
|
134
|
+
* naked body. Set independently of `minBodyChars`: Instagram requires
|
|
135
|
+
* BOTH a non-empty caption AND at least one media item, so the
|
|
136
|
+
* heuristic "media required when minBodyChars === 0" doesn't fit.
|
|
137
|
+
*/
|
|
138
|
+
requiresMedia?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Result of a successful publish. Adapters fill in everything they
|
|
142
|
+
* can; minimum contract is `externalRef`.
|
|
143
|
+
*/
|
|
144
|
+
export interface PublishResult {
|
|
145
|
+
/** Platform-native id (URN / tweet id / post id / media id). */
|
|
146
|
+
externalRef: string;
|
|
147
|
+
/** Public URL when known. Some platforms don't expose this immediately. */
|
|
148
|
+
publishedUrl?: string;
|
|
149
|
+
/** ISO timestamp when the platform accepted the post. */
|
|
150
|
+
publishedAt: string;
|
|
151
|
+
/** Echo of the caller's idempotency token. */
|
|
152
|
+
idempotencyKey?: string;
|
|
153
|
+
/** Echo of the caller's correlation id. */
|
|
154
|
+
correlationId?: string;
|
|
155
|
+
/** Raw vendor response — opaque, surfaced for debugging only. */
|
|
156
|
+
raw?: unknown;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Status check result. Useful when a publish was accepted async and
|
|
160
|
+
* the caller wants to know whether it actually landed.
|
|
161
|
+
*/
|
|
162
|
+
export interface VerifyResult {
|
|
163
|
+
status: 'pending' | 'published' | 'failed' | 'deleted' | 'unknown';
|
|
164
|
+
publishedUrl?: string;
|
|
165
|
+
/** Platform-side error message when `status === 'failed'`. */
|
|
166
|
+
errorMessage?: string;
|
|
167
|
+
raw?: unknown;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Webhook payload classifier output. Used by callers that wire a
|
|
171
|
+
* platform's webhooks into their own job queue.
|
|
172
|
+
*/
|
|
173
|
+
export interface WebhookEvent {
|
|
174
|
+
/** Which platform fired the event. */
|
|
175
|
+
platform: SocialPlatformKey;
|
|
176
|
+
/** Native event type ('post.created', 'post.deleted', etc). Free-form. */
|
|
177
|
+
kind: string;
|
|
178
|
+
/** Foreign key matching `PublishResult.externalRef`. */
|
|
179
|
+
externalRef: string;
|
|
180
|
+
/** Best-effort timestamp; falls back to `now()` if the vendor omits one. */
|
|
181
|
+
occurredAt: string;
|
|
182
|
+
/** Raw payload echoed back for downstream logging. */
|
|
183
|
+
raw: unknown;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Base class for typed publish errors. Adapters throw subclasses;
|
|
187
|
+
* callers `instanceof` to differentiate retry-worthy from permanent
|
|
188
|
+
* failures.
|
|
189
|
+
*/
|
|
190
|
+
export declare class SocialPublishError extends Error {
|
|
191
|
+
readonly platform: SocialPlatformKey;
|
|
192
|
+
/** HTTP status code from the upstream call, when applicable. */
|
|
193
|
+
readonly status?: number;
|
|
194
|
+
/** Raw vendor response body (truncated to 1KB). */
|
|
195
|
+
readonly raw?: unknown;
|
|
196
|
+
constructor(message: string, opts: {
|
|
197
|
+
platform: SocialPlatformKey;
|
|
198
|
+
status?: number;
|
|
199
|
+
raw?: unknown;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/** Bad / expired credentials. Caller should re-fetch + retry. */
|
|
203
|
+
export declare class SocialAuthError extends SocialPublishError {
|
|
204
|
+
constructor(message: string, opts: {
|
|
205
|
+
platform: SocialPlatformKey;
|
|
206
|
+
status?: number;
|
|
207
|
+
raw?: unknown;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/** Rate limit hit — caller should back off and retry. */
|
|
211
|
+
export declare class SocialRateLimitError extends SocialPublishError {
|
|
212
|
+
/** Suggested retry-after seconds, from upstream headers when present. */
|
|
213
|
+
readonly retryAfterSeconds?: number;
|
|
214
|
+
constructor(message: string, opts: {
|
|
215
|
+
platform: SocialPlatformKey;
|
|
216
|
+
status?: number;
|
|
217
|
+
raw?: unknown;
|
|
218
|
+
retryAfterSeconds?: number;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/** Validation rejection from the platform (caught body, malformed media). */
|
|
222
|
+
export declare class SocialValidationError extends SocialPublishError {
|
|
223
|
+
constructor(message: string, opts: {
|
|
224
|
+
platform: SocialPlatformKey;
|
|
225
|
+
status?: number;
|
|
226
|
+
raw?: unknown;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic social-publishing types.
|
|
3
|
+
*
|
|
4
|
+
* Intentionally domain-neutral: this package knows about platforms,
|
|
5
|
+
* posts, media, OAuth credentials, and publish results. It does not
|
|
6
|
+
* know about cadence, fan-out, angles, AI generation, or any other
|
|
7
|
+
* concept a calling app might build on top.
|
|
8
|
+
*
|
|
9
|
+
* The catalog of supported platforms is a database row (`social_platforms`,
|
|
10
|
+
* shipped by `migrations/vk_social_0001_init.sql`); the package
|
|
11
|
+
* provides an adapter implementation per row.
|
|
12
|
+
*/
|
|
13
|
+
// ─── Typed errors ──────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Base class for typed publish errors. Adapters throw subclasses;
|
|
16
|
+
* callers `instanceof` to differentiate retry-worthy from permanent
|
|
17
|
+
* failures.
|
|
18
|
+
*/
|
|
19
|
+
export class SocialPublishError extends Error {
|
|
20
|
+
platform;
|
|
21
|
+
/** HTTP status code from the upstream call, when applicable. */
|
|
22
|
+
status;
|
|
23
|
+
/** Raw vendor response body (truncated to 1KB). */
|
|
24
|
+
raw;
|
|
25
|
+
constructor(message, opts) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'SocialPublishError';
|
|
28
|
+
this.platform = opts.platform;
|
|
29
|
+
this.status = opts.status;
|
|
30
|
+
this.raw = opts.raw;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Bad / expired credentials. Caller should re-fetch + retry. */
|
|
34
|
+
export class SocialAuthError extends SocialPublishError {
|
|
35
|
+
constructor(message, opts) {
|
|
36
|
+
super(message, opts);
|
|
37
|
+
this.name = 'SocialAuthError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Rate limit hit — caller should back off and retry. */
|
|
41
|
+
export class SocialRateLimitError extends SocialPublishError {
|
|
42
|
+
/** Suggested retry-after seconds, from upstream headers when present. */
|
|
43
|
+
retryAfterSeconds;
|
|
44
|
+
constructor(message, opts) {
|
|
45
|
+
super(message, opts);
|
|
46
|
+
this.name = 'SocialRateLimitError';
|
|
47
|
+
this.retryAfterSeconds = opts.retryAfterSeconds;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Validation rejection from the platform (caught body, malformed media). */
|
|
51
|
+
export class SocialValidationError extends SocialPublishError {
|
|
52
|
+
constructor(message, opts) {
|
|
53
|
+
super(message, opts);
|
|
54
|
+
this.name = 'SocialValidationError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoMH,0EAA0E;AAE1E;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAClC,QAAQ,CAAoB;IACrC,gEAAgE;IACvD,MAAM,CAAU;IACzB,mDAAmD;IAC1C,GAAG,CAAW;IACvB,YAAY,OAAe,EAAE,IAI5B;QACC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACtB,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,OAAO,eAAgB,SAAQ,kBAAkB;IACrD,YAAY,OAAe,EAAE,IAAqE;QAChG,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,yDAAyD;AACzD,MAAM,OAAO,oBAAqB,SAAQ,kBAAkB;IAC1D,yEAAyE;IAChE,iBAAiB,CAAU;IACpC,YACE,OAAe,EACf,IAAiG;QAEjG,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC;IAClD,CAAC;CACF;AAED,6EAA6E;AAC7E,MAAM,OAAO,qBAAsB,SAAQ,kBAAkB;IAC3D,YAAY,OAAe,EAAE,IAAqE;QAChG,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic post-validation. Adapters compose this with their per-
|
|
3
|
+
* platform `SocialPlatformConstraints` to surface a uniform error
|
|
4
|
+
* shape without re-implementing every check four times.
|
|
5
|
+
*
|
|
6
|
+
* Returns BOTH errors AND warnings. Callers MUST block publish on
|
|
7
|
+
* `severity === 'error'`; warnings are informational ("you only
|
|
8
|
+
* have 1 hashtag, LinkedIn recommends 2-5").
|
|
9
|
+
*/
|
|
10
|
+
import type { SocialPlatformConstraints, SocialPost, SocialValidationResult } from './types.js';
|
|
11
|
+
export declare function validatePost(post: SocialPost, constraints: SocialPlatformConstraints): SocialValidationResult;
|
|
12
|
+
/**
|
|
13
|
+
* Reduce `(width, height)` to a normalized `W:H` ratio string.
|
|
14
|
+
* Matches the format used in `allowedAspectRatios`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function simplifyRatio(width: number, height: number): string;
|
|
17
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,yBAAyB,EACzB,UAAU,EAEV,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,yBAAyB,GACrC,sBAAsB,CA+HxB;AAgBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAInE"}
|