aimakeall-mcp 0.14.4 → 0.14.6
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 +117 -1
- package/lib/cloud-tools.mjs +131 -51
- package/lib/config.mjs +1 -1
- package/lib/durable-channel-contract.mjs +16 -6
- package/lib/durable-channel-journal.mjs +10 -5
- package/lib/durable-channel-request.mjs +4 -4
- package/lib/durable-remake-contract.mjs +88 -0
- package/lib/durable-remake-request.mjs +375 -0
- package/lib/durable-speech.mjs +2 -2
- package/lib/durable-voice-contract.mjs +62 -0
- package/lib/narration-timeline.mjs +189 -0
- package/lib/review-plan-client.mjs +73 -0
- package/lib/review-production-contract.mjs +198 -0
- package/lib/review-tools.mjs +40 -11
- package/lib/speech-production-journal.mjs +109 -0
- package/lib/speech-production.mjs +161 -0
- package/lib/studio-plan-client.mjs +64 -0
- package/lib/studio-plan-contract.mjs +128 -0
- package/lib/studio-plan-request.mjs +372 -0
- package/lib/voice-catalog.mjs +33 -0
- package/lib/voice-recommendation-client.mjs +40 -0
- package/node_modules/fast-uri/index.js +10 -6
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security-normalization.test.js +38 -0
- package/node_modules/hono/README.md +1 -1
- package/node_modules/hono/dist/_virtual/_rolldown/runtime.js +13 -0
- package/node_modules/hono/dist/adapter/aws-lambda/conninfo.js +42 -23
- package/node_modules/hono/dist/adapter/aws-lambda/handler.js +320 -342
- package/node_modules/hono/dist/adapter/aws-lambda/index.js +2 -8
- package/node_modules/hono/dist/adapter/aws-lambda/types.js +1 -0
- package/node_modules/hono/dist/adapter/bun/conninfo.js +20 -25
- package/node_modules/hono/dist/adapter/bun/index.js +3 -13
- package/node_modules/hono/dist/adapter/bun/serve-static.js +27 -27
- package/node_modules/hono/dist/adapter/bun/server.js +11 -5
- package/node_modules/hono/dist/adapter/bun/ssg.js +25 -15
- package/node_modules/hono/dist/adapter/bun/websocket.js +61 -68
- package/node_modules/hono/dist/adapter/cloudflare-pages/conninfo.js +24 -9
- package/node_modules/hono/dist/adapter/cloudflare-pages/handler.js +59 -62
- package/node_modules/hono/dist/adapter/cloudflare-pages/index.js +2 -8
- package/node_modules/hono/dist/adapter/cloudflare-workers/conninfo.js +7 -9
- package/node_modules/hono/dist/adapter/cloudflare-workers/index.js +3 -8
- package/node_modules/hono/dist/adapter/cloudflare-workers/serve-static-module.js +5 -6
- package/node_modules/hono/dist/adapter/cloudflare-workers/serve-static.js +26 -20
- package/node_modules/hono/dist/adapter/cloudflare-workers/utils.js +21 -34
- package/node_modules/hono/dist/adapter/cloudflare-workers/websocket.js +31 -39
- package/node_modules/hono/dist/adapter/deno/conninfo.js +16 -13
- package/node_modules/hono/dist/adapter/deno/deno.d.js +1 -0
- package/node_modules/hono/dist/adapter/deno/index.js +3 -10
- package/node_modules/hono/dist/adapter/deno/serve-static.js +33 -38
- package/node_modules/hono/dist/adapter/deno/ssg.js +27 -16
- package/node_modules/hono/dist/adapter/deno/websocket.js +30 -30
- package/node_modules/hono/dist/adapter/lambda-edge/conninfo.js +7 -9
- package/node_modules/hono/dist/adapter/lambda-edge/handler.js +102 -87
- package/node_modules/hono/dist/adapter/lambda-edge/index.js +2 -6
- package/node_modules/hono/dist/adapter/netlify/conninfo.js +24 -9
- package/node_modules/hono/dist/adapter/netlify/handler.js +10 -8
- package/node_modules/hono/dist/adapter/netlify/index.js +3 -2
- package/node_modules/hono/dist/adapter/netlify/mod.js +2 -6
- package/node_modules/hono/dist/adapter/service-worker/handler.js +15 -19
- package/node_modules/hono/dist/adapter/service-worker/index.js +23 -7
- package/node_modules/hono/dist/adapter/service-worker/types.js +1 -0
- package/node_modules/hono/dist/adapter/vercel/conninfo.js +7 -10
- package/node_modules/hono/dist/adapter/vercel/handler.js +8 -6
- package/node_modules/hono/dist/adapter/vercel/index.js +2 -6
- package/node_modules/hono/dist/cjs/_virtual/_rolldown/runtime.js +33 -0
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/conninfo.js +42 -44
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +328 -371
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/index.js +7 -34
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/types.js +0 -15
- package/node_modules/hono/dist/cjs/adapter/bun/conninfo.js +21 -47
- package/node_modules/hono/dist/cjs/adapter/bun/index.js +14 -45
- package/node_modules/hono/dist/cjs/adapter/bun/serve-static.js +29 -50
- package/node_modules/hono/dist/cjs/adapter/bun/server.js +11 -26
- package/node_modules/hono/dist/cjs/adapter/bun/ssg.js +24 -35
- package/node_modules/hono/dist/cjs/adapter/bun/websocket.js +63 -91
- package/node_modules/hono/dist/cjs/adapter/cloudflare-pages/conninfo.js +25 -31
- package/node_modules/hono/dist/cjs/adapter/cloudflare-pages/handler.js +61 -85
- package/node_modules/hono/dist/cjs/adapter/cloudflare-pages/index.js +7 -34
- package/node_modules/hono/dist/cjs/adapter/cloudflare-workers/conninfo.js +8 -31
- package/node_modules/hono/dist/cjs/adapter/cloudflare-workers/index.js +7 -33
- package/node_modules/hono/dist/cjs/adapter/cloudflare-workers/serve-static-module.js +7 -29
- package/node_modules/hono/dist/cjs/adapter/cloudflare-workers/serve-static.js +27 -42
- package/node_modules/hono/dist/cjs/adapter/cloudflare-workers/utils.js +21 -55
- package/node_modules/hono/dist/cjs/adapter/cloudflare-workers/websocket.js +33 -62
- package/node_modules/hono/dist/cjs/adapter/deno/conninfo.js +16 -34
- package/node_modules/hono/dist/cjs/adapter/deno/index.js +10 -38
- package/node_modules/hono/dist/cjs/adapter/deno/serve-static.js +33 -59
- package/node_modules/hono/dist/cjs/adapter/deno/ssg.js +27 -37
- package/node_modules/hono/dist/cjs/adapter/deno/websocket.js +32 -53
- package/node_modules/hono/dist/cjs/adapter/lambda-edge/conninfo.js +8 -31
- package/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +105 -119
- package/node_modules/hono/dist/cjs/adapter/lambda-edge/index.js +5 -30
- package/node_modules/hono/dist/cjs/adapter/netlify/conninfo.js +25 -31
- package/node_modules/hono/dist/cjs/adapter/netlify/handler.js +10 -29
- package/node_modules/hono/dist/cjs/adapter/netlify/index.js +5 -21
- package/node_modules/hono/dist/cjs/adapter/netlify/mod.js +5 -30
- package/node_modules/hono/dist/cjs/adapter/service-worker/handler.js +16 -41
- package/node_modules/hono/dist/cjs/adapter/service-worker/index.js +25 -30
- package/node_modules/hono/dist/cjs/adapter/service-worker/types.js +0 -15
- package/node_modules/hono/dist/cjs/adapter/vercel/conninfo.js +8 -32
- package/node_modules/hono/dist/cjs/adapter/vercel/handler.js +8 -27
- package/node_modules/hono/dist/cjs/adapter/vercel/index.js +5 -30
- package/node_modules/hono/dist/cjs/client/client.js +139 -212
- package/node_modules/hono/dist/cjs/client/fetch-result-please.js +62 -80
- package/node_modules/hono/dist/cjs/client/index.js +7 -32
- package/node_modules/hono/dist/cjs/client/types.js +0 -15
- package/node_modules/hono/dist/cjs/client/utils.js +54 -92
- package/node_modules/hono/dist/cjs/compose.js +49 -66
- package/node_modules/hono/dist/cjs/context.js +379 -450
- package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +47 -70
- package/node_modules/hono/dist/cjs/helper/accepts/index.js +3 -27
- package/node_modules/hono/dist/cjs/helper/adapter/index.js +33 -73
- package/node_modules/hono/dist/cjs/helper/conninfo/index.js +0 -15
- package/node_modules/hono/dist/cjs/helper/conninfo/types.js +0 -15
- package/node_modules/hono/dist/cjs/helper/cookie/index.js +75 -116
- package/node_modules/hono/dist/cjs/helper/css/common.js +165 -244
- package/node_modules/hono/dist/cjs/helper/css/index.js +136 -151
- package/node_modules/hono/dist/cjs/helper/dev/index.js +45 -71
- package/node_modules/hono/dist/cjs/helper/factory/index.js +30 -51
- package/node_modules/hono/dist/cjs/helper/html/index.js +32 -62
- package/node_modules/hono/dist/cjs/helper/proxy/index.js +113 -106
- package/node_modules/hono/dist/cjs/helper/route/index.js +123 -66
- package/node_modules/hono/dist/cjs/helper/ssg/index.js +19 -43
- package/node_modules/hono/dist/cjs/helper/ssg/middleware.js +56 -64
- package/node_modules/hono/dist/cjs/helper/ssg/plugins.js +55 -57
- package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +196 -296
- package/node_modules/hono/dist/cjs/helper/ssg/utils.js +47 -90
- package/node_modules/hono/dist/cjs/helper/streaming/index.js +8 -35
- package/node_modules/hono/dist/cjs/helper/streaming/sse.js +53 -85
- package/node_modules/hono/dist/cjs/helper/streaming/stream.js +23 -53
- package/node_modules/hono/dist/cjs/helper/streaming/text.js +10 -32
- package/node_modules/hono/dist/cjs/helper/streaming/utils.js +13 -32
- package/node_modules/hono/dist/cjs/helper/testing/index.js +16 -31
- package/node_modules/hono/dist/cjs/helper/websocket/index.js +51 -76
- package/node_modules/hono/dist/cjs/hono-base.js +347 -402
- package/node_modules/hono/dist/cjs/hono.js +28 -42
- package/node_modules/hono/dist/cjs/http-exception.js +55 -56
- package/node_modules/hono/dist/cjs/index.js +22 -30
- package/node_modules/hono/dist/cjs/jsx/base.js +241 -353
- package/node_modules/hono/dist/cjs/jsx/children.js +16 -41
- package/node_modules/hono/dist/cjs/jsx/components.js +120 -194
- package/node_modules/hono/dist/cjs/jsx/constants.js +15 -42
- package/node_modules/hono/dist/cjs/jsx/context.js +144 -162
- package/node_modules/hono/dist/cjs/jsx/dom/client.js +49 -69
- package/node_modules/hono/dist/cjs/jsx/dom/components.js +23 -52
- package/node_modules/hono/dist/cjs/jsx/dom/context.js +39 -67
- package/node_modules/hono/dist/cjs/jsx/dom/css.js +143 -158
- package/node_modules/hono/dist/cjs/jsx/dom/hooks/index.js +55 -64
- package/node_modules/hono/dist/cjs/jsx/dom/index.js +114 -173
- package/node_modules/hono/dist/cjs/jsx/dom/intrinsic-element/components.js +282 -375
- package/node_modules/hono/dist/cjs/jsx/dom/jsx-dev-runtime.js +14 -49
- package/node_modules/hono/dist/cjs/jsx/dom/jsx-runtime.js +5 -32
- package/node_modules/hono/dist/cjs/jsx/dom/render.js +471 -589
- package/node_modules/hono/dist/cjs/jsx/dom/server.js +37 -60
- package/node_modules/hono/dist/cjs/jsx/dom/utils.js +7 -30
- package/node_modules/hono/dist/cjs/jsx/hooks/index.js +225 -318
- package/node_modules/hono/dist/cjs/jsx/index.js +86 -135
- package/node_modules/hono/dist/cjs/jsx/intrinsic-element/common.js +20 -43
- package/node_modules/hono/dist/cjs/jsx/intrinsic-element/components.js +117 -168
- package/node_modules/hono/dist/cjs/jsx/intrinsic-elements.js +0 -15
- package/node_modules/hono/dist/cjs/jsx/jsx-dev-runtime.js +14 -39
- package/node_modules/hono/dist/cjs/jsx/jsx-runtime.js +34 -68
- package/node_modules/hono/dist/cjs/jsx/streaming.js +123 -167
- package/node_modules/hono/dist/cjs/jsx/types.js +0 -15
- package/node_modules/hono/dist/cjs/jsx/utils.js +91 -175
- package/node_modules/hono/dist/cjs/middleware/basic-auth/index.js +98 -86
- package/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +92 -109
- package/node_modules/hono/dist/cjs/middleware/body-limit/index.js +66 -72
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +153 -199
- package/node_modules/hono/dist/cjs/middleware/combine/index.js +135 -95
- package/node_modules/hono/dist/cjs/middleware/compress/index.js +73 -88
- package/node_modules/hono/dist/cjs/middleware/context-storage/index.js +49 -41
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +107 -107
- package/node_modules/hono/dist/cjs/middleware/csrf/index.js +101 -72
- package/node_modules/hono/dist/cjs/middleware/etag/digest.js +58 -97
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +71 -91
- package/node_modules/hono/dist/cjs/middleware/ip-restriction/index.js +161 -169
- package/node_modules/hono/dist/cjs/middleware/jsx-renderer/index.js +99 -77
- package/node_modules/hono/dist/cjs/middleware/jwk/index.js +3 -27
- package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +118 -135
- package/node_modules/hono/dist/cjs/middleware/jwt/index.js +9 -38
- package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +120 -141
- package/node_modules/hono/dist/cjs/middleware/language/index.js +7 -35
- package/node_modules/hono/dist/cjs/middleware/language/language.js +184 -179
- package/node_modules/hono/dist/cjs/middleware/logger/index.js +41 -62
- package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +82 -88
- package/node_modules/hono/dist/cjs/middleware/method-override/index.js +101 -102
- package/node_modules/hono/dist/cjs/middleware/mount/index.js +71 -0
- package/node_modules/hono/dist/cjs/middleware/powered-by/index.js +23 -30
- package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +38 -36
- package/node_modules/hono/dist/cjs/middleware/request-id/index.js +3 -27
- package/node_modules/hono/dist/cjs/middleware/request-id/request-id.js +34 -42
- package/node_modules/hono/dist/cjs/middleware/secure-headers/index.js +4 -29
- package/node_modules/hono/dist/cjs/middleware/secure-headers/permissions-policy.js +0 -15
- package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +142 -174
- package/node_modules/hono/dist/cjs/middleware/serve-static/index.js +56 -93
- package/node_modules/hono/dist/cjs/middleware/serve-static/path.js +15 -38
- package/node_modules/hono/dist/cjs/middleware/timeout/index.js +40 -45
- package/node_modules/hono/dist/cjs/middleware/timing/index.js +7 -35
- package/node_modules/hono/dist/cjs/middleware/timing/timing.js +177 -118
- package/node_modules/hono/dist/cjs/middleware/trailing-slash/index.js +85 -58
- package/node_modules/hono/dist/cjs/preset/quick.js +18 -37
- package/node_modules/hono/dist/cjs/preset/tiny.js +15 -33
- package/node_modules/hono/dist/cjs/request/constants.js +5 -27
- package/node_modules/hono/dist/cjs/request.js +334 -343
- package/node_modules/hono/dist/cjs/router/linear-router/index.js +3 -27
- package/node_modules/hono/dist/cjs/router/linear-router/router.js +95 -143
- package/node_modules/hono/dist/cjs/router/pattern-router/index.js +3 -27
- package/node_modules/hono/dist/cjs/router/pattern-router/router.js +41 -63
- package/node_modules/hono/dist/cjs/router/reg-exp-router/index.js +7 -34
- package/node_modules/hono/dist/cjs/router/reg-exp-router/matcher.js +18 -45
- package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +91 -135
- package/node_modules/hono/dist/cjs/router/reg-exp-router/prepared-router.js +92 -161
- package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +98 -147
- package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +73 -89
- package/node_modules/hono/dist/cjs/router/smart-router/index.js +3 -27
- package/node_modules/hono/dist/cjs/router/smart-router/router.js +49 -79
- package/node_modules/hono/dist/cjs/router/trie-router/index.js +3 -27
- package/node_modules/hono/dist/cjs/router/trie-router/node.js +134 -186
- package/node_modules/hono/dist/cjs/router/trie-router/router.js +15 -39
- package/node_modules/hono/dist/cjs/router/utils.js +5 -27
- package/node_modules/hono/dist/cjs/router.js +37 -37
- package/node_modules/hono/dist/cjs/types.js +5 -28
- package/node_modules/hono/dist/cjs/utils/accept.js +177 -228
- package/node_modules/hono/dist/cjs/utils/basic-auth.js +16 -40
- package/node_modules/hono/dist/cjs/utils/body.js +84 -110
- package/node_modules/hono/dist/cjs/utils/buffer.js +43 -91
- package/node_modules/hono/dist/cjs/utils/color.js +35 -44
- package/node_modules/hono/dist/cjs/utils/compress.js +11 -26
- package/node_modules/hono/dist/cjs/utils/concurrent.js +29 -59
- package/node_modules/hono/dist/cjs/utils/constants.js +7 -26
- package/node_modules/hono/dist/cjs/utils/cookie.js +107 -177
- package/node_modules/hono/dist/cjs/utils/crypto.js +30 -61
- package/node_modules/hono/dist/cjs/utils/encode.js +31 -48
- package/node_modules/hono/dist/cjs/utils/filepath.js +23 -54
- package/node_modules/hono/dist/cjs/utils/handler.js +11 -33
- package/node_modules/hono/dist/cjs/utils/headers.js +0 -15
- package/node_modules/hono/dist/cjs/utils/html.js +96 -137
- package/node_modules/hono/dist/cjs/utils/http-status.js +0 -15
- package/node_modules/hono/dist/cjs/utils/ipaddr.js +213 -286
- package/node_modules/hono/dist/cjs/utils/jwt/index.js +14 -27
- package/node_modules/hono/dist/cjs/utils/jwt/jwa.js +25 -42
- package/node_modules/hono/dist/cjs/utils/jwt/jws.js +119 -199
- package/node_modules/hono/dist/cjs/utils/jwt/jwt.js +122 -205
- package/node_modules/hono/dist/cjs/utils/jwt/types.js +122 -159
- package/node_modules/hono/dist/cjs/utils/jwt/utf8.js +9 -28
- package/node_modules/hono/dist/cjs/utils/mime.js +75 -101
- package/node_modules/hono/dist/cjs/utils/stream.js +88 -111
- package/node_modules/hono/dist/cjs/utils/types.js +0 -15
- package/node_modules/hono/dist/cjs/utils/url.js +185 -234
- package/node_modules/hono/dist/cjs/validator/index.js +3 -27
- package/node_modules/hono/dist/cjs/validator/utils.js +0 -15
- package/node_modules/hono/dist/cjs/validator/validator.js +60 -103
- package/node_modules/hono/dist/client/client.js +138 -197
- package/node_modules/hono/dist/client/fetch-result-please.js +59 -56
- package/node_modules/hono/dist/client/index.js +3 -7
- package/node_modules/hono/dist/client/types.js +1 -0
- package/node_modules/hono/dist/client/utils.js +51 -68
- package/node_modules/hono/dist/compose.js +49 -45
- package/node_modules/hono/dist/context.js +376 -426
- package/node_modules/hono/dist/helper/accepts/accepts.js +47 -49
- package/node_modules/hono/dist/helper/accepts/index.js +1 -4
- package/node_modules/hono/dist/helper/adapter/index.js +33 -52
- package/node_modules/hono/dist/helper/conninfo/index.js +1 -0
- package/node_modules/hono/dist/helper/conninfo/types.js +1 -0
- package/node_modules/hono/dist/helper/cookie/index.js +74 -94
- package/node_modules/hono/dist/helper/css/common.js +167 -225
- package/node_modules/hono/dist/helper/css/index.js +132 -137
- package/node_modules/hono/dist/helper/dev/index.js +44 -49
- package/node_modules/hono/dist/helper/factory/index.js +27 -27
- package/node_modules/hono/dist/helper/html/index.js +30 -39
- package/node_modules/hono/dist/helper/proxy/index.js +116 -88
- package/node_modules/hono/dist/helper/route/index.js +121 -43
- package/node_modules/hono/dist/helper/ssg/index.js +3 -18
- package/node_modules/hono/dist/helper/ssg/middleware.js +53 -40
- package/node_modules/hono/dist/helper/ssg/plugins.js +55 -36
- package/node_modules/hono/dist/helper/ssg/ssg.js +198 -283
- package/node_modules/hono/dist/helper/ssg/utils.js +53 -75
- package/node_modules/hono/dist/helper/streaming/index.js +2 -8
- package/node_modules/hono/dist/helper/streaming/sse.js +50 -61
- package/node_modules/hono/dist/helper/streaming/stream.js +22 -31
- package/node_modules/hono/dist/helper/streaming/text.js +9 -10
- package/node_modules/hono/dist/helper/streaming/utils.js +9 -12
- package/node_modules/hono/dist/helper/testing/index.js +16 -10
- package/node_modules/hono/dist/helper/websocket/index.js +49 -53
- package/node_modules/hono/dist/hono-base.js +344 -378
- package/node_modules/hono/dist/hono.js +27 -20
- package/node_modules/hono/dist/http-exception.js +53 -33
- package/node_modules/hono/dist/index.js +19 -6
- package/node_modules/hono/dist/jsx/base.js +233 -328
- package/node_modules/hono/dist/jsx/children.js +16 -20
- package/node_modules/hono/dist/jsx/components.js +118 -171
- package/node_modules/hono/dist/jsx/constants.js +9 -15
- package/node_modules/hono/dist/jsx/context.js +153 -150
- package/node_modules/hono/dist/jsx/dom/client.js +45 -48
- package/node_modules/hono/dist/jsx/dom/components.js +21 -29
- package/node_modules/hono/dist/jsx/dom/context.js +37 -44
- package/node_modules/hono/dist/jsx/dom/css.js +142 -147
- package/node_modules/hono/dist/jsx/dom/hooks/index.js +52 -40
- package/node_modules/hono/dist/jsx/dom/index.js +71 -134
- package/node_modules/hono/dist/jsx/dom/intrinsic-element/components.js +275 -359
- package/node_modules/hono/dist/jsx/dom/jsx-dev-runtime.js +14 -18
- package/node_modules/hono/dist/jsx/dom/jsx-runtime.js +2 -8
- package/node_modules/hono/dist/jsx/dom/render.js +492 -595
- package/node_modules/hono/dist/jsx/dom/server.js +33 -29
- package/node_modules/hono/dist/jsx/dom/utils.js +6 -8
- package/node_modules/hono/dist/jsx/hooks/index.js +236 -308
- package/node_modules/hono/dist/jsx/index.js +45 -98
- package/node_modules/hono/dist/jsx/intrinsic-element/common.js +20 -22
- package/node_modules/hono/dist/jsx/intrinsic-element/components.js +113 -154
- package/node_modules/hono/dist/jsx/intrinsic-elements.js +1 -0
- package/node_modules/hono/dist/jsx/jsx-dev-runtime.js +12 -16
- package/node_modules/hono/dist/jsx/jsx-runtime.js +29 -42
- package/node_modules/hono/dist/jsx/streaming.js +120 -143
- package/node_modules/hono/dist/jsx/types.js +1 -0
- package/node_modules/hono/dist/jsx/utils.js +105 -168
- package/node_modules/hono/dist/middleware/basic-auth/index.js +96 -63
- package/node_modules/hono/dist/middleware/bearer-auth/index.js +93 -89
- package/node_modules/hono/dist/middleware/body-limit/index.js +66 -51
- package/node_modules/hono/dist/middleware/cache/index.js +165 -190
- package/node_modules/hono/dist/middleware/combine/index.js +134 -73
- package/node_modules/hono/dist/middleware/compress/index.js +75 -69
- package/node_modules/hono/dist/middleware/context-storage/index.js +49 -19
- package/node_modules/hono/dist/middleware/cors/index.js +107 -86
- package/node_modules/hono/dist/middleware/csrf/index.js +103 -53
- package/node_modules/hono/dist/middleware/etag/digest.js +59 -77
- package/node_modules/hono/dist/middleware/etag/index.js +71 -70
- package/node_modules/hono/dist/middleware/ip-restriction/index.js +163 -158
- package/node_modules/hono/dist/middleware/jsx-renderer/index.js +97 -54
- package/node_modules/hono/dist/middleware/jwk/index.js +1 -4
- package/node_modules/hono/dist/middleware/jwk/jwk.js +116 -112
- package/node_modules/hono/dist/middleware/jwt/index.js +2 -10
- package/node_modules/hono/dist/middleware/jwt/jwt.js +114 -114
- package/node_modules/hono/dist/middleware/language/index.js +2 -15
- package/node_modules/hono/dist/middleware/language/language.js +180 -154
- package/node_modules/hono/dist/middleware/logger/index.js +43 -37
- package/node_modules/hono/dist/middleware/method-not-allowed/index.js +81 -66
- package/node_modules/hono/dist/middleware/method-override/index.js +101 -81
- package/node_modules/hono/dist/middleware/mount/index.js +70 -0
- package/node_modules/hono/dist/middleware/powered-by/index.js +23 -9
- package/node_modules/hono/dist/middleware/pretty-json/index.js +39 -16
- package/node_modules/hono/dist/middleware/request-id/index.js +1 -4
- package/node_modules/hono/dist/middleware/request-id/request-id.js +33 -20
- package/node_modules/hono/dist/middleware/secure-headers/index.js +1 -5
- package/node_modules/hono/dist/middleware/secure-headers/permissions-policy.js +1 -0
- package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +144 -155
- package/node_modules/hono/dist/middleware/serve-static/index.js +56 -72
- package/node_modules/hono/dist/middleware/serve-static/path.js +15 -17
- package/node_modules/hono/dist/middleware/timeout/index.js +40 -23
- package/node_modules/hono/dist/middleware/timing/index.js +2 -9
- package/node_modules/hono/dist/middleware/timing/timing.js +177 -97
- package/node_modules/hono/dist/middleware/trailing-slash/index.js +85 -37
- package/node_modules/hono/dist/preset/quick.js +17 -15
- package/node_modules/hono/dist/preset/tiny.js +14 -11
- package/node_modules/hono/dist/request/constants.js +4 -5
- package/node_modules/hono/dist/request.js +328 -316
- package/node_modules/hono/dist/router/linear-router/index.js +1 -4
- package/node_modules/hono/dist/router/linear-router/router.js +93 -120
- package/node_modules/hono/dist/router/pattern-router/index.js +1 -4
- package/node_modules/hono/dist/router/pattern-router/router.js +39 -40
- package/node_modules/hono/dist/router/reg-exp-router/index.js +1 -7
- package/node_modules/hono/dist/router/reg-exp-router/matcher.js +17 -23
- package/node_modules/hono/dist/router/reg-exp-router/node.js +87 -110
- package/node_modules/hono/dist/router/reg-exp-router/prepared-router.js +89 -137
- package/node_modules/hono/dist/router/reg-exp-router/router.js +92 -129
- package/node_modules/hono/dist/router/reg-exp-router/trie.js +69 -64
- package/node_modules/hono/dist/router/smart-router/index.js +1 -4
- package/node_modules/hono/dist/router/smart-router/router.js +46 -55
- package/node_modules/hono/dist/router/trie-router/index.js +1 -4
- package/node_modules/hono/dist/router/trie-router/node.js +131 -162
- package/node_modules/hono/dist/router/trie-router/router.js +11 -14
- package/node_modules/hono/dist/router/utils.js +4 -5
- package/node_modules/hono/dist/router.js +35 -14
- package/node_modules/hono/dist/types/adapter/aws-lambda/conninfo.d.ts +4 -1
- package/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +150 -104
- package/node_modules/hono/dist/types/adapter/aws-lambda/index.d.ts +4 -8
- package/node_modules/hono/dist/types/adapter/aws-lambda/types.d.ts +152 -122
- package/node_modules/hono/dist/types/adapter/bun/conninfo.d.ts +4 -1
- package/node_modules/hono/dist/types/adapter/bun/index.d.ts +6 -10
- package/node_modules/hono/dist/types/adapter/bun/serve-static.d.ts +7 -2
- package/node_modules/hono/dist/types/adapter/bun/server.d.ts +4 -5
- package/node_modules/hono/dist/types/adapter/bun/ssg.d.ts +5 -1
- package/node_modules/hono/dist/types/adapter/bun/websocket.d.ts +31 -16
- package/node_modules/hono/dist/types/adapter/cloudflare-pages/conninfo.d.ts +4 -1
- package/node_modules/hono/dist/types/adapter/cloudflare-pages/handler.d.ts +29 -17
- package/node_modules/hono/dist/types/adapter/cloudflare-pages/index.d.ts +3 -7
- package/node_modules/hono/dist/types/adapter/cloudflare-workers/conninfo.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/cloudflare-workers/index.d.ts +4 -7
- package/node_modules/hono/dist/types/adapter/cloudflare-workers/serve-static-module.d.ts +5 -2
- package/node_modules/hono/dist/types/adapter/cloudflare-workers/serve-static.d.ts +10 -5
- package/node_modules/hono/dist/types/adapter/cloudflare-workers/utils.d.ts +10 -2
- package/node_modules/hono/dist/types/adapter/cloudflare-workers/websocket.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/deno/conninfo.d.ts +4 -1
- package/node_modules/hono/dist/types/adapter/deno/deno.d.ts +1 -0
- package/node_modules/hono/dist/types/adapter/deno/index.d.ts +5 -8
- package/node_modules/hono/dist/types/adapter/deno/serve-static.d.ts +7 -2
- package/node_modules/hono/dist/types/adapter/deno/ssg.d.ts +5 -1
- package/node_modules/hono/dist/types/adapter/deno/websocket.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/lambda-edge/conninfo.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/lambda-edge/handler.d.ts +83 -57
- package/node_modules/hono/dist/types/adapter/lambda-edge/index.d.ts +3 -7
- package/node_modules/hono/dist/types/adapter/netlify/conninfo.d.ts +4 -1
- package/node_modules/hono/dist/types/adapter/netlify/handler.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/netlify/index.d.ts +3 -5
- package/node_modules/hono/dist/types/adapter/netlify/mod.d.ts +3 -2
- package/node_modules/hono/dist/types/adapter/service-worker/handler.d.ts +9 -7
- package/node_modules/hono/dist/types/adapter/service-worker/index.d.ts +7 -9
- package/node_modules/hono/dist/types/adapter/service-worker/types.d.ts +12 -7
- package/node_modules/hono/dist/types/adapter/vercel/conninfo.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/vercel/handler.d.ts +6 -1
- package/node_modules/hono/dist/types/adapter/vercel/index.d.ts +3 -6
- package/node_modules/hono/dist/types/client/client.d.ts +5 -3
- package/node_modules/hono/dist/types/client/fetch-result-please.d.ts +21 -19
- package/node_modules/hono/dist/types/client/index.d.ts +5 -7
- package/node_modules/hono/dist/types/client/types.d.ts +134 -159
- package/node_modules/hono/dist/types/client/utils.d.ts +6 -4
- package/node_modules/hono/dist/types/compose.d.ts +4 -2
- package/node_modules/hono/dist/types/context.d.ts +335 -335
- package/node_modules/hono/dist/types/helper/accepts/accepts.d.ts +11 -9
- package/node_modules/hono/dist/types/helper/accepts/index.d.ts +2 -5
- package/node_modules/hono/dist/types/helper/adapter/index.d.ts +4 -6
- package/node_modules/hono/dist/types/helper/conninfo/index.d.ts +2 -5
- package/node_modules/hono/dist/types/helper/conninfo/types.d.ts +25 -23
- package/node_modules/hono/dist/types/helper/cookie/index.d.ts +10 -12
- package/node_modules/hono/dist/types/helper/css/common.d.ts +11 -9
- package/node_modules/hono/dist/types/helper/css/index.d.ts +26 -28
- package/node_modules/hono/dist/types/helper/dev/index.d.ts +10 -12
- package/node_modules/hono/dist/types/helper/factory/index.d.ts +26 -73
- package/node_modules/hono/dist/types/helper/html/index.d.ts +4 -7
- package/node_modules/hono/dist/types/helper/proxy/index.d.ts +20 -22
- package/node_modules/hono/dist/types/helper/route/index.d.ts +6 -4
- package/node_modules/hono/dist/types/helper/ssg/index.d.ts +4 -7
- package/node_modules/hono/dist/types/helper/ssg/middleware.d.ts +8 -6
- package/node_modules/hono/dist/types/helper/ssg/plugins.d.ts +3 -1
- package/node_modules/hono/dist/types/helper/ssg/ssg.d.ts +38 -36
- package/node_modules/hono/dist/types/helper/ssg/utils.d.ts +5 -3
- package/node_modules/hono/dist/types/helper/streaming/index.d.ts +4 -8
- package/node_modules/hono/dist/types/helper/streaming/sse.d.ts +10 -8
- package/node_modules/hono/dist/types/helper/streaming/stream.d.ts +4 -2
- package/node_modules/hono/dist/types/helper/streaming/text.d.ts +4 -2
- package/node_modules/hono/dist/types/helper/streaming/utils.d.ts +2 -0
- package/node_modules/hono/dist/types/helper/testing/index.d.ts +7 -9
- package/node_modules/hono/dist/types/helper/websocket/index.d.ts +30 -33
- package/node_modules/hono/dist/types/hono-base.d.ts +210 -210
- package/node_modules/hono/dist/types/hono.d.ts +11 -10
- package/node_modules/hono/dist/types/http-exception.d.ts +20 -22
- package/node_modules/hono/dist/types/index.d.ts +6 -37
- package/node_modules/hono/dist/types/jsx/base.d.ts +40 -35
- package/node_modules/hono/dist/types/jsx/children.d.ts +8 -6
- package/node_modules/hono/dist/types/jsx/components.d.ts +8 -5
- package/node_modules/hono/dist/types/jsx/constants.d.ts +2 -0
- package/node_modules/hono/dist/types/jsx/context.d.ts +9 -6
- package/node_modules/hono/dist/types/jsx/dom/client.d.ts +9 -10
- package/node_modules/hono/dist/types/jsx/dom/components.d.ts +9 -6
- package/node_modules/hono/dist/types/jsx/dom/context.d.ts +3 -1
- package/node_modules/hono/dist/types/jsx/dom/css.d.ts +27 -30
- package/node_modules/hono/dist/types/jsx/dom/hooks/index.d.ts +11 -12
- package/node_modules/hono/dist/types/jsx/dom/index.d.ts +79 -83
- package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +9 -5
- package/node_modules/hono/dist/types/jsx/dom/jsx-dev-runtime.d.ts +4 -6
- package/node_modules/hono/dist/types/jsx/dom/jsx-runtime.d.ts +3 -7
- package/node_modules/hono/dist/types/jsx/dom/render.d.ts +32 -50
- package/node_modules/hono/dist/types/jsx/dom/server.d.ts +87 -85
- package/node_modules/hono/dist/types/jsx/dom/utils.d.ts +2 -0
- package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +16 -16
- package/node_modules/hono/dist/types/jsx/index.d.ts +71 -73
- package/node_modules/hono/dist/types/jsx/intrinsic-element/common.d.ts +3 -1
- package/node_modules/hono/dist/types/jsx/intrinsic-element/components.d.ts +7 -4
- package/node_modules/hono/dist/types/jsx/intrinsic-elements.d.ts +708 -708
- package/node_modules/hono/dist/types/jsx/jsx-dev-runtime.d.ts +4 -7
- package/node_modules/hono/dist/types/jsx/jsx-runtime.d.ts +7 -10
- package/node_modules/hono/dist/types/jsx/streaming.d.ts +9 -10
- package/node_modules/hono/dist/types/jsx/types.d.ts +12 -13
- package/node_modules/hono/dist/types/jsx/utils.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/basic-auth/index.d.ts +17 -19
- package/node_modules/hono/dist/types/middleware/bearer-auth/index.d.ts +46 -48
- package/node_modules/hono/dist/types/middleware/body-limit/index.d.ts +6 -8
- package/node_modules/hono/dist/types/middleware/cache/index.d.ts +13 -15
- package/node_modules/hono/dist/types/middleware/combine/index.d.ts +7 -7
- package/node_modules/hono/dist/types/middleware/compress/index.d.ts +9 -10
- package/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +4 -6
- package/node_modules/hono/dist/types/middleware/cors/index.d.ts +10 -12
- package/node_modules/hono/dist/types/middleware/csrf/index.d.ts +6 -8
- package/node_modules/hono/dist/types/middleware/etag/digest.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/etag/index.d.ts +6 -8
- package/node_modules/hono/dist/types/middleware/ip-restriction/index.d.ts +11 -12
- package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +11 -11
- package/node_modules/hono/dist/types/middleware/jwk/index.d.ts +2 -1
- package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +20 -23
- package/node_modules/hono/dist/types/middleware/jwt/index.d.ts +7 -6
- package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +28 -30
- package/node_modules/hono/dist/types/middleware/language/index.d.ts +6 -5
- package/node_modules/hono/dist/types/middleware/language/language.d.ts +42 -44
- package/node_modules/hono/dist/types/middleware/logger/index.d.ts +3 -5
- package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +7 -9
- package/node_modules/hono/dist/types/middleware/method-override/index.d.ts +14 -16
- package/node_modules/hono/dist/types/middleware/mount/index.d.ts +46 -0
- package/node_modules/hono/dist/types/middleware/powered-by/index.d.ts +8 -10
- package/node_modules/hono/dist/types/middleware/pretty-json/index.d.ts +18 -20
- package/node_modules/hono/dist/types/middleware/request-id/index.d.ts +6 -5
- package/node_modules/hono/dist/types/middleware/request-id/request-id.d.ts +9 -11
- package/node_modules/hono/dist/types/middleware/secure-headers/index.d.ts +6 -6
- package/node_modules/hono/dist/types/middleware/secure-headers/permissions-policy.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/secure-headers/secure-headers.d.ts +55 -57
- package/node_modules/hono/dist/types/middleware/serve-static/index.d.ts +24 -24
- package/node_modules/hono/dist/types/middleware/serve-static/path.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/timeout/index.d.ts +5 -7
- package/node_modules/hono/dist/types/middleware/timing/index.d.ts +6 -5
- package/node_modules/hono/dist/types/middleware/timing/timing.d.ts +17 -20
- package/node_modules/hono/dist/types/middleware/trailing-slash/index.d.ts +35 -37
- package/node_modules/hono/dist/types/preset/quick.d.ts +6 -9
- package/node_modules/hono/dist/types/preset/tiny.d.ts +6 -9
- package/node_modules/hono/dist/types/request/constants.d.ts +2 -0
- package/node_modules/hono/dist/types/request.d.ts +288 -286
- package/node_modules/hono/dist/types/router/linear-router/index.d.ts +2 -5
- package/node_modules/hono/dist/types/router/linear-router/router.d.ts +7 -5
- package/node_modules/hono/dist/types/router/pattern-router/index.d.ts +2 -5
- package/node_modules/hono/dist/types/router/pattern-router/router.d.ts +7 -5
- package/node_modules/hono/dist/types/router/reg-exp-router/index.d.ts +3 -6
- package/node_modules/hono/dist/types/router/reg-exp-router/matcher.d.ts +3 -1
- package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +6 -4
- package/node_modules/hono/dist/types/router/reg-exp-router/prepared-router.d.ts +11 -10
- package/node_modules/hono/dist/types/router/reg-exp-router/router.d.ts +10 -9
- package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +7 -5
- package/node_modules/hono/dist/types/router/smart-router/index.d.ts +2 -5
- package/node_modules/hono/dist/types/router/smart-router/router.d.ts +11 -9
- package/node_modules/hono/dist/types/router/trie-router/index.d.ts +2 -5
- package/node_modules/hono/dist/types/router/trie-router/node.d.ts +6 -4
- package/node_modules/hono/dist/types/router/trie-router/router.d.ts +7 -5
- package/node_modules/hono/dist/types/router/utils.d.ts +2 -0
- package/node_modules/hono/dist/types/router.d.ts +23 -22
- package/node_modules/hono/dist/types/types.d.ts +146 -505
- package/node_modules/hono/dist/types/utils/accept.d.ts +5 -3
- package/node_modules/hono/dist/types/utils/basic-auth.d.ts +4 -2
- package/node_modules/hono/dist/types/utils/body.d.ts +46 -50
- package/node_modules/hono/dist/types/utils/buffer.d.ts +7 -5
- package/node_modules/hono/dist/types/utils/color.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/compress.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/concurrent.d.ts +6 -4
- package/node_modules/hono/dist/types/utils/constants.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/cookie.d.ts +20 -18
- package/node_modules/hono/dist/types/utils/crypto.d.ts +5 -7
- package/node_modules/hono/dist/types/utils/encode.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/filepath.d.ts +5 -3
- package/node_modules/hono/dist/types/utils/handler.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/headers.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/html.d.ts +11 -9
- package/node_modules/hono/dist/types/utils/http-status.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/ipaddr.d.ts +4 -6
- package/node_modules/hono/dist/types/utils/jwt/index.d.ts +18 -12
- package/node_modules/hono/dist/types/utils/jwt/jwa.d.ts +15 -13
- package/node_modules/hono/dist/types/utils/jwt/jws.d.ts +4 -7
- package/node_modules/hono/dist/types/utils/jwt/jwt.d.ts +26 -29
- package/node_modules/hono/dist/types/utils/jwt/types.d.ts +48 -45
- package/node_modules/hono/dist/types/utils/jwt/utf8.d.ts +2 -0
- package/node_modules/hono/dist/types/utils/mime.d.ts +60 -57
- package/node_modules/hono/dist/types/utils/stream.d.ts +27 -25
- package/node_modules/hono/dist/types/utils/types.d.ts +15 -27
- package/node_modules/hono/dist/types/utils/url.d.ts +2 -0
- package/node_modules/hono/dist/types/validator/index.d.ts +3 -7
- package/node_modules/hono/dist/types/validator/utils.d.ts +6 -8
- package/node_modules/hono/dist/types/validator/validator.d.ts +11 -9
- package/node_modules/hono/dist/types.js +4 -6
- package/node_modules/hono/dist/utils/accept.js +185 -215
- package/node_modules/hono/dist/utils/basic-auth.js +18 -21
- package/node_modules/hono/dist/utils/body.js +87 -92
- package/node_modules/hono/dist/utils/buffer.js +44 -71
- package/node_modules/hono/dist/utils/color.js +33 -21
- package/node_modules/hono/dist/utils/compress.js +11 -5
- package/node_modules/hono/dist/utils/concurrent.js +29 -38
- package/node_modules/hono/dist/utils/constants.js +7 -5
- package/node_modules/hono/dist/utils/cookie.js +114 -163
- package/node_modules/hono/dist/utils/crypto.js +30 -40
- package/node_modules/hono/dist/utils/encode.js +30 -26
- package/node_modules/hono/dist/utils/filepath.js +23 -33
- package/node_modules/hono/dist/utils/handler.js +10 -11
- package/node_modules/hono/dist/utils/headers.js +1 -0
- package/node_modules/hono/dist/utils/html.js +97 -117
- package/node_modules/hono/dist/utils/http-status.js +1 -0
- package/node_modules/hono/dist/utils/ipaddr.js +227 -279
- package/node_modules/hono/dist/utils/jwt/index.js +12 -4
- package/node_modules/hono/dist/utils/jwt/jwa.js +24 -20
- package/node_modules/hono/dist/utils/jwt/jws.js +115 -174
- package/node_modules/hono/dist/utils/jwt/jwt.js +120 -197
- package/node_modules/hono/dist/utils/jwt/types.js +79 -95
- package/node_modules/hono/dist/utils/jwt/utf8.js +9 -7
- package/node_modules/hono/dist/utils/mime.js +74 -79
- package/node_modules/hono/dist/utils/stream.js +86 -88
- package/node_modules/hono/dist/utils/types.js +1 -0
- package/node_modules/hono/dist/utils/url.js +187 -215
- package/node_modules/hono/dist/validator/index.js +1 -4
- package/node_modules/hono/dist/validator/utils.js +1 -0
- package/node_modules/hono/dist/validator/validator.js +61 -83
- package/node_modules/hono/package.json +36 -28
- package/node_modules/ip-address/README.md +114 -114
- package/node_modules/ip-address/dist/common.d.ts +5 -0
- package/node_modules/ip-address/dist/common.js +12 -0
- package/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/ip-address/dist/ipv4.d.ts +10 -3
- package/node_modules/ip-address/dist/ipv4.js +27 -5
- package/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/ip-address/dist/ipv6.d.ts +5 -1
- package/node_modules/ip-address/dist/ipv6.js +18 -3
- package/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/ip-address/package.json +1 -1
- package/node_modules/proxy-addr/HISTORY.md +60 -54
- package/node_modules/proxy-addr/README.md +1 -1
- package/node_modules/proxy-addr/index.js +32 -0
- package/node_modules/proxy-addr/package.json +8 -4
- package/package.json +1 -1
|
@@ -5,9 +5,14 @@ const hash = value => createHash('sha256').update(value).digest('hex');
|
|
|
5
5
|
const fail = () => { throw Object.assign(new Error('채널 분석 기록을 확인하지 못했습니다. 자동 재제출하지 않습니다.'), { code: 'DURABLE_CHANNEL_JOURNAL_UNAVAILABLE', paidReplayAllowed: false }); };
|
|
6
6
|
// Immutable files with exclusive creation. No truncation, overwrite, stale-lock
|
|
7
7
|
// removal, expiry, TTL or ambiguous-write replay; never stores a PAT or media.
|
|
8
|
-
export function createChannelFileJournal({ stateDir, authorityFingerprint }) {
|
|
8
|
+
export function createChannelFileJournal({ stateDir, authorityFingerprint, namespace = 'channel-analysis-v1' }) {
|
|
9
9
|
if (typeof stateDir !== 'string' || !path.isAbsolute(stateDir) || !/^[a-f0-9]{64}$/u.test(authorityFingerprint || '')) fail();
|
|
10
|
-
|
|
10
|
+
if (!['channel-analysis-v1', 'voice-recommendation-v1', 'review-plan-v1', 'studio-plan-v1'].includes(namespace)) fail();
|
|
11
|
+
const handles = new WeakMap(), root = path.join(stateDir, namespace);
|
|
12
|
+
const maxEntries = namespace === 'voice-recommendation-v1' ? 4096 : 32;
|
|
13
|
+
// Review output includes full source evidence (16 MiB artifact budget).
|
|
14
|
+
// Keep the original channel/voice bounds; never trim evidence to fit them.
|
|
15
|
+
const maxResultBytes = namespace === 'review-plan-v1' ? 20 * 1024 * 1024 : namespace === 'studio-plan-v1' ? 10 * 1024 * 1024 : namespace === 'voice-recommendation-v1' ? 65536 : 1200000;
|
|
11
16
|
function directory(dir, create = false, privateOnly = true) {
|
|
12
17
|
if (create) { try { mkdirSync(dir, { mode: 0o700 }); } catch (e) { if (e.code !== 'EEXIST') fail(); } }
|
|
13
18
|
const st = lstatSync(dir); if (!st.isDirectory() || st.isSymbolicLink() || st.uid !== process.getuid() || st.mode & (privateOnly ? 0o077 : 0o022)) fail();
|
|
@@ -40,14 +45,14 @@ export function createChannelFileJournal({ stateDir, authorityFingerprint }) {
|
|
|
40
45
|
async lookup(identity, { assertCurrent }) { assertCurrent(); directory(root); const value = loaded(identity); return value ? envelope(value, assertCurrent) : null; },
|
|
41
46
|
async claim(identity, { assertCurrent }) { assertCurrent(); directory(root); const existing = loaded(identity); if (existing) return envelope(existing, assertCurrent);
|
|
42
47
|
// A full journal is recoverable, never silently pruned to make room.
|
|
43
|
-
if (readdirSync(root).length >=
|
|
48
|
+
if (readdirSync(root).length >= maxEntries) fail(); const dir = location(identity);
|
|
44
49
|
try { mkdirSync(dir, { mode: 0o700 }); } catch (e) { if (e.code !== 'EEXIST') fail(); const raced = loaded(identity); if (!raced) fail(); return envelope(raced, assertCurrent); }
|
|
45
|
-
if (readdirSync(root).length >
|
|
50
|
+
if (readdirSync(root).length > maxEntries) fail();
|
|
46
51
|
const record = { version: 1, ...identity, localSubmissionId: randomUUID(), createdAt: Date.now(), state: 'submitted', jobId: '' };
|
|
47
52
|
assertCurrent(); exclusive(path.join(dir, 'intent.json'), { authorityFingerprint, record }); assertCurrent(); return envelope({ dir, record }, assertCurrent, true);
|
|
48
53
|
},
|
|
49
54
|
async rememberJob(handle, jobId) { const s = state(handle); if (!/^epoch-v2-[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u.test(jobId || '')) fail(); exclusive(path.join(s.dir, 'accepted.json'), { authorityFingerprint, jobId }); s.record = { ...s.record, jobId }; s.check(); },
|
|
50
55
|
async markResult(handle, { state: status }) { const s = state(handle); if (!s.record.jobId || !['unknown', 'completed', 'failed'].includes(status)) fail(); exclusive(path.join(s.dir, 'terminal.json'), { jobId: s.record.jobId, state: status }); s.check(); },
|
|
51
|
-
async persistResult(identity, result, check) { check(); const value = loaded(identity); if (!value || value.record.jobId !== result.backgroundJobId) fail(); exclusive(path.join(value.dir, 'result.json'), result,
|
|
56
|
+
async persistResult(identity, result, check) { check(); const value = loaded(identity); if (!value || value.record.jobId !== result.backgroundJobId) fail(); exclusive(path.join(value.dir, 'result.json'), result, maxResultBytes); check(); return true; },
|
|
52
57
|
});
|
|
53
58
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { captureDurableChannelInput, captureDurableChannelOutput, DURABLE_CHANNEL_CONTRACT_VERSION,
|
|
2
|
-
DURABLE_CHANNEL_MODEL, DURABLE_CHANNEL_LIMITS, DURABLE_CHANNEL_TYPES, DURABLE_CHANNEL_OUTPUT_FIELDS, DURABLE_CHANNEL_PATHS } from './durable-channel-contract.mjs';
|
|
2
|
+
DURABLE_CHANNEL_MODEL, durableChannelCapabilityModels, DURABLE_CHANNEL_LIMITS, DURABLE_CHANNEL_TYPES, DURABLE_CHANNEL_OUTPUT_FIELDS, DURABLE_CHANNEL_PATHS } from './durable-channel-contract.mjs';
|
|
3
3
|
|
|
4
4
|
export const durableChannelPath = operation => DURABLE_CHANNEL_PATHS[operation];
|
|
5
5
|
export const durableChannelCapabilityPath = operation => `${durableChannelPath(operation)}/execution-capability`;
|
|
@@ -85,7 +85,7 @@ export function validateDurableChannelCapability(value, operation) {
|
|
|
85
85
|
const captured = object(value);
|
|
86
86
|
if (captured.version !== 1 || captured.contractVersion !== DURABLE_CHANNEL_CONTRACT_VERSION || captured.operation !== operation
|
|
87
87
|
|| !['legacy-sync', 'queue-v2', 'unavailable'].includes(captured.mode)) fail('RESPONSE_INVALID');
|
|
88
|
-
exactConstants(captured.model,
|
|
88
|
+
exactConstants(captured.model, durableChannelCapabilityModels(operation)); exactConstants(captured.limits, DURABLE_CHANNEL_LIMITS);
|
|
89
89
|
const allowed = ['version', 'contractVersion', 'operation', 'mode', 'model', 'limits', ...(captured.mode === 'queue-v2'
|
|
90
90
|
? ['paidReplayAllowed', 'recovery'] : captured.mode === 'unavailable' ? ['reason'] : [])];
|
|
91
91
|
if (Object.keys(captured).some(key => !allowed.includes(key)) || captured.mode === 'queue-v2'
|
|
@@ -116,8 +116,8 @@ export function validateCompletedDurableChannelJob(value, id) {
|
|
|
116
116
|
|| !Array.isArray(result.accountCostEvents) || result.accountCostEvents.length > DURABLE_CHANNEL_LIMITS.maxStages) fail('RESPONSE_INVALID');
|
|
117
117
|
const count = result.accountCostEvents.length;
|
|
118
118
|
if (result.operation === 'channel-style' && (count < 1 || count > 2)
|
|
119
|
-
|| count > 0 && result.
|
|
120
|
-
|| (count ? result.
|
|
119
|
+
|| result.operation === 'voice-recommend' && (count > 2 || count === 0 && (!['edge', 'supertonic2'].includes(result.voiceRecommendation?.ttsProvider) || result.voiceRecommendation?.sampleVoiceProfile !== null))
|
|
120
|
+
|| (count ? !(result.operation === 'voice-recommend' ? ['evolink', 'kie'] : ['evolink']).includes(result.provider) : result.provider !== 'none')) fail('RESPONSE_INVALID');
|
|
121
121
|
let output; try { output = captureDurableChannelOutput(Object.fromEntries(DURABLE_CHANNEL_OUTPUT_FIELDS
|
|
122
122
|
.filter(key => result[key] !== undefined).map(key => [key, result[key]]))); } catch { fail('RESPONSE_INVALID'); }
|
|
123
123
|
const ids = new Set();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Native cut/review planning only; media analysis, TTS and rendering are separate.
|
|
2
|
+
import { normalizeReviewProductionInput, validateReviewProductionPlan } from './review-production-contract.mjs';
|
|
3
|
+
export const DURABLE_REMAKE_CONTRACT_VERSION = 1;
|
|
4
|
+
export const DURABLE_REMAKE_PATHS = Object.freeze({ 'remake-versions': '/api/tracker/gemini/remake-versions', 'review-plan': '/api/tracker/review/plan' });
|
|
5
|
+
export const DURABLE_REMAKE_TYPES = Object.freeze({ 'remake-versions': 'durable-remake:versions', 'review-plan': 'durable-remake:review-plan' });
|
|
6
|
+
export const DURABLE_REMAKE_MODEL = Object.freeze({ 'remake-versions': 'gemini-3-flash', 'review-plan': 'gemini-3.8-flash' });
|
|
7
|
+
const legacyModels = Object.freeze({ 'remake-versions': 'gemini-3-flash' });
|
|
8
|
+
// Already-installed remake clients compare the complete capability map.
|
|
9
|
+
export const durableRemakeCapabilityModels = operation => operation === 'remake-versions' ? legacyModels : DURABLE_REMAKE_MODEL;
|
|
10
|
+
export const DURABLE_REMAKE_LIMITS = Object.freeze({ maxInputBytes: 25 * 1024 * 1024, maxOutputBytes: 16 * 1024 * 1024, maxProjectId: 160, maxTargetId: 240, maxStages: 66 });
|
|
11
|
+
export const DURABLE_REMAKE_OUTPUT_FIELDS = Object.freeze(['operation','projectId','targetId','analysisId','inputKey','presetId','preserveOrder','rerollCount','targetTime','versionCount','promptFrameworks','summaryBody','summaryHeadline','sourceVideos','versions','reviewPlan']);
|
|
12
|
+
const inputFields = ['operation','projectId','targetId','analysisId','inputKey','analysis','presetId','preserveOrder','rerollCount','targetTime','versionCount'];
|
|
13
|
+
const presets = ['tikitaka','snack','recap','deep','shopping','tts','tutorial','highlight','cinema'];
|
|
14
|
+
const invalid = output => Object.assign(new Error(`DURABLE_REMAKE_${output ? 'OUTPUT' : 'INPUT'}_INVALID`), { code: `DURABLE_REMAKE_${output ? 'OUTPUT' : 'INPUT'}_INVALID`, statusCode: output ? 502 : 400 });
|
|
15
|
+
function reject() { throw invalid(false); }
|
|
16
|
+
function clone(value, state, depth = 0) {
|
|
17
|
+
if (++state.nodes > 1_500_000 || depth > 40) reject();
|
|
18
|
+
if (value === null || typeof value === 'boolean') return value;
|
|
19
|
+
if (typeof value === 'number') { if (!Number.isFinite(value)) reject(); return value; }
|
|
20
|
+
if (typeof value === 'string') { if (!value.isWellFormed() || /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(value)) reject(); return value; }
|
|
21
|
+
if (!value || typeof value !== 'object' || state.seen.has(value)) reject();
|
|
22
|
+
state.seen.add(value); const d = Object.getOwnPropertyDescriptors(value); let out;
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
const n = d.length.value;
|
|
25
|
+
if (Object.getPrototypeOf(value) !== Array.prototype || n > 100000 || Reflect.ownKeys(d).length !== n + 1) reject();
|
|
26
|
+
out = Array.from({ length: n }, (_, i) => { if (!d[i]?.enumerable || !('value' in d[i])) reject(); return clone(d[i].value, state, depth + 1); });
|
|
27
|
+
} else {
|
|
28
|
+
if (![Object.prototype, null].includes(Object.getPrototypeOf(value)) || Reflect.ownKeys(d).some(k => typeof k !== 'string' || ['__proto__','constructor','prototype'].includes(k) || !d[k].enumerable || !('value' in d[k]))) reject();
|
|
29
|
+
out = Object.fromEntries(Object.entries(d).map(([key, field]) => [key, clone(field.value, state, depth + 1)]));
|
|
30
|
+
}
|
|
31
|
+
state.seen.delete(value); return Object.freeze(out);
|
|
32
|
+
}
|
|
33
|
+
function capture(value, fields, maxBytes) {
|
|
34
|
+
const body = clone(value, { nodes: 0, seen: new WeakSet() });
|
|
35
|
+
if (!body || Array.isArray(body) || typeof body !== 'object' || Object.keys(body).some(k => !fields.includes(k)) || new TextEncoder().encode(JSON.stringify(body)).byteLength > maxBytes) reject();
|
|
36
|
+
return body;
|
|
37
|
+
}
|
|
38
|
+
function text(value, limit, empty = false) { if (typeof value !== 'string' || value.length > limit || !empty && !value.trim()) reject(); }
|
|
39
|
+
function identity(body) {
|
|
40
|
+
if (!Object.hasOwn(DURABLE_REMAKE_TYPES, body.operation)) reject();
|
|
41
|
+
for (const [key, limit] of [['projectId',160],['targetId',240], ...(body.operation === 'review-plan' ? [] : [['analysisId',240],['inputKey',4096]])]) {
|
|
42
|
+
text(body[key], limit); if (body[key].trim() !== body[key] || /[\u0000-\u001f\u007f]/u.test(body[key])) reject();
|
|
43
|
+
}
|
|
44
|
+
if (body.operation === 'review-plan') return;
|
|
45
|
+
if (!presets.includes(body.presetId) || typeof body.preserveOrder !== 'boolean' || !Number.isSafeInteger(body.rerollCount) || body.rerollCount < 0 || body.rerollCount > 1e6) reject();
|
|
46
|
+
text(body.targetTime, 32); text(body.versionCount, 16);
|
|
47
|
+
if (!/^(?:original|[1-9][0-9]{0,4}(?:s|m)?)$/u.test(body.targetTime) || !/^(?:[1-9]|10)(?:개)?$/u.test(body.versionCount)) reject();
|
|
48
|
+
}
|
|
49
|
+
export function captureDurableRemakeInput(value) {
|
|
50
|
+
const body = capture(value, [...inputFields, 'reviewInput'], DURABLE_REMAKE_LIMITS.maxInputBytes); identity(body);
|
|
51
|
+
if (body.operation === 'review-plan') {
|
|
52
|
+
if (Object.keys(body).some(key => !['operation','projectId','targetId','reviewInput'].includes(key)) || !body.reviewInput
|
|
53
|
+
|| Object.keys(body.reviewInput).some(key => !['version','workType','workTitle','episode','targetDurationSec','spoilerPolicy','deliveryStyle','reviewAngle','sourceCatalog','globalStory','fullTranscriptSegments'].includes(key))) reject();
|
|
54
|
+
normalizeReviewProductionInput(body.reviewInput);
|
|
55
|
+
return Object.freeze({ body, jobType: DURABLE_REMAKE_TYPES[body.operation] });
|
|
56
|
+
}
|
|
57
|
+
if (Object.hasOwn(body, 'reviewInput')) reject();
|
|
58
|
+
if (!body.analysis || !Array.isArray(body.analysis.videos) || !body.analysis.videos.length || body.analysis.videos.length > 5) reject();
|
|
59
|
+
return Object.freeze({ body, jobType: DURABLE_REMAKE_TYPES[body.operation] });
|
|
60
|
+
}
|
|
61
|
+
export function captureDurableRemakeOutput(value) {
|
|
62
|
+
try {
|
|
63
|
+
const out = capture(value, DURABLE_REMAKE_OUTPUT_FIELDS, DURABLE_REMAKE_LIMITS.maxOutputBytes); identity(out);
|
|
64
|
+
if (out.operation === 'review-plan') {
|
|
65
|
+
if (Object.keys(out).some(key => !['operation','projectId','targetId','reviewPlan'].includes(key)) || !out.reviewPlan) reject();
|
|
66
|
+
const plan = out.reviewPlan;
|
|
67
|
+
const normalized = validateReviewProductionPlan({ ...plan.reviewProfile, sourceCatalog: plan.sourceCatalog,
|
|
68
|
+
...(plan.globalStory === undefined ? {} : { globalStory: plan.globalStory, fullTranscriptSegments: plan.fullTranscriptSegments }) }, plan.version);
|
|
69
|
+
// Closed, canonical result: no provider-injected identity, file, URL or
|
|
70
|
+
// hidden result fields. Validation is shared with the original service.
|
|
71
|
+
const canonical = item => JSON.stringify(item, (_key, child) => child && !Array.isArray(child) && typeof child === 'object'
|
|
72
|
+
? Object.fromEntries(Object.keys(child).sort().map(key => [key, child[key]])) : child);
|
|
73
|
+
if (canonical(plan) !== canonical(normalized)) reject();
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
if (Object.hasOwn(out, 'reviewPlan')) reject();
|
|
77
|
+
if (!Array.isArray(out.sourceVideos) || !out.sourceVideos.length || out.sourceVideos.length > 5 || !Array.isArray(out.versions) || !out.versions.length || out.versions.length > 10
|
|
78
|
+
|| !Array.isArray(out.promptFrameworks) || out.promptFrameworks.length !== 2 || out.promptFrameworks[0] !== '티키타카 편집점' || out.promptFrameworks[1] !== '바이럴 마스터 PD') reject();
|
|
79
|
+
text(out.summaryBody, 64000, true); text(out.summaryHeadline, 16000, true);
|
|
80
|
+
const ids = new Set();
|
|
81
|
+
for (const version of out.versions) {
|
|
82
|
+
text(version.id, 240); if (ids.has(version.id) || Object.hasOwn(version, 'sourceVideos') || !Array.isArray(version.rows) || version.rows.length > 1000) reject();
|
|
83
|
+
ids.add(version.id);
|
|
84
|
+
for (const row of version.rows) if (!['N','S','A','SN'].includes(row.mode) || !Number.isFinite(row.durationSec) || row.durationSec <= 0 || !Array.isArray(row.clipRefs)) reject();
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
} catch { throw invalid(true); }
|
|
88
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { captureDurableRemakeInput, captureDurableRemakeOutput, DURABLE_REMAKE_CONTRACT_VERSION,
|
|
2
|
+
DURABLE_REMAKE_MODEL, DURABLE_REMAKE_LIMITS, DURABLE_REMAKE_TYPES, DURABLE_REMAKE_OUTPUT_FIELDS, DURABLE_REMAKE_PATHS, durableRemakeCapabilityModels } from './durable-remake-contract.mjs';
|
|
3
|
+
|
|
4
|
+
export const durableRemakePath = operation => DURABLE_REMAKE_PATHS[operation];
|
|
5
|
+
export const durableRemakeCapabilityPath = operation => `${durableRemakePath(operation)}/execution-capability`;
|
|
6
|
+
export const DURABLE_REMAKE_REQUEST_LIMITS = Object.freeze({ maxResponseBytes: DURABLE_REMAKE_LIMITS.maxOutputBytes + 262144, maxReadChunks: 65536 });
|
|
7
|
+
const JOB = /^epoch-v2-[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u;
|
|
8
|
+
const COST = /^bg-durable-remake-v1:[a-f0-9]{64}:(?:[1-9]|[1-5][0-9]|6[0-6])$/u;
|
|
9
|
+
const encoder = new TextEncoder(), failures = new WeakMap();
|
|
10
|
+
const messages = Object.freeze({
|
|
11
|
+
INVALID: '리뷰 기획 요청을 안전하게 확인하지 못했습니다.',
|
|
12
|
+
AUTHORITY_CHANGED: '계정·프로젝트·입력이 변경되어 이전 리뷰 기획 작업을 중단했습니다.',
|
|
13
|
+
SESSION_REQUIRED: '로그인 후 리뷰 기획를 생성해 주세요.',
|
|
14
|
+
UNAVAILABLE: '현재 서버에서는 안전한 리뷰 기획 생성을 사용할 수 없습니다.',
|
|
15
|
+
RESPONSE_INVALID: '리뷰 기획 응답을 안전하게 확인하지 못했습니다. 자동 재생성하지 않습니다.',
|
|
16
|
+
JOURNAL_UNAVAILABLE: '제출 기록을 확인하지 못했습니다. 자동 재생성하지 않습니다.',
|
|
17
|
+
JOURNAL_FULL: '안전한 제출 기록 한도(계정당 32건)에 도달했습니다. 기존 작업 조회는 가능하지만 새 생성은 차단됩니다. 기록을 삭제하지 말고 고객지원에 문의해 주세요.',
|
|
18
|
+
UNRESOLVED: '이전 제출 결과가 확정되지 않았습니다. 자동 재생성하지 않습니다.',
|
|
19
|
+
MISSING: '이전 작업을 찾을 수 없습니다. 새 생성 요청을 보내지 않았습니다.',
|
|
20
|
+
WAIT_ABORTED: '기다리기를 중단했습니다. 생성 취소가 확인된 것은 아닙니다.',
|
|
21
|
+
WAIT_TIMEOUT: '대기 시간이 초과되었습니다. 기존 작업만 다시 조회할 수 있습니다.',
|
|
22
|
+
DIFFERENT_EDITOR: '이 프로젝트에 다른 제작 화면의 미확정 작업이 있습니다. 원래 화면에서 먼저 기존 작업을 조회해 주세요.',
|
|
23
|
+
APPLY_UNCONFIRMED: '작업 결과는 완료됐지만 프로젝트 저장을 확인하지 못했습니다. 다음에 기존 작업만 다시 조회합니다.',
|
|
24
|
+
STORAGE_BUDGET: '리메이크 결과 저장 한도(결과당 20 MiB·계정 전체 48 MiB)에 도달했습니다. 기존 결과와 제출 기록은 보존하며 자동 재생성하지 않습니다. 고객지원에 문의해 주세요.',
|
|
25
|
+
ACTIVE: '리뷰 기획 작업이 이미 진행 중입니다.',
|
|
26
|
+
});
|
|
27
|
+
function failure(kind) {
|
|
28
|
+
const error = Object.assign(new Error(messages[kind] || messages.INVALID), {
|
|
29
|
+
code: `DURABLE_REMAKE_${kind}`, paidReplayAllowed: false,
|
|
30
|
+
}); failures.set(error, kind); return error;
|
|
31
|
+
}
|
|
32
|
+
const fail = kind => { throw failure(kind); };
|
|
33
|
+
const own = (value, key) => value && typeof value === 'object' ? Object.getOwnPropertyDescriptor(value, key)?.value : undefined;
|
|
34
|
+
// Legacy transport already projects usage; copy only its bounded public fields.
|
|
35
|
+
// Never copy an error message, request, headers, arbitrary object or credential.
|
|
36
|
+
function legacyUsage(value, depth = 0) {
|
|
37
|
+
if (!value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return undefined;
|
|
38
|
+
const output = {};
|
|
39
|
+
for (const key of ['aiCalls', 'costUsd', 'inputTokens', 'outputTokens', 'totalTokens', 'credits', 'creditsConsumed']) {
|
|
40
|
+
const number = own(value, key);
|
|
41
|
+
if (typeof number === 'number' && Number.isFinite(number) && number >= 0 && number <= Number.MAX_SAFE_INTEGER) output[key] = number;
|
|
42
|
+
}
|
|
43
|
+
for (const key of ['model', 'provider', 'stage']) {
|
|
44
|
+
const item = own(value, key); if (text(item, 160)) output[key] = item;
|
|
45
|
+
}
|
|
46
|
+
const stages = own(value, 'stages');
|
|
47
|
+
if (depth === 0 && Array.isArray(stages) && stages.length > 0 && stages.length <= 66) {
|
|
48
|
+
output.stages = Array.from({ length: stages.length }, (_, i) => legacyUsage(own(stages, String(i)), 1)).filter(Boolean);
|
|
49
|
+
if (!output.stages.length) delete output.stages;
|
|
50
|
+
}
|
|
51
|
+
return Object.keys(output).length ? Object.freeze(output) : undefined;
|
|
52
|
+
}
|
|
53
|
+
function object(value) {
|
|
54
|
+
if (!value || typeof value !== 'object' || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail('RESPONSE_INVALID');
|
|
55
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
56
|
+
if (Reflect.ownKeys(descriptors).some(key => typeof key !== 'string' || !('value' in descriptors[key]) || !descriptors[key].enumerable)) fail('RESPONSE_INVALID');
|
|
57
|
+
return Object.fromEntries(Object.entries(descriptors).map(([key, item]) => [key, item.value]));
|
|
58
|
+
}
|
|
59
|
+
function text(value, maximum = 255) {
|
|
60
|
+
return typeof value === 'string' && value.length > 0 && value.length <= maximum && value.trim() === value
|
|
61
|
+
&& value.isWellFormed() && !/[\u0000-\u001f\u007f]/u.test(value);
|
|
62
|
+
}
|
|
63
|
+
function origin(value) {
|
|
64
|
+
if (!text(value, 2048)) fail('INVALID');
|
|
65
|
+
let url; try { url = new URL(value); } catch { fail('INVALID'); }
|
|
66
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== '/'
|
|
67
|
+
|| ![url.origin, `${url.origin}/`].includes(value)) fail('INVALID');
|
|
68
|
+
return url.origin;
|
|
69
|
+
}
|
|
70
|
+
function authority(getAuthority) {
|
|
71
|
+
const value = getAuthority(), ownerUserId = own(value, 'ownerUserId'), sessionRevision = own(value, 'sessionRevision');
|
|
72
|
+
if (!text(ownerUserId) || ownerUserId.startsWith('deleted:')
|
|
73
|
+
|| !(text(sessionRevision, 512) || Number.isSafeInteger(sessionRevision) && sessionRevision >= 0)) fail('SESSION_REQUIRED');
|
|
74
|
+
return Object.freeze({ ownerUserId, sessionRevision });
|
|
75
|
+
}
|
|
76
|
+
function synchronous(callback) {
|
|
77
|
+
const result = callback();
|
|
78
|
+
if (result && typeof result.then === 'function') { Promise.resolve(result).catch(() => {}); fail('AUTHORITY_CHANGED'); }
|
|
79
|
+
if (result === false) fail('AUTHORITY_CHANGED');
|
|
80
|
+
}
|
|
81
|
+
function exactConstants(value, expected) {
|
|
82
|
+
const captured = object(value), keys = Object.keys(expected);
|
|
83
|
+
if (Object.keys(captured).length !== keys.length || keys.some(key => captured[key] !== expected[key])) fail('RESPONSE_INVALID');
|
|
84
|
+
}
|
|
85
|
+
export function validateDurableRemakeCapability(value, operation) {
|
|
86
|
+
const captured = object(value);
|
|
87
|
+
if (captured.version !== 1 || captured.contractVersion !== DURABLE_REMAKE_CONTRACT_VERSION || captured.operation !== operation
|
|
88
|
+
|| !['legacy-sync', 'queue-v2', 'unavailable'].includes(captured.mode)) fail('RESPONSE_INVALID');
|
|
89
|
+
exactConstants(captured.model, durableRemakeCapabilityModels(operation)); exactConstants(captured.limits, DURABLE_REMAKE_LIMITS);
|
|
90
|
+
const allowed = ['version', 'contractVersion', 'operation', 'mode', 'model', 'limits', ...(captured.mode === 'queue-v2'
|
|
91
|
+
? ['paidReplayAllowed', 'recovery'] : captured.mode === 'unavailable' ? ['reason'] : [])];
|
|
92
|
+
if (Object.keys(captured).some(key => !allowed.includes(key)) || captured.mode === 'queue-v2'
|
|
93
|
+
&& (captured.paidReplayAllowed !== false || captured.recovery !== 'read-only-known-job')
|
|
94
|
+
|| captured.reason !== undefined && !text(captured.reason, 128)) fail('RESPONSE_INVALID');
|
|
95
|
+
return captured.mode;
|
|
96
|
+
}
|
|
97
|
+
function captureUsage(value, provider, model, { total = false } = {}) {
|
|
98
|
+
const item = object(value);
|
|
99
|
+
if (item.aiCalls !== 1 || !Number.isSafeInteger(item.inputTokens) || item.inputTokens < 0
|
|
100
|
+
|| !Number.isSafeInteger(item.outputTokens) || item.outputTokens < 0
|
|
101
|
+
|| !Number.isSafeInteger(item.inputTokens + item.outputTokens)
|
|
102
|
+
|| typeof item.costUsd !== 'number' || !Number.isFinite(item.costUsd) || item.costUsd < 0
|
|
103
|
+
|| item.model !== undefined && item.model !== model
|
|
104
|
+
|| item.provider !== undefined && item.provider !== provider
|
|
105
|
+
|| (total || item.totalTokens !== undefined) && item.totalTokens !== item.inputTokens + item.outputTokens) fail('RESPONSE_INVALID');
|
|
106
|
+
return Object.freeze({ aiCalls: 1, inputTokens: item.inputTokens, outputTokens: item.outputTokens, costUsd: item.costUsd,
|
|
107
|
+
...(item.totalTokens === undefined ? {} : { totalTokens: item.totalTokens }),
|
|
108
|
+
...(item.model === undefined ? {} : { model: item.model }), ...(item.provider === undefined ? {} : { provider: item.provider }) });
|
|
109
|
+
}
|
|
110
|
+
export function validateCompletedDurableRemakeJob(value, id) {
|
|
111
|
+
const job = object(value);
|
|
112
|
+
if (!JOB.test(id) || job.id !== id || !Object.values(DURABLE_REMAKE_TYPES).includes(job.type) || job.status !== 'completed') fail('RESPONSE_INVALID');
|
|
113
|
+
const result = object(job.result), model = DURABLE_REMAKE_MODEL[result.operation];
|
|
114
|
+
if (DURABLE_REMAKE_TYPES[result.operation] !== job.type || result.kind !== 'durable-remake-v2'
|
|
115
|
+
|| result.model !== model
|
|
116
|
+
|| result.accountingState !== 'settled' || result.hasAccountCostEvents !== true
|
|
117
|
+
|| !Array.isArray(result.accountCostEvents) || result.accountCostEvents.length > DURABLE_REMAKE_LIMITS.maxStages) fail('RESPONSE_INVALID');
|
|
118
|
+
const count = result.accountCostEvents.length;
|
|
119
|
+
if (result.operation === 'review-plan' && count !== 1) fail('RESPONSE_INVALID');
|
|
120
|
+
if (count < 1 || count > 66 || !['evolink','kie'].includes(result.provider)) fail('RESPONSE_INVALID');
|
|
121
|
+
let output; try { output = captureDurableRemakeOutput(Object.fromEntries(DURABLE_REMAKE_OUTPUT_FIELDS
|
|
122
|
+
.filter(key => result[key] !== undefined).map(key => [key, result[key]]))); } catch { fail('RESPONSE_INVALID'); }
|
|
123
|
+
const ids = new Set();
|
|
124
|
+
const receipts = result.accountCostEvents.map(raw => {
|
|
125
|
+
const event = object(raw), provider = event.provider;
|
|
126
|
+
const committed = event.accountCostRecorded === true && event.accountCostSuppressedByReset !== true;
|
|
127
|
+
const suppressed = event.accountCostRecorded === false && event.accountCostSuppressedByReset === true;
|
|
128
|
+
if (!COST.test(event.accountCostEventId || '') || ids.has(event.accountCostEventId) || !(committed || suppressed)
|
|
129
|
+
|| event.accountCostSuppressedByReset !== undefined && typeof event.accountCostSuppressedByReset !== 'boolean'
|
|
130
|
+
|| event.model !== model || provider !== result.provider || !Number.isSafeInteger(event.createdAt)
|
|
131
|
+
|| event.createdAt <= 0 || event.createdAt > 253402300799999 || typeof event.costUsd !== 'number'
|
|
132
|
+
|| !Number.isFinite(event.costUsd) || event.costUsd < 0) fail('RESPONSE_INVALID');
|
|
133
|
+
ids.add(event.accountCostEventId);
|
|
134
|
+
const usage = captureUsage(event.usage, provider, model);
|
|
135
|
+
if (usage.costUsd !== event.costUsd) fail('RESPONSE_INVALID');
|
|
136
|
+
return Object.freeze({ accountCostEventId: event.accountCostEventId, createdAt: event.createdAt,
|
|
137
|
+
provider, model, costUsd: event.costUsd, accountCostRecorded: committed,
|
|
138
|
+
...(suppressed ? { accountCostSuppressedByReset: true } : {}), usage });
|
|
139
|
+
});
|
|
140
|
+
const total = object(result.usage), sum = key => receipts.reduce((n, item) => n + item.usage[key], 0);
|
|
141
|
+
if (total.aiCalls !== receipts.length || total.inputTokens !== sum('inputTokens') || total.outputTokens !== sum('outputTokens')
|
|
142
|
+
|| total.totalTokens !== total.inputTokens + total.outputTokens || !Number.isSafeInteger(total.totalTokens)
|
|
143
|
+
|| typeof total.costUsd !== 'number' || !Number.isFinite(total.costUsd) || total.costUsd < 0
|
|
144
|
+
|| Math.abs(total.costUsd - sum('costUsd')) > 0.000001) fail('RESPONSE_INVALID');
|
|
145
|
+
const usage = Object.freeze({ aiCalls: total.aiCalls, inputTokens: total.inputTokens, outputTokens: total.outputTokens,
|
|
146
|
+
totalTokens: total.totalTokens, costUsd: total.costUsd });
|
|
147
|
+
return Object.freeze({ ...output, ok: true, kind: 'durable-remake-v2', model, provider: result.provider, usage, hasAccountCostEvents: true,
|
|
148
|
+
accountingState: 'settled', accountCostEvents: Object.freeze(receipts), backgroundJobId: id });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Strict stream EOF, fatal UTF-8 and declared-byte checks. Abort starts reader
|
|
152
|
+
// cancellation immediately, but does not detach an admitted read/cancel promise.
|
|
153
|
+
async function readJson(response, { check, signal, url }) {
|
|
154
|
+
let reader, cancellation, complete = false;
|
|
155
|
+
const cancel = () => {
|
|
156
|
+
if (cancellation || complete) return;
|
|
157
|
+
try { cancellation = Promise.resolve(reader ? reader.cancel() : response?.body?.cancel()).catch(() => {}); }
|
|
158
|
+
catch { cancellation = Promise.resolve(); }
|
|
159
|
+
};
|
|
160
|
+
const abort = () => cancel();
|
|
161
|
+
try {
|
|
162
|
+
check();
|
|
163
|
+
if (!response || response.redirected || response.type === 'opaqueredirect' || response.type === 'opaque'
|
|
164
|
+
|| response.url && response.url !== url
|
|
165
|
+
|| !Number.isInteger(response.status) || typeof response.body?.getReader !== 'function') fail('RESPONSE_INVALID');
|
|
166
|
+
const mime = (response.headers.get('content-type') || '').split(';', 1)[0].trim().toLowerCase();
|
|
167
|
+
if (mime !== 'application/json') fail('RESPONSE_INVALID');
|
|
168
|
+
const length = response.headers.get('content-length'), encoding = response.headers.get('content-encoding');
|
|
169
|
+
if (length !== null && (!/^(0|[1-9][0-9]*)$/u.test(length) || !Number.isSafeInteger(Number(length))
|
|
170
|
+
|| Number(length) > DURABLE_REMAKE_REQUEST_LIMITS.maxResponseBytes)) fail('RESPONSE_INVALID');
|
|
171
|
+
reader = response.body.getReader(); signal.addEventListener('abort', abort, { once: true });
|
|
172
|
+
if (signal.aborted) { cancel(); check(); }
|
|
173
|
+
const decoder = new TextDecoder('utf-8', { fatal: true }), blocks = []; let bytes = 0, chunks = 0;
|
|
174
|
+
for (;;) {
|
|
175
|
+
check(); const part = await reader.read(); check();
|
|
176
|
+
if (part.done) break;
|
|
177
|
+
if (!(part.value instanceof Uint8Array) || ++chunks > DURABLE_REMAKE_REQUEST_LIMITS.maxReadChunks
|
|
178
|
+
|| (bytes += part.value.byteLength) > DURABLE_REMAKE_REQUEST_LIMITS.maxResponseBytes) fail('RESPONSE_INVALID');
|
|
179
|
+
blocks.push(decoder.decode(part.value, { stream: true }));
|
|
180
|
+
}
|
|
181
|
+
if (!bytes || length !== null && (!encoding || encoding === 'identity') && Number(length) !== bytes) fail('RESPONSE_INVALID');
|
|
182
|
+
blocks.push(decoder.decode()); const value = JSON.parse(blocks.join('')); check(); complete = true;
|
|
183
|
+
return { status: response.status, body: value };
|
|
184
|
+
} catch (error) { cancel(); throw failures.has(error) ? error : failure('RESPONSE_INVALID'); }
|
|
185
|
+
finally { signal.removeEventListener('abort', abort); await cancellation; reader?.releaseLock(); }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** A journal intent is consumed before the one paid POST. Restored intentions
|
|
189
|
+
* with an ID can only GET it; no ID, a missing job, failure or mode downgrade
|
|
190
|
+
* never permits another POST or the legacy callback. Browser storage loss is
|
|
191
|
+
* not server-side idempotency; this API offers no reset/new-intent escape. */
|
|
192
|
+
export function createDurableRemakeRequest({ journal, apiOrigin, fetchImpl = globalThis.fetch, getAuthority,
|
|
193
|
+
getHeaders, ensureSession, crypto = globalThis.crypto, pollMs = 1000, timeoutMs = 240000 } = {}) {
|
|
194
|
+
const base = origin(apiOrigin), digest = typeof crypto?.subtle?.digest === 'function' ? crypto.subtle.digest.bind(crypto.subtle) : null;
|
|
195
|
+
if ([fetchImpl, getAuthority, getHeaders, ensureSession, ...['lookup', 'claim', 'rememberJob', 'markResult'].map(key => journal?.[key])]
|
|
196
|
+
.some(fn => typeof fn !== 'function') || !digest || !Number.isSafeInteger(pollMs) || pollMs < 1 || pollMs > 10000
|
|
197
|
+
|| !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 900000) throw failure('INVALID');
|
|
198
|
+
const storage = Object.fromEntries(['lookup', 'claim', 'rememberJob', 'markResult'].map(key => [key, journal[key].bind(journal)]));
|
|
199
|
+
let active = false;
|
|
200
|
+
return Object.freeze({ async readCapability({ operation, assertCurrent, signal, assertQueueReady } = {}) {
|
|
201
|
+
if (!Object.hasOwn(DURABLE_REMAKE_TYPES, operation) || typeof assertCurrent !== 'function'
|
|
202
|
+
|| signal !== undefined && !(signal instanceof AbortSignal)) fail('INVALID');
|
|
203
|
+
const captured = authority(getAuthority), controller = new AbortController();
|
|
204
|
+
const abort = () => controller.abort(failure('WAIT_ABORTED'));
|
|
205
|
+
const timer = setTimeout(() => controller.abort(failure('WAIT_TIMEOUT')), Math.min(timeoutMs, 15000));
|
|
206
|
+
signal?.addEventListener('abort', abort, { once: true }); if (signal?.aborted) abort();
|
|
207
|
+
const check = () => { const current = authority(getAuthority); synchronous(assertCurrent);
|
|
208
|
+
if (current.ownerUserId !== captured.ownerUserId || current.sessionRevision !== captured.sessionRevision) fail('AUTHORITY_CHANGED');
|
|
209
|
+
if (controller.signal.aborted) throw controller.signal.reason;
|
|
210
|
+
};
|
|
211
|
+
try {
|
|
212
|
+
check(); if (await ensureSession() === false) fail('SESSION_REQUIRED'); check();
|
|
213
|
+
const headers = new Headers(await getHeaders()); headers.set('Accept', 'application/json'); check();
|
|
214
|
+
const url = `${base}${durableRemakeCapabilityPath(operation)}`;
|
|
215
|
+
const response = await fetchImpl(url, { method: 'GET', headers, credentials: 'include', redirect: 'error', cache: 'no-store', signal: controller.signal });
|
|
216
|
+
const reply = await readJson(response, { check, signal: controller.signal, url }); check();
|
|
217
|
+
if (reply.status !== 200) fail('UNAVAILABLE');
|
|
218
|
+
const mode = validateDurableRemakeCapability(reply.body, operation);
|
|
219
|
+
if (mode === 'unavailable') fail('UNAVAILABLE');
|
|
220
|
+
if (mode === 'queue-v2') {
|
|
221
|
+
if (typeof assertQueueReady !== 'function' || await assertQueueReady() !== true) fail('INVALID'); check();
|
|
222
|
+
}
|
|
223
|
+
return mode;
|
|
224
|
+
} catch (caught) { if (own(caught, 'code') === 'DURABLE_REMAKE_STORAGE_BUDGET_EXCEEDED') fail('STORAGE_BUDGET'); throw failures.has(caught) ? caught : failure('RESPONSE_INVALID'); }
|
|
225
|
+
finally { clearTimeout(timer); signal?.removeEventListener('abort', abort); }
|
|
226
|
+
}, async run(rawBody, { projectId, assertCurrent, signal, onState = () => {}, onAccountCost = () => {}, legacy,
|
|
227
|
+
recoveryOnly = false, operation, targetId, applyResult, assertQueueReady } = {}) {
|
|
228
|
+
if (active) throw failure('ACTIVE');
|
|
229
|
+
if (!Object.hasOwn(DURABLE_REMAKE_TYPES, operation) || !text(targetId, 240) || !text(projectId, 160) || [assertCurrent, onState, onAccountCost].some(fn => typeof fn !== 'function')
|
|
230
|
+
|| signal !== undefined && !(signal instanceof AbortSignal) || typeof recoveryOnly !== 'boolean') throw failure('INVALID');
|
|
231
|
+
let body;
|
|
232
|
+
// Legacy accepts its original inputs, which may exceed the new queue's
|
|
233
|
+
// bounded contract. Defer only this v2 validation error until transport
|
|
234
|
+
// selection; even invalid edited inputs must first recover pending work.
|
|
235
|
+
if (!recoveryOnly) { try { body = captureDurableRemakeInput(rawBody).body; if (body.operation !== operation || body.targetId !== targetId || body.projectId !== projectId) body = undefined; } catch { body = undefined; } }
|
|
236
|
+
const captured = authority(getAuthority); active = true;
|
|
237
|
+
const controller = new AbortController(), deadline = performance.now() + timeoutMs;
|
|
238
|
+
const abort = () => controller.abort(failure('WAIT_ABORTED'));
|
|
239
|
+
const timer = setTimeout(() => controller.abort(failure('WAIT_TIMEOUT')), timeoutMs);
|
|
240
|
+
signal?.addEventListener('abort', abort, { once: true }); if (signal?.aborted) abort();
|
|
241
|
+
let jobId = null, status = 'checking', handle, claimStarted = false, legacyStarted = false, recovering = false;
|
|
242
|
+
const check = () => {
|
|
243
|
+
let current; try { current = authority(getAuthority); synchronous(assertCurrent); } catch { fail('AUTHORITY_CHANGED'); }
|
|
244
|
+
if (current.ownerUserId !== captured.ownerUserId || current.sessionRevision !== captured.sessionRevision) fail('AUTHORITY_CHANGED');
|
|
245
|
+
if (!controller.signal.aborted && performance.now() >= deadline) controller.abort(failure('WAIT_TIMEOUT'));
|
|
246
|
+
if (controller.signal.aborted) throw controller.signal.reason;
|
|
247
|
+
};
|
|
248
|
+
const checked = async operation => { check(); const value = await operation(); check(); return value; };
|
|
249
|
+
const publish = next => { check(); status = next; try { Promise.resolve(onState(Object.freeze({ version: 1, status,
|
|
250
|
+
jobId, recovering, paidReplayAllowed: false }))).catch(() => {}); } catch { /* UI cannot authorize a replay. */ } };
|
|
251
|
+
const persist = async operation => { try { return await checked(operation); } catch (error) {
|
|
252
|
+
check(); throw failures.has(error) ? error : failure(own(error, 'code') === 'UPLOAD_METADATA_JOURNAL_FULL' ? 'JOURNAL_FULL' : 'JOURNAL_UNAVAILABLE');
|
|
253
|
+
} };
|
|
254
|
+
async function request(path, method = 'GET') {
|
|
255
|
+
if (recoveryOnly && method !== 'GET') fail('INVALID');
|
|
256
|
+
const rawHeaders = await checked(() => getHeaders()), headers = new Headers(rawHeaders);
|
|
257
|
+
headers.set('Accept', 'application/json'); if (method === 'POST') headers.set('Content-Type', 'application/json');
|
|
258
|
+
let response;
|
|
259
|
+
try {
|
|
260
|
+
check(); response = await fetchImpl(`${base}${path}`, { method, headers, credentials: 'include', redirect: 'error',
|
|
261
|
+
cache: 'no-store', signal: controller.signal, ...(method === 'POST' ? { body: JSON.stringify(body) } : {}) });
|
|
262
|
+
return await readJson(response, { check, signal: controller.signal, url: `${base}${path}` });
|
|
263
|
+
} catch (error) { check(); throw failures.has(error) ? error : failure('RESPONSE_INVALID'); }
|
|
264
|
+
}
|
|
265
|
+
const envelope = (value, identity) => {
|
|
266
|
+
const item = object(value), record = object(item.record);
|
|
267
|
+
if (typeof item.canSubmit !== 'boolean' || !item.handle || typeof item.handle !== 'object'
|
|
268
|
+
|| record.version !== 1 || ['ownerUserId', 'apiOrigin', 'projectId'].some(key => record[key] !== identity[key])
|
|
269
|
+
|| typeof record.inputDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(record.inputDigest)
|
|
270
|
+
|| item.canSubmit && record.inputDigest !== identity.inputDigest
|
|
271
|
+
|| typeof record.jobId !== 'string' || record.jobId && !JOB.test(record.jobId)
|
|
272
|
+
|| !['submitted', 'unknown', 'completed', 'failed'].includes(record.state)
|
|
273
|
+
|| item.canSubmit && (record.jobId || record.state !== 'submitted')) fail('JOURNAL_UNAVAILABLE');
|
|
274
|
+
return { handle: item.handle, canSubmit: item.canSubmit, jobId: record.jobId || null };
|
|
275
|
+
};
|
|
276
|
+
const pause = () => checked(() => new Promise(resolve => {
|
|
277
|
+
let waiting;
|
|
278
|
+
const done = () => { clearTimeout(waiting); controller.signal.removeEventListener('abort', done); resolve(); };
|
|
279
|
+
waiting = setTimeout(done, pollMs); controller.signal.addEventListener('abort', done, { once: true });
|
|
280
|
+
if (controller.signal.aborted) done();
|
|
281
|
+
}));
|
|
282
|
+
try {
|
|
283
|
+
check(); publish('checking');
|
|
284
|
+
// A read-only pending-project probe has no input/body to persist. Its
|
|
285
|
+
// sentinel digest is never claimed, posted or used for legacy fallback.
|
|
286
|
+
const bytes = recoveryOnly || !body ? new Uint8Array(32)
|
|
287
|
+
: new Uint8Array(await checked(() => digest('SHA-256', encoder.encode(JSON.stringify(body)))));
|
|
288
|
+
if (bytes.byteLength !== 32) fail('INVALID');
|
|
289
|
+
const scopeBytes = new Uint8Array(await checked(() => digest('SHA-256', encoder.encode(JSON.stringify([operation, projectId, targetId])))));
|
|
290
|
+
if (scopeBytes.byteLength !== 32) fail('INVALID');
|
|
291
|
+
const identity = Object.freeze({ ownerUserId: captured.ownerUserId, apiOrigin: base, projectId: `durable-remake:v1:${Array.from(scopeBytes, byte => byte.toString(16).padStart(2, '0')).join('')}`,
|
|
292
|
+
inputDigest: Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('') });
|
|
293
|
+
let found = await persist(() => storage.lookup(identity, { assertCurrent: check }));
|
|
294
|
+
if (!found && recoveryOnly) return null;
|
|
295
|
+
if (await checked(() => ensureSession()) === false) fail('SESSION_REQUIRED');
|
|
296
|
+
if (!found) {
|
|
297
|
+
const capability = await request(durableRemakeCapabilityPath(operation));
|
|
298
|
+
if (capability.status !== 200) fail('UNAVAILABLE');
|
|
299
|
+
const mode = validateDurableRemakeCapability(capability.body, operation);
|
|
300
|
+
// Re-observe after the capability await, including a deployment change.
|
|
301
|
+
found = await persist(() => storage.lookup(identity, { assertCurrent: check }));
|
|
302
|
+
if (!found && mode === 'legacy-sync') {
|
|
303
|
+
if (typeof legacy !== 'function') fail('UNAVAILABLE');
|
|
304
|
+
// This preserves the legacy callback; the journal does not provide
|
|
305
|
+
// atomic legacy/v2 exclusion during a mixed deployment mode switch.
|
|
306
|
+
publish('legacy-sync'); check(); legacyStarted = true;
|
|
307
|
+
return await checked(() => legacy(body || rawBody, { signal: controller.signal }));
|
|
308
|
+
}
|
|
309
|
+
if (!found && mode !== 'queue-v2') fail('UNAVAILABLE');
|
|
310
|
+
if (!found && !body) fail('INVALID');
|
|
311
|
+
if (!found) { if (typeof assertQueueReady !== 'function' || await checked(assertQueueReady) !== true) fail('INVALID'); claimStarted = true; found = await persist(() => storage.claim(identity, { assertCurrent: check })); }
|
|
312
|
+
}
|
|
313
|
+
const selected = envelope(found, identity); handle = selected.handle; jobId = selected.jobId;
|
|
314
|
+
recovering = !selected.canSubmit;
|
|
315
|
+
if (selected.canSubmit && !claimStarted) fail('JOURNAL_UNAVAILABLE');
|
|
316
|
+
if (selected.canSubmit) {
|
|
317
|
+
publish('submitting');
|
|
318
|
+
const reply = await request(`${durableRemakePath(operation)}/jobs`, 'POST'), value = object(reply.body);
|
|
319
|
+
const accepted = reply.status === 202 && value.ok === true && value.async === true;
|
|
320
|
+
const uncertain = reply.status === 503 && value.code === 'BACKGROUND_ENQUEUE_UNCONFIRMED';
|
|
321
|
+
const returnedId = accepted ? own(value.job, 'id') : uncertain ? value.jobId : undefined;
|
|
322
|
+
if (typeof returnedId !== 'string' || !JOB.test(returnedId)) fail('UNRESOLVED');
|
|
323
|
+
jobId = returnedId;
|
|
324
|
+
await persist(() => storage.rememberJob(handle, jobId));
|
|
325
|
+
}
|
|
326
|
+
if (!jobId) { publish('unknown'); fail('UNRESOLVED'); }
|
|
327
|
+
for (;;) {
|
|
328
|
+
publish('checking');
|
|
329
|
+
const reply = await request(`/api/tracker/review/plan/jobs/${jobId}`);
|
|
330
|
+
if (reply.status === 404) { publish('missing'); fail('MISSING'); }
|
|
331
|
+
const value = object(reply.body), job = object(value.job);
|
|
332
|
+
if (reply.status !== 200 || value.ok !== true || job.id !== jobId || !Object.values(DURABLE_REMAKE_TYPES).includes(job.type)
|
|
333
|
+
|| !['queued', 'running', 'completed', 'failed', 'cancelled', 'unknown'].includes(job.status)) fail('RESPONSE_INVALID');
|
|
334
|
+
if (job.type !== DURABLE_REMAKE_TYPES[operation]) fail('DIFFERENT_EDITOR');
|
|
335
|
+
if (job.status === 'completed') {
|
|
336
|
+
const result = validateCompletedDurableRemakeJob(job, jobId);
|
|
337
|
+
if (result.targetId !== targetId || result.projectId !== projectId || result.operation !== operation) fail('DIFFERENT_EDITOR');
|
|
338
|
+
// A provider-completed job is not a durably applied browser result.
|
|
339
|
+
// Keep its known ID pending through UI/storage failures or page exits.
|
|
340
|
+
if (typeof applyResult !== 'function') fail('APPLY_UNCONFIRMED');
|
|
341
|
+
try { if (await checked(() => applyResult(Object.freeze({ ...result, recoveredPreviousRequest: recovering }))) !== true) fail('APPLY_UNCONFIRMED'); }
|
|
342
|
+
catch (error) { check(); if (own(error, 'code') === 'DURABLE_REMAKE_STORAGE_BUDGET_EXCEEDED') fail('STORAGE_BUDGET'); if (failures.has(error)) throw error; fail('APPLY_UNCONFIRMED'); }
|
|
343
|
+
try { if (await checked(() => onAccountCost(result)) !== true) fail('APPLY_UNCONFIRMED'); }
|
|
344
|
+
catch (error) { check(); if (own(error, 'code') === 'DURABLE_REMAKE_STORAGE_BUDGET_EXCEEDED') fail('STORAGE_BUDGET'); if (failures.has(error)) throw error; fail('APPLY_UNCONFIRMED'); }
|
|
345
|
+
await persist(() => storage.markResult(handle, { state: 'completed' })); publish('completed');
|
|
346
|
+
check(); return Object.freeze({ ...result, recoveredPreviousRequest: recovering });
|
|
347
|
+
}
|
|
348
|
+
publish(job.status);
|
|
349
|
+
if (!['queued', 'running'].includes(job.status)) {
|
|
350
|
+
await persist(() => storage.markResult(handle, { state: job.status === 'unknown' ? 'unknown' : 'failed' }));
|
|
351
|
+
fail('UNRESOLVED');
|
|
352
|
+
}
|
|
353
|
+
await pause();
|
|
354
|
+
}
|
|
355
|
+
} catch (caught) {
|
|
356
|
+
controller.abort(caught);
|
|
357
|
+
// Do not persist after a stopped authority. A submitted record without an
|
|
358
|
+
// ID already restores UNKNOWN, even if the POST never left this browser.
|
|
359
|
+
let changed = false; try { const fresh = authority(getAuthority); synchronous(assertCurrent);
|
|
360
|
+
changed = fresh.ownerUserId !== captured.ownerUserId || fresh.sessionRevision !== captured.sessionRevision; } catch { changed = true; }
|
|
361
|
+
const error = failure(changed ? 'AUTHORITY_CHANGED' : failures.get(caught) || 'RESPONSE_INVALID');
|
|
362
|
+
if (legacyStarted && !changed) {
|
|
363
|
+
const usage = legacyUsage(own(caught, 'providerUsage'));
|
|
364
|
+
if (usage) {
|
|
365
|
+
error.providerUsage = usage;
|
|
366
|
+
const model = own(caught, 'providerModel'); if (text(model, 160)) error.providerModel = model;
|
|
367
|
+
error.partialProviderUsage = own(caught, 'partialProviderUsage') === true;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (jobId) error.backgroundJobId = jobId;
|
|
371
|
+
error.submissionState = jobId ? status : handle || claimStarted || legacyStarted ? 'unknown' : 'not-submitted';
|
|
372
|
+
throw error;
|
|
373
|
+
} finally { clearTimeout(timer); signal?.removeEventListener('abort', abort); active = false; }
|
|
374
|
+
} });
|
|
375
|
+
}
|
package/lib/durable-speech.mjs
CHANGED
|
@@ -25,7 +25,7 @@ export const durableSpeechToolSchema = Object.freeze({
|
|
|
25
25
|
text: z.string().min(1).max(10000).describe("전체 내레이션. 최대 5,000자, Supertone은 300자"),
|
|
26
26
|
options: optionsSchema.optional().describe("해당 공급자의 보이스·모델·언어·속도 옵션. 비용 이벤트/실행 상태는 전달하지 마세요."),
|
|
27
27
|
});
|
|
28
|
-
function
|
|
28
|
+
export function captureDurableSpeechInput(value) {
|
|
29
29
|
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw invalid();
|
|
30
30
|
const props = Object.getOwnPropertyDescriptors(value);
|
|
31
31
|
if (Reflect.ownKeys(props).some(key => !["requestId", "provider", "text", "options"].includes(key) || !Object.hasOwn(props[key], "value"))) throw invalid();
|
|
@@ -50,7 +50,7 @@ function capture(value) {
|
|
|
50
50
|
/** One JSON submit only. Neither errors nor a missing shared backend may invoke
|
|
51
51
|
* the legacy binary tool, switch provider, or create a replacement request ID. */
|
|
52
52
|
export async function submitDurableSpeech(api, value) {
|
|
53
|
-
const { requestId, input } =
|
|
53
|
+
const { requestId, input } = captureDurableSpeechInput(value), base = { generationJobId: requestId, kind: "tts", paidReplayAllowed: false, nextTool: "get_generation_job" };
|
|
54
54
|
try {
|
|
55
55
|
const response = await api.request(GENERATION_JOB_PATH, { method: "POST", timeoutMs: 30000, body: { id: requestId, kind: "tts", input, includePreview: false } });
|
|
56
56
|
if (response?.job?.id !== requestId || response.job.kind !== "tts" || !["pending", "running", "completed", "failed", "interrupted", "cancel_requested", "cancelled"].includes(response.job.status)) throw invalid();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Called only after the enclosing durable contract has made an immutable,
|
|
2
|
+
// bounded plain-JSON snapshot. Catalogs are observations, never credentials.
|
|
3
|
+
export const VOICE_INPUT_FIELDS = Object.freeze(['ttsProvider', 'voiceCatalog', 'topic', 'scriptExcerpt', 'sampleVoiceHints', 'sampleAudioBase64', 'productionProfile']);
|
|
4
|
+
const PROVIDERS = ['typecast', 'elevenlabs', 'supertone', 'edge', 'supertonic2'];
|
|
5
|
+
const PROFILE_FIELDS = ['version', 'categoryId', 'tone', 'deliveryStyle', 'targetDurationSec', 'imageSourceMode', 'visualMotionMode', 'autoComposeEnabled', 'subtitleStylePresetId', 'titleStylePresetId'];
|
|
6
|
+
const invalid = () => { throw new Error('DURABLE_VOICE_CONTRACT_INVALID'); };
|
|
7
|
+
function fields(value, allowed) {
|
|
8
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).some(k => !allowed.includes(k))) invalid();
|
|
9
|
+
}
|
|
10
|
+
function text(value, max, required = false) {
|
|
11
|
+
if (typeof value !== 'string' || value.length > max || required && !value.trim()) invalid();
|
|
12
|
+
}
|
|
13
|
+
export function validateVoiceRecommendationInput(body) {
|
|
14
|
+
if (!PROVIDERS.includes(body.ttsProvider)) invalid();
|
|
15
|
+
for (const [key, max] of [['topic', 500], ['scriptExcerpt', 2000], ['sampleVoiceHints', 500]]) if (body[key] !== undefined) text(body[key], max);
|
|
16
|
+
const audio = body.sampleAudioBase64;
|
|
17
|
+
if (audio !== undefined && (typeof audio !== 'string' || audio.length > 11_000_000 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(audio))) invalid();
|
|
18
|
+
if (![body.topic, body.scriptExcerpt, body.sampleVoiceHints, audio].some(v => v?.trim())) invalid();
|
|
19
|
+
const catalog = body.voiceCatalog;
|
|
20
|
+
if (!Array.isArray(catalog) || !catalog.length || catalog.length > 250) invalid();
|
|
21
|
+
const ids = new Set();
|
|
22
|
+
for (const voice of catalog) {
|
|
23
|
+
fields(voice, ['voiceId', 'name', 'nameKo', 'age', 'gender', 'language', 'tags']);
|
|
24
|
+
text(voice.voiceId, 160, true);
|
|
25
|
+
if (voice.voiceId.trim() !== voice.voiceId || /[\u0000-\u0020\u007f]/u.test(voice.voiceId) || ids.has(voice.voiceId)) invalid();
|
|
26
|
+
ids.add(voice.voiceId);
|
|
27
|
+
for (const [key, max] of [['name', 60], ['nameKo', 60], ['age', 24], ['gender', 16], ['language', 24]]) text(voice[key], max);
|
|
28
|
+
if (!voice.name.trim() && !voice.nameKo.trim() || !Array.isArray(voice.tags) || voice.tags.length > 4) invalid();
|
|
29
|
+
voice.tags.forEach(tag => text(tag, 40, true));
|
|
30
|
+
}
|
|
31
|
+
const profile = body.productionProfile;
|
|
32
|
+
if (profile != null) {
|
|
33
|
+
fields(profile, PROFILE_FIELDS);
|
|
34
|
+
if (profile.version !== 1 || !/^[a-z][a-z0-9-]{0,63}$/u.test(profile.categoryId) || !['casual-social', 'standard', 'news-briefing'].includes(profile.deliveryStyle)
|
|
35
|
+
|| !Number.isFinite(profile.targetDurationSec) || profile.targetDurationSec <= 0 || profile.targetDurationSec > (['community-shorts', 'viral-shorts'].includes(profile.categoryId) ? 180 : 3600)) invalid();
|
|
36
|
+
if (profile.tone !== undefined) text(profile.tone, 300);
|
|
37
|
+
if (profile.imageSourceMode !== undefined && !['web', 'ai', 'mixed'].includes(profile.imageSourceMode)
|
|
38
|
+
|| profile.visualMotionMode !== undefined && !['image-motion', 'i2v'].includes(profile.visualMotionMode)
|
|
39
|
+
|| profile.autoComposeEnabled !== undefined && typeof profile.autoComposeEnabled !== 'boolean') invalid();
|
|
40
|
+
for (const key of ['subtitleStylePresetId', 'titleStylePresetId']) if (profile[key] !== undefined && !/^[a-z][a-z0-9-]{0,63}$/u.test(profile[key])) invalid();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function validateVoiceRecommendationOutput(value) {
|
|
44
|
+
fields(value, ['ttsProvider', 'recommendations', 'sampleVoiceProfile', 'notes']);
|
|
45
|
+
if (!PROVIDERS.includes(value.ttsProvider) || !Array.isArray(value.recommendations) || !value.recommendations.length || value.recommendations.length > 3
|
|
46
|
+
|| !Array.isArray(value.notes) || value.notes.length > 5) invalid();
|
|
47
|
+
value.notes.forEach(note => text(note, 500, true));
|
|
48
|
+
const ids = new Set();
|
|
49
|
+
for (const rec of value.recommendations) {
|
|
50
|
+
fields(rec, ['voiceId', 'name', 'nameKo', 'age', 'gender', 'reason']);
|
|
51
|
+
for (const [key, max] of [['voiceId', 160], ['name', 60], ['nameKo', 60], ['age', 24], ['gender', 16], ['reason', 300]]) text(rec[key], max, key === 'voiceId');
|
|
52
|
+
if (ids.has(rec.voiceId)) invalid(); ids.add(rec.voiceId);
|
|
53
|
+
}
|
|
54
|
+
const p = value.sampleVoiceProfile;
|
|
55
|
+
if (p !== null) {
|
|
56
|
+
const strings = ['ageImpression', 'energy', 'gender', 'mood', 'pace', 'styleSummary', 'tone'];
|
|
57
|
+
fields(p, [...strings, 'syntheticLikely']);
|
|
58
|
+
if (Object.keys(p).length !== 8) invalid();
|
|
59
|
+
for (const key of strings) if (p[key] !== null) text(p[key], 80, true);
|
|
60
|
+
if (p.syntheticLikely !== null && typeof p.syntheticLikely !== 'boolean') invalid();
|
|
61
|
+
}
|
|
62
|
+
}
|